1# SPDX-License-Identifier: Apache-2.0
2
3include_guard(GLOBAL)
4
5include(user_cache)
6
7# Dependencies on CMake modules from the CMake distribution.
8include(CheckCCompilerFlag)
9include(CheckCXXCompilerFlag)
10
11########################################################
12# Table of contents
13########################################################
14# 1. Zephyr-aware extensions
15# 1.1. zephyr_*
16# 1.2. zephyr_library_*
17# 1.2.1 zephyr_interface_library_*
18# 1.3. generate_inc_*
19# 1.4. board_*
20# 1.5. Misc.
21# 2. Kconfig-aware extensions
22# 2.1 Misc
23# 3. CMake-generic extensions
24# 3.1. *_ifdef
25# 3.2. *_ifndef
26# 3.3. *_option compiler compatibility checks
27# 3.3.1 Toolchain integration
28# 3.4. Debugging CMake
29# 3.5. File system management
30# 4. Devicetree extensions
31# 4.1 dt_*
32# 4.2. *_if_dt_node
33# 4.3  zephyr_dt_*
34# 5. Zephyr linker functions
35# 5.1. zephyr_linker*
36# 6 Function helper macros
37# 7 Linkable loadable extensions (llext)
38# 7.1 llext_* configuration functions
39# 7.2 add_llext_* build control functions
40# 7.3 llext helper functions
41
42########################################################
43# 1. Zephyr-aware extensions
44########################################################
45# 1.1. zephyr_*
46#
47# The following methods are for modifying the CMake library[0] called
48# "zephyr". zephyr is a catch-all CMake library for source files that
49# can be built purely with the include paths, defines, and other
50# compiler flags that all zephyr source files use.
51# [0] https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html
52#
53# Example usage:
54# zephyr_sources(
55#   random_esp32.c
56#   utils.c
57# )
58#
59# Is short for:
60# target_sources(zephyr PRIVATE
61#   ${CMAKE_CURRENT_SOURCE_DIR}/random_esp32.c
62#   ${CMAKE_CURRENT_SOURCE_DIR}/utils.c
63# )
64#
65# As a very high-level introduction here are two call graphs that are
66# purposely minimalistic and incomplete.
67#
68#  zephyr_library_cc_option()
69#           |
70#           v
71#  zephyr_library_compile_options()  -->  target_compile_options()
72#
73#
74#  zephyr_cc_option()           --->  target_cc_option()
75#                                          |
76#                                          v
77#  zephyr_cc_option_fallback()  --->  target_cc_option_fallback()
78#                                          |
79#                                          v
80#  zephyr_compile_options()     --->  target_compile_options()
81#
82
83
84# https://cmake.org/cmake/help/latest/command/target_sources.html
85function(zephyr_sources)
86  foreach(arg ${ARGV})
87    if(IS_DIRECTORY ${arg})
88      message(FATAL_ERROR "zephyr_sources() was called on a directory")
89    endif()
90    target_sources(zephyr PRIVATE ${arg})
91  endforeach()
92endfunction()
93
94# https://cmake.org/cmake/help/latest/command/target_include_directories.html
95function(zephyr_include_directories)
96  target_include_directories(zephyr_interface INTERFACE ${ARGV})
97endfunction()
98
99# https://cmake.org/cmake/help/latest/command/target_include_directories.html
100function(zephyr_system_include_directories)
101  target_include_directories(zephyr_interface SYSTEM INTERFACE ${ARGV})
102endfunction()
103
104# https://cmake.org/cmake/help/latest/command/target_compile_definitions.html
105function(zephyr_compile_definitions)
106  target_compile_definitions(zephyr_interface INTERFACE ${ARGV})
107endfunction()
108
109# https://cmake.org/cmake/help/latest/command/target_compile_options.html
110function(zephyr_compile_options)
111  target_compile_options(zephyr_interface INTERFACE ${ARGV})
112endfunction()
113
114# https://cmake.org/cmake/help/latest/command/target_link_libraries.html
115function(zephyr_link_libraries)
116  target_link_libraries(zephyr_interface INTERFACE ${ARGV})
117endfunction()
118
119function(zephyr_libc_link_libraries)
120  set_property(TARGET zephyr_interface APPEND PROPERTY LIBC_LINK_LIBRARIES ${ARGV})
121endfunction()
122
123# See this file section 3.1. target_cc_option
124function(zephyr_cc_option)
125  foreach(arg ${ARGV})
126    target_cc_option(zephyr_interface INTERFACE ${arg})
127  endforeach()
128endfunction()
129
130function(zephyr_cc_option_fallback option1 option2)
131    target_cc_option_fallback(zephyr_interface INTERFACE ${option1} ${option2})
132endfunction()
133
134function(zephyr_ld_options)
135    target_ld_options(zephyr_interface INTERFACE ${ARGV})
136endfunction()
137
138# Getter functions for extracting build information from
139# zephyr_interface. Returning lists, and strings is supported, as is
140# requesting specific categories of build information (defines,
141# includes, options).
142#
143# The naming convention follows:
144# zephyr_get_${build_information}_for_lang${format}(lang x [STRIP_PREFIX])
145# Where
146#  the argument 'x' is written with the result
147# and
148#  ${build_information} can be one of
149#   - include_directories           # -I directories
150#   - system_include_directories    # -isystem directories
151#   - compile_definitions           # -D'efines
152#   - compile_options               # misc. compiler flags
153# and
154#  ${format} can be
155#   - the empty string '', signifying that it should be returned as a list
156#   - _as_string signifying that it should be returned as a string
157# and
158#  ${lang} can be one of
159#   - C
160#   - CXX
161#   - ASM
162#
163# STRIP_PREFIX
164#
165# By default the result will be returned ready to be passed directly
166# to a compiler, e.g. prefixed with -D, or -I, but it is possible to
167# omit this prefix by specifying 'STRIP_PREFIX' . This option has no
168# effect for 'compile_options'.
169#
170# e.g.
171# zephyr_get_include_directories_for_lang(ASM x)
172# writes "-Isome_dir;-Isome/other/dir" to x
173
174function(zephyr_get_include_directories_for_lang_as_string lang i)
175  zephyr_get_include_directories_for_lang(${lang} list_of_flags DELIMITER " " ${ARGN})
176
177  convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags)
178
179  set(${i} ${str_of_flags} PARENT_SCOPE)
180endfunction()
181
182function(zephyr_get_system_include_directories_for_lang_as_string lang i)
183  zephyr_get_system_include_directories_for_lang(${lang} list_of_flags DELIMITER " " ${ARGN})
184
185  convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags)
186
187  set(${i} ${str_of_flags} PARENT_SCOPE)
188endfunction()
189
190function(zephyr_get_compile_definitions_for_lang_as_string lang i)
191  zephyr_get_compile_definitions_for_lang(${lang} list_of_flags DELIMITER " " ${ARGN})
192
193  convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags)
194
195  set(${i} ${str_of_flags} PARENT_SCOPE)
196endfunction()
197
198function(zephyr_get_compile_options_for_lang_as_string lang i)
199  zephyr_get_compile_options_for_lang(${lang} list_of_flags DELIMITER " ")
200
201  convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags)
202
203  set(${i} ${str_of_flags} PARENT_SCOPE)
204endfunction()
205
206function(zephyr_get_include_directories_for_lang lang i)
207  zephyr_get_parse_args(args ${ARGN})
208  get_property(flags TARGET zephyr_interface PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
209
210  process_flags(${lang} flags output_list)
211  string(REPLACE ";" "$<SEMICOLON>" genexp_output_list "${output_list}")
212
213  if(NOT ARGN)
214    set(result_output_list "-I$<JOIN:${genexp_output_list},$<SEMICOLON>-I>")
215  elseif(args_STRIP_PREFIX)
216    # The list has no prefix, so don't add it.
217    set(result_output_list ${output_list})
218  elseif(args_DELIMITER)
219    set(result_output_list "-I$<JOIN:${genexp_output_list},${args_DELIMITER}-I>")
220  endif()
221  set(${i} ${result_output_list} PARENT_SCOPE)
222endfunction()
223
224function(zephyr_get_system_include_directories_for_lang lang i)
225  zephyr_get_parse_args(args ${ARGN})
226  get_property(flags TARGET zephyr_interface PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES)
227
228  process_flags(${lang} flags output_list)
229  string(REPLACE ";" "$<SEMICOLON>" genexp_output_list "${output_list}")
230
231  set_ifndef(args_DELIMITER "$<SEMICOLON>")
232  set(result_output_list "$<$<BOOL:${genexp_output_list}>:-isystem$<JOIN:${genexp_output_list},${args_DELIMITER}-isystem>>")
233
234  set(${i} ${result_output_list} PARENT_SCOPE)
235endfunction()
236
237function(zephyr_get_compile_definitions_for_lang lang i)
238  zephyr_get_parse_args(args ${ARGN})
239  get_property(flags TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_DEFINITIONS)
240
241  process_flags(${lang} flags output_list)
242  string(REPLACE ";" "$<SEMICOLON>" genexp_output_list "${output_list}")
243
244  set_ifndef(args_DELIMITER "$<SEMICOLON>")
245  set(result_output_list "-D$<JOIN:${genexp_output_list},${args_DELIMITER}-D>")
246
247  set(${i} ${result_output_list} PARENT_SCOPE)
248endfunction()
249
250function(zephyr_get_compile_options_for_lang lang i)
251  zephyr_get_parse_args(args ${ARGN})
252  get_property(flags TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_OPTIONS)
253
254  process_flags(${lang} flags output_list)
255  string(REPLACE ";" "$<SEMICOLON>" genexp_output_list "${output_list}")
256
257  set_ifndef(args_DELIMITER "$<SEMICOLON>")
258  set(result_output_list "$<JOIN:${genexp_output_list},${args_DELIMITER}>")
259
260  set(${i} ${result_output_list} PARENT_SCOPE)
261endfunction()
262
263# This function writes a dict to it's output parameter
264# 'return_dict'. The dict has information about the parsed arguments,
265#
266# Usage:
267#   zephyr_get_parse_args(foo ${ARGN})
268#   print(foo_STRIP_PREFIX) # foo_STRIP_PREFIX might be set to 1
269function(zephyr_get_parse_args return_dict)
270  foreach(x ${ARGN})
271    if(DEFINED single_argument)
272      set(${single_argument} ${x} PARENT_SCOPE)
273      unset(single_argument)
274    else()
275      if(x STREQUAL STRIP_PREFIX)
276        set(${return_dict}_STRIP_PREFIX 1 PARENT_SCOPE)
277      elseif(x STREQUAL NO_SPLIT)
278        set(${return_dict}_NO_SPLIT 1 PARENT_SCOPE)
279      elseif(x STREQUAL DELIMITER)
280        set(single_argument ${return_dict}_DELIMITER)
281      endif()
282    endif()
283  endforeach()
284endfunction()
285
286function(process_flags lang input output)
287  # The flags might contains compile language generator expressions that
288  # look like this:
289  # $<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
290  # $<$<COMPILE_LANGUAGE:CXX>:$<OTHER_EXPRESSION>>
291  #
292  # Flags that don't specify a language like this apply to all
293  # languages.
294  #
295  # See COMPILE_LANGUAGE in
296  # https://cmake.org/cmake/help/v3.3/manual/cmake-generator-expressions.7.html
297  #
298  # To deal with this, we apply a regex to extract the flag and also
299  # to find out if the language matches.
300  #
301  # If this doesn't work out we might need to ban the use of
302  # COMPILE_LANGUAGE and instead partition C, CXX, and ASM into
303  # different libraries
304  set(languages C CXX ASM)
305
306  set(tmp_list "")
307
308  foreach(flag ${${input}})
309    set(is_compile_lang_generator_expression 0)
310    foreach(l ${languages})
311      if(flag MATCHES "<COMPILE_LANGUAGE:${l}>:([^>]+)>")
312        set(updated_flag ${CMAKE_MATCH_1})
313        set(is_compile_lang_generator_expression 1)
314        if(${l} STREQUAL ${lang})
315          # This test will match in case there are more generator expressions in the flag.
316          # As example: $<$<COMPILE_LANGUAGE:C>:$<OTHER_EXPRESSION>>
317          #             $<$<OTHER_EXPRESSION:$<COMPILE_LANGUAGE:C>:something>>
318          string(REGEX MATCH "(\\\$<)[^\\\$]*(\\\$<)[^\\\$]*(\\\$<)" IGNORE_RESULT ${flag})
319          if(CMAKE_MATCH_2)
320            # Nested generator expressions are used, just substitute `$<COMPILE_LANGUAGE:${l}>` to `1`
321            string(REGEX REPLACE "\\\$<COMPILE_LANGUAGE:${l}>" "1" updated_flag ${flag})
322          endif()
323          list(APPEND tmp_list ${updated_flag})
324          break()
325        endif()
326      endif()
327    endforeach()
328
329    if(NOT is_compile_lang_generator_expression)
330      # SHELL is used to avoid de-duplication, but when process flags
331      # then this tag must be removed to return real compile/linker flags.
332      if(flag MATCHES "SHELL:[ ]*(.*)")
333        separate_arguments(flag UNIX_COMMAND ${CMAKE_MATCH_1})
334      endif()
335      # Flags may be placed inside generator expression, therefore any flag
336      # which is not already a generator expression must have commas converted.
337      if(NOT flag MATCHES "\\\$<.*>")
338        string(REPLACE "," "$<COMMA>" flag "${flag}")
339      endif()
340      list(APPEND tmp_list ${flag})
341    endif()
342  endforeach()
343
344  set(${output} ${tmp_list} PARENT_SCOPE)
345endfunction()
346
347function(convert_list_of_flags_to_string_of_flags ptr_list_of_flags string_of_flags)
348  # Convert the list to a string so we can do string replace
349  # operations on it and replace the ";" list separators with a
350  # whitespace so the flags are spaced out
351  string(REPLACE ";"  " "  locally_scoped_string_of_flags "${${ptr_list_of_flags}}")
352
353  # Set the output variable in the parent scope
354  set(${string_of_flags} ${locally_scoped_string_of_flags} PARENT_SCOPE)
355endfunction()
356
357macro(get_property_and_add_prefix result target property prefix)
358  zephyr_get_parse_args(args ${ARGN})
359
360  if(args_STRIP_PREFIX)
361    set(maybe_prefix "")
362  else()
363    set(maybe_prefix ${prefix})
364  endif()
365
366  get_property(target_property TARGET ${target} PROPERTY ${property})
367  foreach(x ${target_property})
368    list(APPEND ${result} ${maybe_prefix}${x})
369  endforeach()
370endmacro()
371
372# 1.2 zephyr_library_*
373#
374# Zephyr libraries use CMake's library concept and a set of
375# assumptions about how zephyr code is organized to cut down on
376# boilerplate code.
377#
378# A Zephyr library can be constructed by the function zephyr_library
379# or zephyr_library_named. The constructors create a CMake library
380# with a name accessible through the variable ZEPHYR_CURRENT_LIBRARY.
381#
382# The variable ZEPHYR_CURRENT_LIBRARY should seldom be needed since
383# the zephyr libraries have methods that modify the libraries. These
384# methods have the signature: zephyr_library_<target-function>
385#
386# The methods are wrappers around the CMake target_* functions. See
387# https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html for
388# documentation on the underlying target_* functions.
389#
390# The methods modify the CMake target_* API to reduce boilerplate;
391#  PRIVATE is assumed
392#  The target is assumed to be ZEPHYR_CURRENT_LIBRARY
393#
394# When a flag that is given through the zephyr_* API conflicts with
395# the zephyr_library_* API then precedence will be given to the
396# zephyr_library_* API. In other words, local configuration overrides
397# global configuration.
398
399# Constructor with a directory-inferred name
400macro(zephyr_library)
401  zephyr_library_get_current_dir_lib_name(${ZEPHYR_BASE} lib_name)
402  zephyr_library_named(${lib_name})
403endmacro()
404
405# Determines what the current directory's lib name would be according to the
406# provided base and writes it to the argument "lib_name"
407macro(zephyr_library_get_current_dir_lib_name base lib_name)
408  # Remove the prefix (/home/sebo/zephyr/driver/serial/CMakeLists.txt => driver/serial/CMakeLists.txt)
409  file(RELATIVE_PATH name ${base} ${CMAKE_CURRENT_LIST_FILE})
410
411  # Remove the filename (driver/serial/CMakeLists.txt => driver/serial)
412  get_filename_component(name ${name} DIRECTORY)
413
414  # Replace / with __ (driver/serial => driver__serial)
415  string(REGEX REPLACE "/" "__" name ${name})
416
417  # Replace : with __ (C:/zephyrproject => C____zephyrproject)
418  string(REGEX REPLACE ":" "__" name ${name})
419
420  set(${lib_name} ${name})
421endmacro()
422
423# Constructor with an explicitly given name.
424macro(zephyr_library_named name)
425  # This is a macro because we need add_library() to be executed
426  # within the scope of the caller.
427  set(ZEPHYR_CURRENT_LIBRARY ${name})
428  add_library(${name} STATIC "")
429
430  zephyr_append_cmake_library(${name})
431
432  target_link_libraries(${name} PUBLIC zephyr_interface)
433endmacro()
434
435# Provides amend functionality to a Zephyr library for out-of-tree usage.
436#
437# When called from a Zephyr module, the corresponding zephyr library defined
438# within Zephyr will be looked up.
439#
440# Note, in order to ensure correct library when amending, the folder structure in the
441# Zephyr module must resemble the structure used in Zephyr, as example:
442#
443# Example: to amend the zephyr library created in
444# ZEPHYR_BASE/drivers/entropy/CMakeLists.txt
445# add the following file:
446# ZEPHYR_MODULE/drivers/entropy/CMakeLists.txt
447# with content:
448# zephyr_library_amend()
449# zephyr_library_sources(...)
450#
451# It is also possible to use generator expression when amending to Zephyr
452# libraries.
453#
454# For example, in case it is required to expose the Zephyr library's folder as
455# include path then the following is possible:
456# zephyr_library_amend()
457# zephyr_library_include_directories($<TARGET_PROPERTY:SOURCE_DIR>)
458#
459# See the CMake documentation for more target properties or generator
460# expressions.
461#
462macro(zephyr_library_amend)
463  # This is a macro because we need to ensure the ZEPHYR_CURRENT_LIBRARY and
464  # following zephyr_library_* calls are executed within the scope of the
465  # caller.
466  if(NOT ZEPHYR_CURRENT_MODULE_DIR)
467    message(FATAL_ERROR "Function only available for Zephyr modules.")
468  endif()
469
470  zephyr_library_get_current_dir_lib_name(${ZEPHYR_CURRENT_MODULE_DIR} lib_name)
471
472  set(ZEPHYR_CURRENT_LIBRARY ${lib_name})
473endmacro()
474
475function(zephyr_link_interface interface)
476  target_link_libraries(${interface} INTERFACE zephyr_interface)
477endfunction()
478
479#
480# zephyr_library versions of normal CMake target_<func> functions
481# Note, paths passed to this function must be relative in order
482# to support the library relocation feature of zephyr_code_relocate
483#
484function(zephyr_library_sources source)
485  target_sources(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${source} ${ARGN})
486endfunction()
487
488function(zephyr_library_include_directories)
489  target_include_directories(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${ARGN})
490endfunction()
491
492function(zephyr_library_link_libraries item)
493  target_link_libraries(${ZEPHYR_CURRENT_LIBRARY} PUBLIC ${item} ${ARGN})
494endfunction()
495
496function(zephyr_library_compile_definitions item)
497  target_compile_definitions(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${item} ${ARGN})
498endfunction()
499
500function(zephyr_library_compile_options item)
501  # The compiler is relied upon for sane behaviour when flags are in
502  # conflict. Compilers generally give precedence to flags given late
503  # on the command line. So to ensure that zephyr_library_* flags are
504  # placed late on the command line we create a dummy interface
505  # library and link with it to obtain the flags.
506  #
507  # Linking with a dummy interface library will place flags later on
508  # the command line than the flags from zephyr_interface because
509  # zephyr_interface will be the first interface library that flags
510  # are taken from.
511
512  string(MD5 uniqueness "${ARGV}")
513  set(lib_name options_interface_lib_${uniqueness})
514
515  if (NOT TARGET ${lib_name})
516    # Create the unique target only if it doesn't exist.
517    add_library(           ${lib_name} INTERFACE)
518    target_compile_options(${lib_name} INTERFACE ${item} ${ARGN})
519  endif()
520
521  target_link_libraries(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${lib_name})
522endfunction()
523
524function(zephyr_library_cc_option)
525  foreach(option ${ARGV})
526    string(MAKE_C_IDENTIFIER check${option} check)
527    zephyr_check_compiler_flag(C ${option} ${check})
528
529    if(${${check}})
530      zephyr_library_compile_options(${option})
531    endif()
532  endforeach()
533endfunction()
534
535function(zephyr_library_add_dependencies)
536  add_dependencies(${ZEPHYR_CURRENT_LIBRARY} ${ARGN})
537endfunction()
538
539# Add the existing CMake library 'library' to the global list of
540# Zephyr CMake libraries. This is done automatically by the
541# constructor but must be called explicitly on CMake libraries that do
542# not use a zephyr library constructor.
543function(zephyr_append_cmake_library library)
544  if(TARGET zephyr_prebuilt)
545    message(WARNING
546      "zephyr_library() or zephyr_library_named() called in Zephyr CMake "
547      "application mode. `${library}` will not be treated as a Zephyr library."
548      "To create a Zephyr library in Zephyr CMake kernel mode consider "
549      "creating a Zephyr module. See more here: "
550      "https://docs.zephyrproject.org/latest/guides/modules.html"
551    )
552  endif()
553  set_property(GLOBAL APPEND PROPERTY ZEPHYR_LIBS ${library})
554endfunction()
555
556# Add the imported library 'library_name', located at 'library_path' to the
557# global list of Zephyr CMake libraries.
558function(zephyr_library_import library_name library_path)
559  add_library(${library_name} STATIC IMPORTED GLOBAL)
560  set_target_properties(${library_name}
561    PROPERTIES IMPORTED_LOCATION
562    ${library_path}
563    )
564  zephyr_append_cmake_library(${library_name})
565endfunction()
566
567# Place the current zephyr library in the application memory partition.
568#
569# The partition argument is the name of the partition where the library shall
570# be placed.
571#
572# Note: Ensure the given partition has been defined using
573#       K_APPMEM_PARTITION_DEFINE in source code.
574function(zephyr_library_app_memory partition)
575  set_property(TARGET zephyr_property_target
576               APPEND PROPERTY COMPILE_OPTIONS
577               "-l" $<TARGET_FILE_NAME:${ZEPHYR_CURRENT_LIBRARY}> "${partition}")
578endfunction()
579
580# Configure a Zephyr library specific property.
581#
582# Usage:
583#   zephyr_library_property(<property> <value>)
584#
585# Current Zephyr library specific properties that are supported:
586# ALLOW_EMPTY <TRUE:FALSE>: Allow a Zephyr library to be empty.
587#                           An empty Zephyr library will generate a CMake
588#                           configure time warning unless `ALLOW_EMPTY` is TRUE.
589function(zephyr_library_property)
590  set(single_args "ALLOW_EMPTY")
591  cmake_parse_arguments(LIB_PROP "" "${single_args}" "" ${ARGN})
592
593  if(LIB_PROP_UNPARSED_ARGUMENTS)
594      message(FATAL_ERROR "zephyr_library_property(${ARGV0} ...) given unknown arguments: ${FILE_UNPARSED_ARGUMENTS}")
595  endif()
596
597  foreach(arg ${single_args})
598    if(DEFINED LIB_PROP_${arg})
599      set_property(TARGET ${ZEPHYR_CURRENT_LIBRARY} PROPERTY ${arg} ${LIB_PROP_${arg}})
600    endif()
601  endforeach()
602endfunction()
603
604# 1.2.1 zephyr_interface_library_*
605#
606# A Zephyr interface library is a thin wrapper over a CMake INTERFACE
607# library. The most important responsibility of this abstraction is to
608# ensure that when a user KConfig-enables a library then the header
609# files of this library will be accessible to the 'app' library.
610#
611# This is done because when a user uses Kconfig to enable a library he
612# expects to be able to include its header files and call its
613# functions out-of-the box.
614#
615# A Zephyr interface library should be used when there exists some
616# build information (include directories, defines, compiler flags,
617# etc.) that should be applied to a set of Zephyr libraries and 'app'
618# might be one of these libraries.
619#
620# Zephyr libraries must explicitly call
621# zephyr_library_link_libraries(<interface_library>) to use this build
622# information. 'app' is treated as a special case for usability
623# reasons; a Kconfig option (CONFIG_APP_LINK_WITH_<interface_library>)
624# should exist for each interface_library and will determine if 'app'
625# links with the interface_library.
626#
627# This API has a constructor like the zephyr_library API has, but it
628# does not have wrappers over the other cmake target functions.
629macro(zephyr_interface_library_named name)
630  add_library(${name} INTERFACE)
631  set_property(GLOBAL APPEND PROPERTY ZEPHYR_INTERFACE_LIBS ${name})
632endmacro()
633
634# 1.3 generate_inc_*
635
636# These functions are useful if there is a need to generate a file
637# that can be included into the application at build time. The file
638# can also be compressed automatically when embedding it.
639#
640# See tests/application_development/gen_inc_file for an example of
641# usage.
642function(generate_inc_file
643    source_file    # The source file to be converted to hex
644    generated_file # The generated file
645    )
646  add_custom_command(
647    OUTPUT ${generated_file}
648    COMMAND
649    ${PYTHON_EXECUTABLE}
650    ${ZEPHYR_BASE}/scripts/build/file2hex.py
651    ${ARGN} # Extra arguments are passed to file2hex.py
652    --file ${source_file}
653    > ${generated_file} # Does pipe redirection work on Windows?
654    DEPENDS ${source_file}
655    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
656    )
657endfunction()
658
659function(generate_inc_file_for_gen_target
660    target          # The cmake target that depends on the generated file
661    source_file     # The source file to be converted to hex
662    generated_file  # The generated file
663    gen_target      # The generated file target we depend on
664                    # Any additional arguments are passed on to file2hex.py
665    )
666  generate_inc_file(${source_file} ${generated_file} ${ARGN})
667
668  # Ensure 'generated_file' is generated before 'target' by creating a
669  # dependency between the two targets
670
671  add_dependencies(${target} ${gen_target})
672endfunction()
673
674function(generate_inc_file_for_target
675    target          # The cmake target that depends on the generated file
676    source_file     # The source file to be converted to hex
677    generated_file  # The generated file
678                    # Any additional arguments are passed on to file2hex.py
679    )
680  # Ensure 'generated_file' is generated before 'target' by creating a
681  # 'custom_target' for it and setting up a dependency between the two
682  # targets
683
684  # But first create a unique name for the custom target
685  generate_unique_target_name_from_filename(${generated_file} generated_target_name)
686
687  add_custom_target(${generated_target_name} DEPENDS ${generated_file})
688  generate_inc_file_for_gen_target(${target} ${source_file} ${generated_file} ${generated_target_name} ${ARGN})
689endfunction()
690
691# 1.4. board_*
692#
693# This section is for extensions related to Zephyr board handling.
694#
695# Zephyr board extensions current contains:
696# - Board runners
697# - Board revision
698
699# Zephyr board runners:
700#   Zephyr board runner extension functions control Zephyr's board runners
701#   from the build system. The Zephyr build system has targets for
702#   flashing and debugging supported boards. These are wrappers around a
703#   "runner" Python subpackage that is part of Zephyr's "west" tool.
704#
705# This section provides glue between CMake and the Python code that
706# manages the runners.
707
708set(TYPES "FLASH" "DEBUG" "SIM" "ROBOT")
709function(_board_check_runner_type type) # private helper
710  if (NOT "${type}" IN_LIST TYPES)
711    message(FATAL_ERROR "invalid type ${type}; should be one of: ${TYPES}")
712  endif()
713endfunction()
714
715# This function sets the runner for the board unconditionally.  It's
716# meant to be used from application CMakeLists.txt files.
717#
718# NOTE: Usually board_set_xxx_ifnset() is best in board.cmake files.
719#       This lets the user set the runner at cmake time, or in their
720#       own application's CMakeLists.txt.
721#
722# Usage:
723#   board_set_runner(FLASH pyocd)
724#
725# This would set the board's flash runner to "pyocd".
726#
727# In general, "type" is FLASH, DEBUG, SIM or ROBOT and "runner" is
728# the name of a runner.
729function(board_set_runner type runner)
730  _board_check_runner_type(${type})
731  if (DEFINED BOARD_${type}_RUNNER)
732    message(STATUS "overriding ${type} runner ${BOARD_${type}_RUNNER}; it's now ${runner}")
733  endif()
734  set(BOARD_${type}_RUNNER ${runner} PARENT_SCOPE)
735endfunction()
736
737# This macro is like board_set_runner(), but will only make a change
738# if that runner is currently not set.
739#
740# See also board_set_flasher_ifnset() and board_set_debugger_ifnset().
741macro(board_set_runner_ifnset type runner)
742  _board_check_runner_type(${type})
743  # This is a macro because set_ifndef() works at parent scope.
744  # If this were a function, that would be this function's scope,
745  # which wouldn't work.
746  set_ifndef(BOARD_${type}_RUNNER ${runner})
747endmacro()
748
749# A convenience macro for board_set_runner(FLASH ${runner}).
750macro(board_set_flasher runner)
751  board_set_runner(FLASH ${runner})
752endmacro()
753
754# A convenience macro for board_set_runner(DEBUG ${runner}).
755macro(board_set_debugger runner)
756  board_set_runner(DEBUG ${runner})
757endmacro()
758
759# A convenience macro for board_set_runner_ifnset(FLASH ${runner}).
760macro(board_set_flasher_ifnset runner)
761  board_set_runner_ifnset(FLASH ${runner})
762endmacro()
763
764# A convenience macro for board_set_runner_ifnset(DEBUG ${runner}).
765macro(board_set_debugger_ifnset runner)
766  board_set_runner_ifnset(DEBUG ${runner})
767endmacro()
768
769# A convenience macro for board_set_runner_ifnset(ROBOT ${runner}).
770macro(board_set_robot_runner_ifnset runner)
771  board_set_runner_ifnset(ROBOT ${runner})
772endmacro()
773
774# A convenience macro for board_set_runner_ifnset(SIM ${runner}).
775macro(board_set_sim_runner_ifnset runner)
776  board_set_runner_ifnset(SIM ${runner})
777endmacro()
778
779# This function is intended for board.cmake files and application
780# CMakeLists.txt files.
781#
782# Usage from board.cmake files:
783#   board_runner_args(runner "--some-arg=val1" "--another-arg=val2")
784#
785# The build system will then ensure the command line used to
786# create the runner contains:
787#   --some-arg=val1 --another-arg=val2
788#
789# Within application CMakeLists.txt files, ensure that all calls to
790# board_runner_args() are part of a macro named app_set_runner_args(),
791# like this, which is defined before calling 'find_package(Zephyr)':
792#   macro(app_set_runner_args)
793#     board_runner_args(runner "--some-app-setting=value")
794#   endmacro()
795#
796# The build system tests for the existence of the macro and will
797# invoke it at the appropriate time if it is defined.
798#
799# Any explicitly provided settings given by this function override
800# defaults provided by the build system.
801function(board_runner_args runner)
802  string(MAKE_C_IDENTIFIER ${runner} runner_id)
803  # Note the "_EXPLICIT_" here, and see below.
804  set_property(GLOBAL APPEND PROPERTY BOARD_RUNNER_ARGS_EXPLICIT_${runner_id} ${ARGN})
805endfunction()
806
807# This function is intended for internal use by
808# boards/common/runner.board.cmake files.
809#
810# Basic usage:
811#   board_finalize_runner_args(runner)
812#
813# This ensures the build system captures all arguments added in any
814# board_runner_args() calls, and otherwise finishes registering a
815# runner for use.
816#
817# Extended usage:
818#   board_runner_args(runner "--some-arg=default-value")
819#
820# This provides common or default values for arguments. These are
821# placed before board_runner_args() calls, so they generally take
822# precedence, except for arguments which can be given multiple times
823# (use these with caution).
824function(board_finalize_runner_args runner)
825  # If the application provided a macro to add additional runner
826  # arguments, handle them.
827  if(COMMAND app_set_runner_args)
828    app_set_runner_args()
829  endif()
830
831  # Retrieve the list of explicitly set arguments.
832  string(MAKE_C_IDENTIFIER ${runner} runner_id)
833  get_property(explicit GLOBAL PROPERTY "BOARD_RUNNER_ARGS_EXPLICIT_${runner_id}")
834
835  # Note no _EXPLICIT_ here. This property contains the final list.
836  set_property(GLOBAL APPEND PROPERTY BOARD_RUNNER_ARGS_${runner_id}
837    # Default arguments from the common runner file come first.
838    ${ARGN}
839    # Arguments explicitly given with board_runner_args() come
840    # next, so they take precedence over the common runner file.
841    ${explicit}
842    # Arguments given via the CMake cache come last of all. Users
843    # can provide variables in this way from the CMake command line.
844    ${BOARD_RUNNER_ARGS_${runner_id}}
845    )
846
847  # Add the finalized runner to the global property list.
848  set_property(GLOBAL APPEND PROPERTY ZEPHYR_RUNNERS ${runner})
849endfunction()
850
851function(board_set_rimage_target target)
852  set(RIMAGE_TARGET ${target} CACHE STRING "rimage target")
853  zephyr_check_cache(RIMAGE_TARGET)
854endfunction()
855
856# Zephyr board revision:
857#
858# This section provides a function for revision checking.
859
860# Usage:
861#   board_check_revision(FORMAT <LETTER | NUMBER | MAJOR.MINOR.PATCH>
862#                        [EXACT]
863#                        [DEFAULT_REVISION <revision>]
864#                        [HIGHEST_REVISION <revision>]
865#   )
866#
867# Zephyr board extension function.
868#
869# This function can be used in `boards/<board>/revision.cmake` to check a user
870# requested revision against available board revisions.
871#
872# The function will check the revision from `-DBOARD=<board>@<revision>` that
873# is provided by the user according to the arguments.
874# When `EXACT` is not specified, this function will set the Zephyr build system
875# variable `ACTIVE_BOARD_REVISION` with the selected revision.
876#
877# FORMAT <LETTER | NUMBER | MAJOR.MINOR.PATCH>: Specify the revision format.
878#         LETTER:             Revision format is a single letter from A - Z.
879#         NUMBER:             Revision format is a single integer number.
880#         MAJOR.MINOR.PATCH:  Revision format is three numbers, separated by `.`,
881#                             `x.y.z`. Trailing zeroes may be omitted on the
882#                             command line, which means:
883#                             1.0.0 == 1.0 == 1
884#
885# OPTIONAL: Revision specifier is optional. If revision is not provided the base
886#           board will be used. If both `EXACT` and `OPTIONAL` are given, then
887#           specifying the revision is optional, but if it is given then the
888#           `EXACT` requirements apply. Mutually exclusive with `DEFAULT_REVISION`.
889#
890# EXACT: Revision is required to be an exact match. As example, available revisions are:
891#        0.1.0 and 0.3.0, and user provides 0.2.0, then an error is reported
892#        when `EXACT` is given.
893#        If `EXACT` is not provided, then closest lower revision will be selected
894#        as the active revision, which in the example will be `0.1.0`.
895#
896# DEFAULT_REVISION: Provides a default revision to use when user has not selected
897#                   a revision number. If no default revision is provided then
898#                   user will be printed with an error if no revision is given
899#                   on the command line.
900#
901# HIGHEST_REVISION: Allows to specify highest valid revision for a board.
902#                   This can be used to ensure that a newer board cannot be used
903#                   with an older Zephyr. As example, if current board supports
904#                   revisions 0.x.0-0.99.99 and 1.0.0-1.99.99, and it is expected
905#                   that current board implementation will not work with board
906#                   revision 2.0.0, then HIGHEST_REVISION can be set to 1.99.99,
907#                   and user will be printed with an error if using
908#                   `<board>@2.0.0` or higher.
909#                   This field is not needed when `EXACT` is used.
910#
911# VALID_REVISIONS:  A list of valid revisions for this board.
912#                   If this argument is not provided, then each Kconfig fragment
913#                   of the form ``<board>_<revision>.conf`` in the board folder
914#                   will be used as a valid revision for the board.
915#
916function(board_check_revision)
917  set(options OPTIONAL EXACT)
918  set(single_args FORMAT DEFAULT_REVISION HIGHEST_REVISION)
919  set(multi_args  VALID_REVISIONS)
920  cmake_parse_arguments(BOARD_REV "${options}" "${single_args}" "${multi_args}" ${ARGN})
921
922  string(TOUPPER ${BOARD_REV_FORMAT} BOARD_REV_FORMAT)
923
924  if(DEFINED BOARD_REV_DEFAULT_REVISION AND BOARD_REV_OPTIONAL)
925    message(FATAL_ERROR "Arguments BOARD_REVISION and OPTIONAL are mutually exclusive")
926  endif()
927
928  if(NOT DEFINED BOARD_REVISION)
929    if(BOARD_REV_OPTIONAL)
930      return()
931    elseif(DEFINED BOARD_REV_DEFAULT_REVISION)
932      set(BOARD_REVISION ${BOARD_REV_DEFAULT_REVISION})
933      set(BOARD_REVISION ${BOARD_REVISION} PARENT_SCOPE)
934    else()
935      message(FATAL_ERROR "No board revision specified, Board: `${BOARD}` \
936              requires a revision. Please use: `-DBOARD=${BOARD}@<revision>`")
937    endif()
938  endif()
939
940  if(DEFINED BOARD_REV_HIGHEST_REVISION)
941    if(((BOARD_REV_FORMAT STREQUAL LETTER) AND
942        (BOARD_REVISION STRGREATER BOARD_REV_HIGHEST_REVISION)) OR
943       ((BOARD_REV_FORMAT STREQUAL NUMBER) AND
944       (BOARD_REVISION GREATER BOARD_REV_HIGHEST_REVISION)) OR
945       ((BOARD_REV_FORMAT MATCHES "^MAJOR\.MINOR\.PATCH$") AND
946        (BOARD_REVISION VERSION_GREATER BOARD_REV_HIGHEST_REVISION))
947    )
948      message(FATAL_ERROR "Board revision `${BOARD_REVISION}` greater than \
949              highest supported revision `${BOARD_REV_HIGHEST_REVISION}`. \
950              Please specify a valid board revision.")
951    endif()
952  endif()
953
954  if(BOARD_REV_FORMAT STREQUAL LETTER)
955    set(revision_regex "([A-Z])")
956  elseif(BOARD_REV_FORMAT STREQUAL NUMBER)
957    set(revision_regex "([0-9]+)")
958  elseif(BOARD_REV_FORMAT MATCHES "^MAJOR\.MINOR\.PATCH$")
959    set(revision_regex "((0|[1-9][0-9]*)(\.[0-9]+)(\.[0-9]+))")
960    # We allow loose <board>@<revision> typing on command line.
961    # so append missing zeroes.
962    if(BOARD_REVISION MATCHES "((0|[1-9][0-9]*)(\.[0-9]+)?(\.[0-9]+)?)")
963      if(NOT CMAKE_MATCH_3)
964        set(BOARD_REVISION ${BOARD_REVISION}.0)
965        set(BOARD_REVISION ${BOARD_REVISION} PARENT_SCOPE)
966      endif()
967      if(NOT CMAKE_MATCH_4)
968        set(BOARD_REVISION ${BOARD_REVISION}.0)
969        set(BOARD_REVISION ${BOARD_REVISION} PARENT_SCOPE)
970      endif()
971    endif()
972  else()
973    message(FATAL_ERROR "Invalid format specified for \
974    `board_check_revision(FORMAT <LETTER | NUMBER | MAJOR.MINOR.PATCH>)`")
975  endif()
976
977  if(NOT (BOARD_REVISION MATCHES "^${revision_regex}$"))
978    message(FATAL_ERROR "Invalid revision format used for `${BOARD_REVISION}`. \
979            Board `${BOARD}` uses revision format: ${BOARD_REV_FORMAT}.")
980  endif()
981
982  if(NOT DEFINED BOARD_REV_VALID_REVISIONS)
983    file(GLOB revision_candidates LIST_DIRECTORIES false RELATIVE ${BOARD_DIR}
984         ${BOARD_DIR}/${BOARD}_*.conf
985    )
986    string(REPLACE "." "_" underscore_revision_regex ${revision_regex})
987    set(file_revision_regex "${BOARD}_${underscore_revision_regex}.conf")
988    foreach(candidate ${revision_candidates})
989      if(${candidate} MATCHES "${file_revision_regex}")
990        string(REPLACE "_" "." FOUND_BOARD_REVISION ${CMAKE_MATCH_1})
991        list(APPEND BOARD_REV_VALID_REVISIONS ${FOUND_BOARD_REVISION})
992      endif()
993    endforeach()
994  endif()
995
996  if(${BOARD_REVISION} IN_LIST BOARD_REV_VALID_REVISIONS)
997    # Found exact match.
998    return()
999  endif()
1000
1001  if(NOT BOARD_REV_EXACT)
1002    foreach(TEST_REVISION ${BOARD_REV_VALID_REVISIONS})
1003      if((BOARD_REV_FORMAT MATCHES "^MAJOR\.MINOR\.PATCH$") AND
1004         (${BOARD_REVISION} VERSION_GREATER_EQUAL ${TEST_REVISION}) AND
1005         (${TEST_REVISION} VERSION_GREATER_EQUAL "${ACTIVE_BOARD_REVISION}")
1006      )
1007        set(ACTIVE_BOARD_REVISION ${TEST_REVISION})
1008      elseif((BOARD_REV_FORMAT STREQUAL LETTER) AND
1009             (${BOARD_REVISION} STRGREATER ${TEST_REVISION}) AND
1010             (${TEST_REVISION} STRGREATER "${ACTIVE_BOARD_REVISION}")
1011      )
1012        set(ACTIVE_BOARD_REVISION ${TEST_REVISION})
1013      elseif((BOARD_REV_FORMAT STREQUAL NUMBER) AND
1014             (${BOARD_REVISION} GREATER ${TEST_REVISION}) AND
1015             (${TEST_REVISION} GREATER "${ACTIVE_BOARD_REVISION}")
1016      )
1017        set(ACTIVE_BOARD_REVISION ${TEST_REVISION})
1018      endif()
1019    endforeach()
1020  endif()
1021
1022  if(BOARD_REV_EXACT OR NOT DEFINED ACTIVE_BOARD_REVISION)
1023    message(FATAL_ERROR "Board revision `${BOARD_REVISION}` for board \
1024            `${BOARD}` not found. Please specify a valid board revision.")
1025  endif()
1026
1027  set(ACTIVE_BOARD_REVISION ${ACTIVE_BOARD_REVISION} PARENT_SCOPE)
1028endfunction()
1029
1030# 1.5. Misc.
1031
1032# zephyr_check_compiler_flag is a part of Zephyr's toolchain
1033# infrastructure. It should be used when testing toolchain
1034# capabilities and it should normally be used in place of the
1035# functions:
1036#
1037# check_compiler_flag
1038# check_c_compiler_flag
1039# check_cxx_compiler_flag
1040#
1041# See check_compiler_flag() for API documentation as it has the same
1042# API.
1043#
1044# It is implemented as a wrapper on top of check_compiler_flag, which
1045# again wraps the CMake-builtin's check_c_compiler_flag and
1046# check_cxx_compiler_flag.
1047#
1048# It takes time to check for compatibility of flags against toolchains
1049# so we cache the capability test results in USER_CACHE_DIR (This
1050# caching comes in addition to the caching that CMake does in the
1051# build folder's CMakeCache.txt)
1052function(zephyr_check_compiler_flag lang option check)
1053  # Check if the option is covered by any hardcoded check before doing
1054  # an automated test.
1055  zephyr_check_compiler_flag_hardcoded(${lang} "${option}" _${check} exists)
1056  if(exists)
1057    set(${check} ${_${check}} PARENT_SCOPE)
1058    return()
1059  endif()
1060
1061  # Locate the cache directory
1062  set_ifndef(
1063    ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE_DIR
1064    ${USER_CACHE_DIR}/ToolchainCapabilityDatabase
1065    )
1066
1067  # The toolchain capability database/cache is maintained as a
1068  # directory of files. The filenames in the directory are keys, and
1069  # the file contents are the values in this key-value store.
1070
1071  # We need to create a unique key wrt. testing the toolchain
1072  # capability. This key must include everything that can affect the
1073  # toolchain test.
1074  #
1075  # Also, to fit the key into a filename we calculate the MD5 sum of
1076  # the key.
1077
1078  # The 'cacheformat' must be bumped if a bug in the caching mechanism
1079  # is detected and all old keys must be invalidated.
1080  set(cacheformat 3)
1081
1082  set(key_string "")
1083  set(key_string "${key_string}${cacheformat}_")
1084  set(key_string "${key_string}${TOOLCHAIN_SIGNATURE}_")
1085  set(key_string "${key_string}${lang}_")
1086  set(key_string "${key_string}${option}_")
1087  set(key_string "${key_string}${CMAKE_REQUIRED_FLAGS}_")
1088
1089  string(MD5 key "${key_string}")
1090
1091  # Check the cache
1092  set(key_path ${ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE_DIR}/${key})
1093  if(EXISTS ${key_path})
1094    file(READ
1095    ${key_path}   # File to be read
1096    key_value     # Output variable
1097    LIMIT 1       # Read at most 1 byte ('0' or '1')
1098    )
1099
1100    set(${check} ${key_value} PARENT_SCOPE)
1101    return()
1102  endif()
1103
1104  # Flags that start with -Wno-<warning> can not be tested by
1105  # check_compiler_flag, they will always pass, but -W<warning> can be
1106  # tested, so to test -Wno-<warning> flags we test -W<warning>
1107  # instead.
1108  if("${option}" MATCHES "-Wno-(.*)")
1109    string(REPLACE "-Wno-" "-W" possibly_translated_option "${option}")
1110  else()
1111    set(possibly_translated_option ${option})
1112  endif()
1113
1114  check_compiler_flag(${lang} "${possibly_translated_option}" inner_check)
1115
1116  set(${check} ${inner_check} PARENT_SCOPE)
1117
1118  # Populate the cache
1119  if(NOT (EXISTS ${key_path}))
1120
1121    # This is racy. As often with race conditions, this one can easily be
1122    # made worse and demonstrated with a simple delay:
1123    #    execute_process(COMMAND "sleep" "5")
1124    # Delete the cache, add the sleep above and run twister with a
1125    # large number of JOBS. Once it's done look at the log.txt file
1126    # below and you will see that concurrent cmake processes created the
1127    # same files multiple times.
1128
1129    # While there are a number of reasons why this race seems both very
1130    # unlikely and harmless, let's play it safe anyway and write to a
1131    # private, temporary file first. All modern filesystems seem to
1132    # support at least one atomic rename API and cmake's file(RENAME
1133    # ...) officially leverages that.
1134    string(RANDOM LENGTH 8 tempsuffix)
1135
1136    file(
1137      WRITE
1138      "${key_path}_tmp_${tempsuffix}"
1139      ${inner_check}
1140      )
1141    file(
1142      RENAME
1143      "${key_path}_tmp_${tempsuffix}" "${key_path}"
1144      )
1145
1146    # Populate a metadata file (only intended for trouble shooting)
1147    # with information about the hash, the toolchain capability
1148    # result, and the toolchain test.
1149    file(
1150      APPEND
1151      ${ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE_DIR}/log.txt
1152      "${inner_check} ${key} ${key_string}\n"
1153      )
1154  endif()
1155endfunction()
1156
1157function(zephyr_check_compiler_flag_hardcoded lang option check exists)
1158  # Various flags that are not supported for CXX may not be testable
1159  # because they would produce a warning instead of an error during
1160  # the test.  Exclude them by toolchain-specific blocklist.
1161  if((${lang} STREQUAL CXX) AND ("${option}" IN_LIST CXX_EXCLUDED_OPTIONS))
1162    set(${check} 0 PARENT_SCOPE)
1163    set(${exists} 1 PARENT_SCOPE)
1164  else()
1165    # There does not exist a hardcoded check for this option.
1166    set(${exists} 0 PARENT_SCOPE)
1167  endif()
1168endfunction(zephyr_check_compiler_flag_hardcoded)
1169
1170# zephyr_linker_sources(<location> [SORT_KEY <sort_key>] <files>)
1171#
1172# <files> is one or more .ld formatted files whose contents will be
1173#    copied/included verbatim into the given <location> in the global linker.ld.
1174#    Preprocessor directives work inside <files>. Relative paths are resolved
1175#    relative to the calling file, like zephyr_sources().
1176#    Subsequent calls to zephyr_linker_sources with the same file(s) will remove
1177#    these from the original location. Only the last call is considered.
1178# <location> is one of
1179#    NOINIT        Inside the noinit output section.
1180#    RWDATA        Inside the data output section.
1181#    RODATA        Inside the rodata output section.
1182#    ROM_START     Inside the first output section of the image. This option is
1183#                  currently only available on ARM Cortex-M, ARM Cortex-R,
1184#                  x86, ARC, openisa_rv32m1, and RISC-V.
1185#    ROM_SECTIONS  Inside the ROMABLE_REGION GROUP, not initialized.
1186#    RAM_SECTIONS  Inside the RAMABLE_REGION GROUP, not initialized.
1187#    DATA_SECTIONS Inside the RAMABLE_REGION GROUP, initialized.
1188#    RAMFUNC_SECTION Inside the RAMFUNC RAMABLE_REGION GROUP, not initialized.
1189#    NOCACHE_SECTION Inside the NOCACHE section
1190#    ITCM_SECTION  Inside the itcm section.
1191#    DTCM_SECTION  Inside the dtcm data section.
1192#    SECTIONS      Near the end of the file. Don't use this when linking into
1193#                  RAMABLE_REGION, use RAM_SECTIONS instead.
1194#    PINNED_RODATA Similar to RODATA but pinned in memory.
1195#    PINNED_RAM_SECTIONS
1196#                  Similar to RAM_SECTIONS but pinned in memory.
1197#    PINNED_DATA_SECTIONS
1198#                  Similar to DATA_SECTIONS but pinned in memory.
1199# <sort_key> is an optional key to sort by inside of each location. The key must
1200#    be alphanumeric, and the keys are sorted alphabetically. If no key is
1201#    given, the key 'default' is used. Keys are case-sensitive.
1202#
1203# Use NOINIT, RWDATA, and RODATA unless they don't work for your use case.
1204#
1205# When placing into NOINIT, RWDATA, RODATA, ROM_START, RAMFUNC_SECTION,
1206# NOCACHE_SECTION, DTCM_SECTION or ITCM_SECTION the contents of the files will
1207# be placed inside an output section, so assume the section definition is
1208# already present, e.g.:
1209#    _mysection_start = .;
1210#    KEEP(*(.mysection));
1211#    _mysection_end = .;
1212#    _mysection_size = ABSOLUTE(_mysection_end - _mysection_start);
1213#
1214# When placing into SECTIONS, ROM_SECTIONS, RAM_SECTIONS or DATA_SECTIONS, the
1215# files must instead define their own output sections to achieve the same thing:
1216#    SECTION_PROLOGUE(.mysection,,)
1217#    {
1218#        _mysection_start = .;
1219#        KEEP(*(.mysection))
1220#        _mysection_end = .;
1221#    } GROUP_LINK_IN(ROMABLE_REGION)
1222#    _mysection_size = _mysection_end - _mysection_start;
1223#
1224# Note about the above examples: If the first example was used with RODATA, and
1225# the second with SECTIONS, the two examples do the same thing from a user
1226# perspective.
1227#
1228# Friendly reminder: Beware of the different ways the location counter ('.')
1229# behaves inside vs. outside section definitions.
1230function(zephyr_linker_sources location)
1231  # Set up the paths to the destination files. These files are #included inside
1232  # the global linker.ld.
1233  set(snippet_base       "${__build_dir}/include/generated")
1234  set(sections_path      "${snippet_base}/snippets-sections.ld")
1235  set(rom_sections_path  "${snippet_base}/snippets-rom-sections.ld")
1236  set(ram_sections_path  "${snippet_base}/snippets-ram-sections.ld")
1237  set(data_sections_path "${snippet_base}/snippets-data-sections.ld")
1238  set(rom_start_path     "${snippet_base}/snippets-rom-start.ld")
1239  set(noinit_path        "${snippet_base}/snippets-noinit.ld")
1240  set(rwdata_path        "${snippet_base}/snippets-rwdata.ld")
1241  set(rodata_path        "${snippet_base}/snippets-rodata.ld")
1242  set(ramfunc_path       "${snippet_base}/snippets-ramfunc-section.ld")
1243  set(nocache_path       "${snippet_base}/snippets-nocache-section.ld")
1244  set(itcm_path          "${snippet_base}/snippets-itcm-section.ld")
1245  set(dtcm_path          "${snippet_base}/snippets-dtcm-section.ld")
1246
1247  set(pinned_ram_sections_path  "${snippet_base}/snippets-pinned-ram-sections.ld")
1248  set(pinned_data_sections_path "${snippet_base}/snippets-pinned-data-sections.ld")
1249  set(pinned_rodata_path        "${snippet_base}/snippets-pinned-rodata.ld")
1250
1251  # Clear destination files if this is the first time the function is called.
1252  get_property(cleared GLOBAL PROPERTY snippet_files_cleared)
1253  if (NOT DEFINED cleared)
1254    file(WRITE ${sections_path} "")
1255    file(WRITE ${rom_sections_path} "")
1256    file(WRITE ${ram_sections_path} "")
1257    file(WRITE ${data_sections_path} "")
1258    file(WRITE ${rom_start_path} "")
1259    file(WRITE ${noinit_path} "")
1260    file(WRITE ${rwdata_path} "")
1261    file(WRITE ${rodata_path} "")
1262    file(WRITE ${ramfunc_path} "")
1263    file(WRITE ${nocache_path} "")
1264    file(WRITE ${itcm_path} "")
1265    file(WRITE ${dtcm_path} "")
1266    file(WRITE ${pinned_ram_sections_path} "")
1267    file(WRITE ${pinned_data_sections_path} "")
1268    file(WRITE ${pinned_rodata_path} "")
1269    set_property(GLOBAL PROPERTY snippet_files_cleared true)
1270  endif()
1271
1272  # Choose destination file, based on the <location> argument.
1273  if ("${location}" STREQUAL "SECTIONS")
1274    set(snippet_path "${sections_path}")
1275  elseif("${location}" STREQUAL "ROM_SECTIONS")
1276    set(snippet_path "${rom_sections_path}")
1277  elseif("${location}" STREQUAL "RAM_SECTIONS")
1278    set(snippet_path "${ram_sections_path}")
1279  elseif("${location}" STREQUAL "DATA_SECTIONS")
1280    set(snippet_path "${data_sections_path}")
1281  elseif("${location}" STREQUAL "ROM_START")
1282    set(snippet_path "${rom_start_path}")
1283  elseif("${location}" STREQUAL "NOINIT")
1284    set(snippet_path "${noinit_path}")
1285  elseif("${location}" STREQUAL "RWDATA")
1286    set(snippet_path "${rwdata_path}")
1287  elseif("${location}" STREQUAL "RODATA")
1288    set(snippet_path "${rodata_path}")
1289  elseif("${location}" STREQUAL "RAMFUNC_SECTION")
1290    set(snippet_path "${ramfunc_path}")
1291  elseif("${location}" STREQUAL "NOCACHE_SECTION")
1292    set(snippet_path "${nocache_path}")
1293  elseif("${location}" STREQUAL "ITCM_SECTION")
1294    dt_has_chosen(HAS_ITCM PROPERTY "zephyr,itcm")
1295    if(NOT HAS_ITCM)
1296      message(FATAL_ERROR "Trying to link snippet into itcm but no itcm available. "
1297              "Add `zephyr,itcm` to the device tree if supported on your device or choose another "
1298              "location.")
1299    endif()
1300    set(snippet_path "${itcm_path}")
1301  elseif("${location}" STREQUAL "DTCM_SECTION")
1302    dt_has_chosen(HAS_DTCM PROPERTY "zephyr,dtcm")
1303    if(NOT HAS_DTCM)
1304      message(FATAL_ERROR "Trying to link snippet into dtcm but no dtcm available. "
1305              "Add `zephyr,dtcm` to the device tree if supported on your device or choose another "
1306              "location.")
1307    endif()
1308    set(snippet_path "${dtcm_path}")
1309  elseif("${location}" STREQUAL "PINNED_RAM_SECTIONS")
1310    set(snippet_path "${pinned_ram_sections_path}")
1311  elseif("${location}" STREQUAL "PINNED_DATA_SECTIONS")
1312    set(snippet_path "${pinned_data_sections_path}")
1313  elseif("${location}" STREQUAL "PINNED_RODATA")
1314    set(snippet_path "${pinned_rodata_path}")
1315  else()
1316    message(fatal_error "Must choose valid location for linker snippet.")
1317  endif()
1318
1319  cmake_parse_arguments(L "" "SORT_KEY" "" ${ARGN})
1320  set(SORT_KEY default)
1321  if(DEFINED L_SORT_KEY)
1322    set(SORT_KEY ${L_SORT_KEY})
1323  endif()
1324
1325  foreach(file IN ITEMS ${L_UNPARSED_ARGUMENTS})
1326    # Resolve path.
1327    if(IS_ABSOLUTE ${file})
1328      set(path ${file})
1329    else()
1330      set(path ${CMAKE_CURRENT_SOURCE_DIR}/${file})
1331    endif()
1332
1333    if(IS_DIRECTORY ${path})
1334      message(FATAL_ERROR "zephyr_linker_sources() was called on a directory")
1335    endif()
1336
1337    # Find the relative path to the linker file from the include folder.
1338    file(RELATIVE_PATH relpath ${ZEPHYR_BASE}/include ${path})
1339
1340    # Create strings to be written into the file
1341    set (include_str "/* Sort key: \"${SORT_KEY}\" */#include \"${relpath}\"")
1342
1343    # Remove line from other snippet file, if already used
1344    get_property(old_path GLOBAL PROPERTY "snippet_files_used_${relpath}")
1345    if (DEFINED old_path)
1346      file(STRINGS ${old_path} lines)
1347      list(FILTER lines EXCLUDE REGEX ${relpath})
1348      string(REPLACE ";" "\n;" lines "${lines}") # Add newline to each line.
1349      file(WRITE ${old_path} ${lines} "\n")
1350    endif()
1351    set_property(GLOBAL PROPERTY "snippet_files_used_${relpath}" ${snippet_path})
1352
1353    # Add new line to existing lines, sort them, and write them back.
1354    file(STRINGS ${snippet_path} lines) # Get current lines (without newlines).
1355    list(APPEND lines ${include_str})
1356    list(SORT lines)
1357    string(REPLACE ";" "\n;" lines "${lines}") # Add newline to each line.
1358    file(WRITE ${snippet_path} ${lines} "\n")
1359  endforeach()
1360endfunction(zephyr_linker_sources)
1361
1362# Helper macro for conditionally calling zephyr_code_relocate() when a
1363# specific Kconfig symbol is enabled. See zephyr_code_relocate() description
1364# for supported arguments.
1365macro(zephyr_code_relocate_ifdef feature_toggle)
1366  if(${${feature_toggle}})
1367    zephyr_code_relocate(${ARGN})
1368  endif()
1369endmacro()
1370
1371# Helper function for CONFIG_CODE_DATA_RELOCATION
1372# This function may either be invoked with a list of files, or a library
1373# name to relocate.
1374#
1375# The FILES directive will relocate a list of files (wildcards supported)
1376# This directive will relocate file1. and file2.c to SRAM:
1377# zephyr_code_relocate(FILES file1.c file2.c LOCATION SRAM)
1378# Note, files can also be passed as a comma separated list to support using
1379# cmake generator arguments
1380#
1381# The LIBRARY directive will relocate a library
1382# This directive will relocate the target my_lib to SRAM:
1383# zephyr_code_relocate(LIBRARY my_lib SRAM)
1384#
1385# The following optional arguments are supported:
1386# - NOCOPY: this flag indicates that the file data does not need to be copied
1387#   at boot time (For example, for flash XIP).
1388# - NOKEEP: suppress the generation of KEEP() statements in the linker script,
1389#   to allow any unused code in the given files/library to be discarded.
1390# - PHDR [program_header]: add program header. Used on Xtensa platforms.
1391function(zephyr_code_relocate)
1392  set(options NOCOPY NOKEEP)
1393  set(single_args LIBRARY LOCATION PHDR)
1394  set(multi_args FILES)
1395  cmake_parse_arguments(CODE_REL "${options}" "${single_args}"
1396    "${multi_args}" ${ARGN})
1397  # Argument validation
1398  if(CODE_REL_UNPARSED_ARGUMENTS)
1399    message(FATAL_ERROR "zephyr_code_relocate(${ARGV0} ...) "
1400      "given unknown arguments: ${CODE_REL_UNPARSED_ARGUMENTS}")
1401  endif()
1402  if((NOT CODE_REL_FILES) AND (NOT CODE_REL_LIBRARY))
1403    message(FATAL_ERROR
1404      "zephyr_code_relocate() requires either FILES or LIBRARY be provided")
1405  endif()
1406  if(CODE_REL_FILES AND CODE_REL_LIBRARY)
1407    message(FATAL_ERROR "zephyr_code_relocate() only accepts "
1408      "one argument between FILES and LIBRARY")
1409  endif()
1410  if(NOT CODE_REL_LOCATION)
1411    message(FATAL_ERROR "zephyr_code_relocate() requires a LOCATION argument")
1412  endif()
1413  if(CODE_REL_LIBRARY)
1414    # Use cmake generator expression to convert library to file list,
1415    # supporting relative and absolute paths
1416    set(genex_src_dir "$<TARGET_PROPERTY:${CODE_REL_LIBRARY},SOURCE_DIR>")
1417    set(genex_src_list "$<TARGET_PROPERTY:${CODE_REL_LIBRARY},SOURCES>")
1418
1419    if(CMAKE_HOST_WIN32)
1420      # Note that this assumes windows absolute filenames start with a letter and colon, this does
1421      # not support \\x network paths and is untested under the likes of msys2/cygwin
1422      set(src_list_abs "$<FILTER:${genex_src_list},INCLUDE,^[A-Za-z]\:>")
1423      set(src_list_rel "$<FILTER:${genex_src_list},EXCLUDE,^[A-Za-z]\:>")
1424    else()
1425      set(src_list_abs "$<FILTER:${genex_src_list},INCLUDE,^/>")
1426      set(src_list_rel "$<FILTER:${genex_src_list},EXCLUDE,^/>")
1427    endif()
1428
1429    set(src_list "${genex_src_dir}/$<JOIN:${src_list_rel},$<SEMICOLON>${genex_src_dir}/>")
1430    set(nonempty_src_list "$<$<BOOL:${src_list_rel}>:${src_list}>")
1431    set(sep_list "$<$<AND:$<BOOL:${src_list_abs}>,$<BOOL:${src_list_rel}>>:$<SEMICOLON>>")
1432    set(file_list "${src_list_abs}${sep_list}${nonempty_src_list}")
1433  else()
1434    # Check if CODE_REL_FILES is a generator expression, if so leave it
1435    # untouched.
1436    string(GENEX_STRIP "${CODE_REL_FILES}" no_genex)
1437    if(CODE_REL_FILES STREQUAL no_genex)
1438      # no generator expression in CODE_REL_FILES, check if list of files
1439      # is absolute
1440      foreach(file ${CODE_REL_FILES})
1441        if(NOT IS_ABSOLUTE ${file})
1442          set(file ${CMAKE_CURRENT_SOURCE_DIR}/${file})
1443        endif()
1444        list(APPEND file_list ${file})
1445      endforeach()
1446    else()
1447      # Generator expression is present in file list. Leave the list untouched.
1448      set(file_list ${CODE_REL_FILES})
1449    endif()
1450  endif()
1451  if(NOT CODE_REL_NOCOPY)
1452    set(flag_list COPY)
1453  else()
1454    set(flag_list NOCOPY)
1455  endif()
1456  if(CODE_REL_NOKEEP)
1457    list(APPEND flag_list NOKEEP)
1458  endif()
1459  if(CODE_REL_PHDR)
1460    set(CODE_REL_LOCATION "${CODE_REL_LOCATION}\ :${CODE_REL_PHDR}")
1461  endif()
1462  # We use the "|" character to separate code relocation directives, instead of
1463  # using set_property(APPEND) to produce a ";"-separated CMake list. This way,
1464  # each directive can embed multiple CMake lists, representing flags and files,
1465  # the latter of which can come from generator expressions.
1466  get_property(code_rel_str TARGET code_data_relocation_target
1467    PROPERTY COMPILE_DEFINITIONS)
1468  set_property(TARGET code_data_relocation_target
1469    PROPERTY COMPILE_DEFINITIONS
1470    "${code_rel_str}|${CODE_REL_LOCATION}:${flag_list}:${file_list}")
1471endfunction()
1472
1473# Usage:
1474#   check_dtc_flag("-Wtest" DTC_WARN_TEST)
1475#
1476# Writes 1 to the output variable 'ok' if
1477# the flag is supported, otherwise writes 0.
1478#
1479# using
1480function(check_dtc_flag flag ok)
1481  execute_process(
1482    COMMAND
1483    ${DTC} ${flag} -v
1484    ERROR_QUIET
1485    OUTPUT_QUIET
1486    RESULT_VARIABLE dtc_check_ret
1487  )
1488  if (dtc_check_ret EQUAL 0)
1489    set(${ok} 1 PARENT_SCOPE)
1490  else()
1491    set(${ok} 0 PARENT_SCOPE)
1492  endif()
1493endfunction()
1494
1495# Function to round number to next power of two.
1496#
1497# Usage:
1498#   pow2round(<variable>)
1499#
1500# Example:
1501# set(test 2)
1502# pow2round(test)
1503# # test is still 2
1504#
1505# set(test 5)
1506# pow2round(test)
1507# # test is now 8
1508#
1509# Arguments:
1510# n   = Variable containing the number to round
1511function(pow2round n)
1512  math(EXPR x "${${n}} & (${${n}} - 1)")
1513  if(${x} EQUAL 0)
1514    return()
1515  endif()
1516
1517  math(EXPR ${n} "${${n}} | (${${n}} >> 1)")
1518  math(EXPR ${n} "${${n}} | (${${n}} >> 2)")
1519  math(EXPR ${n} "${${n}} | (${${n}} >> 4)")
1520  math(EXPR ${n} "${${n}} | (${${n}} >> 8)")
1521  math(EXPR ${n} "${${n}} | (${${n}} >> 16)")
1522  math(EXPR ${n} "${${n}} | (${${n}} >> 32)")
1523  math(EXPR ${n} "${${n}} + 1")
1524  set(${n} ${${n}} PARENT_SCOPE)
1525endfunction()
1526
1527# Function to create a build string based on BOARD, BOARD_REVISION, and BUILD
1528# type.
1529#
1530# This is a common function to ensure that build strings are always created
1531# in a uniform way.
1532# A single string is returned containing the full build string constructed from
1533# all arguments.
1534#
1535# When MERGE is supplied a list of build strings will be returned with the full
1536# build string as first item in the list.
1537# The full order of build strings returned in the list will be:
1538# - Normalized board target build string, this includes qualifiers and revision
1539# - Build string with board variants removed in addition
1540# - Build string with cpuset removed in addition
1541# - Build string with soc removed in addition
1542#
1543# If BUILD is supplied, then build type will be appended to each entry in the
1544# list above.
1545# If REVISION is supplied or obtained as system wide setting a build string
1546# with the sanitized revision string will be added in addition to the
1547# non-revisioned entry for each entry.
1548#
1549# Usage:
1550#   zephyr_build_string(<out-variable>
1551#                       BOARD <board>
1552#                       [SHORT <out-variable>]
1553#                       [BOARD_QUALIFIERS <qualifiers>]
1554#                       [BOARD_REVISION <revision>]
1555#                       [BUILD <type>]
1556#                       [MERGE [REVERSE]]
1557#   )
1558#   zephyr_build_string(<out-variable>
1559#                       BOARD_QUALIFIERS <qualifiers>
1560#                       [BUILD <type>]
1561#                       [MERGE [REVERSE]]
1562#   )
1563#
1564# <out-variable>:            Output variable where the build string will be returned.
1565# SHORT <out-variable>:      Output variable where the shortened build string will be returned.
1566# BOARD <board>:             Board name to use when creating the build string.
1567# BOARD_REVISION <revision>: Board revision to use when creating the build string.
1568# BUILD <type>:              Build type to use when creating the build string.
1569# MERGE:                     Return a list of build strings instead of a single build string.
1570# REVERSE:                   Reverse the list before returning it.
1571#
1572# Examples
1573# calling
1574#   zephyr_build_string(build_string BOARD alpha BUILD debug)
1575# will return the string `alpha_debug` in `build_string` parameter.
1576#
1577# calling
1578#   zephyr_build_string(build_string BOARD alpha BOARD_REVISION 1.0.0 BUILD debug)
1579# will return the string `alpha_1_0_0_debug` in `build_string` parameter.
1580#
1581# calling
1582#   zephyr_build_string(build_string BOARD alpha BOARD_QUALIFIERS /soc/bar)
1583# will return the string `alpha_soc_bar` in `build_string` parameter.
1584#
1585# calling
1586#   zephyr_build_string(build_string BOARD alpha BOARD_REVISION 1.0.0 BOARD_QUALIFIERS /soc/bar MERGE)
1587# will return a list of the following strings
1588# `alpha_soc_bar_1_0_0;alpha_soc_bar` in `build_string` parameter.
1589#
1590# calling
1591#   zephyr_build_string(build_string SHORT short_build_string BOARD alpha BOARD_REVISION 1.0.0 BOARD_QUALIFIERS /soc/bar MERGE)
1592# will return two lists of the following strings
1593# `alpha_soc_bar_1_0_0;alpha_soc_bar` in `build_string` parameter.
1594# `alpha_bar_1_0_0;alpha_bar` in `short_build_string` parameter.
1595#
1596# calling
1597#   zephyr_build_string(build_string BOARD_QUALIFIERS /soc/bar/foo)
1598# will return the string `soc_bar_foo` in `build_string` parameter.
1599#
1600function(zephyr_build_string outvar)
1601  set(options MERGE REVERSE)
1602  set(single_args BOARD BOARD_QUALIFIERS BOARD_REVISION BUILD SHORT)
1603
1604  cmake_parse_arguments(BUILD_STR "${options}" "${single_args}" "" ${ARGN})
1605  if(BUILD_STR_UNPARSED_ARGUMENTS)
1606    message(FATAL_ERROR
1607      "zephyr_build_string(${ARGV0} <val> ...) given unknown arguments:"
1608      " ${BUILD_STR_UNPARSED_ARGUMENTS}"
1609    )
1610  endif()
1611
1612  if(DEFINED BUILD_STR_BOARD_REVISION AND NOT BUILD_STR_BOARD)
1613    message(FATAL_ERROR
1614      "zephyr_build_string(${ARGV0} <list> BOARD_REVISION ${BUILD_STR_BOARD_REVISION} ...)"
1615      " given without BOARD argument, please specify BOARD"
1616    )
1617  endif()
1618
1619  if(DEFINED BUILD_STR_BOARD_REVISION AND NOT DEFINED BUILD_STR_BOARD)
1620    message(FATAL_ERROR
1621      "zephyr_build_string(${ARGV0} <list> BOARD_REVISION ${BUILD_STR_BOARD_REVISION} ...)"
1622      " given without BOARD argument, these must be used together"
1623    )
1624  endif()
1625
1626  if(DEFINED BUILD_STR_SHORT AND NOT DEFINED BUILD_STR_BOARD)
1627    message(FATAL_ERROR
1628      "zephyr_build_string(${ARGV0} <list> SHORT ${BUILD_STR_SHORT} ...)"
1629      " given without BOARD argument, these must be used together"
1630    )
1631  endif()
1632
1633  string(REPLACE "/" ";" str_segment_list "${BUILD_STR_BOARD_QUALIFIERS}")
1634  string(REPLACE "." "_" revision_string "${BUILD_STR_BOARD_REVISION}")
1635
1636  string(JOIN "_" ${outvar} ${BUILD_STR_BOARD} ${str_segment_list} ${revision_string} ${BUILD_STR_BUILD})
1637
1638  if(BUILD_STR_MERGE)
1639    string(JOIN "_" variant_string ${BUILD_STR_BOARD} ${str_segment_list} ${BUILD_STR_BUILD})
1640
1641    if(NOT "${variant_string}" IN_LIST ${outvar})
1642      list(APPEND ${outvar} "${variant_string}")
1643    endif()
1644  endif()
1645
1646  if(BUILD_STR_REVERSE)
1647    list(REVERSE ${outvar})
1648  endif()
1649  list(REMOVE_DUPLICATES ${outvar})
1650
1651  if(BUILD_STR_SHORT AND BUILD_STR_BOARD_QUALIFIERS)
1652    string(REGEX REPLACE "^/[^/]*(.*)" "\\1" shortened_qualifiers "${BOARD_QUALIFIERS}")
1653    string(REPLACE "/" ";" str_short_segment_list "${shortened_qualifiers}")
1654    string(JOIN "_" ${BUILD_STR_SHORT}
1655           ${BUILD_STR_BOARD} ${str_short_segment_list} ${revision_string} ${BUILD_STR_BUILD}
1656    )
1657    if(BUILD_STR_MERGE)
1658      string(JOIN "_" variant_string ${BUILD_STR_BOARD} ${str_short_segment_list} ${BUILD_STR_BUILD})
1659
1660      if(NOT "${variant_string}" IN_LIST ${BUILD_STR_SHORT})
1661        list(APPEND ${BUILD_STR_SHORT} "${variant_string}")
1662      endif()
1663    endif()
1664
1665    if(BUILD_STR_REVERSE)
1666      list(REVERSE ${BUILD_STR_SHORT})
1667    endif()
1668    list(REMOVE_DUPLICATES ${BUILD_STR_SHORT})
1669    set(${BUILD_STR_SHORT} ${${BUILD_STR_SHORT}} PARENT_SCOPE)
1670  endif()
1671
1672  # This updates the provided outvar in parent scope (callers scope)
1673  set(${outvar} ${${outvar}} PARENT_SCOPE)
1674endfunction()
1675
1676# Function to add one or more directories to the include list passed to the syscall generator.
1677function(zephyr_syscall_include_directories)
1678  foreach(one_dir ${ARGV})
1679    if(EXISTS ${one_dir})
1680      set(include_dir ${one_dir})
1681    elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${one_dir})
1682      set(include_dir ${CMAKE_CURRENT_SOURCE_DIR}/${one_dir})
1683    else()
1684      message(FATAL_ERROR "Syscall include directory not found: ${one_dir}")
1685    endif()
1686
1687    target_include_directories(
1688      syscalls_interface INTERFACE
1689      ${include_dir}
1690    )
1691    add_dependencies(
1692      syscalls_interface
1693      ${include_dir}
1694    )
1695
1696    unset(include_dir)
1697  endforeach()
1698endfunction()
1699
1700# Function to add header file(s) to the list to be passed to syscall generator.
1701function(zephyr_syscall_header)
1702  foreach(one_file ${ARGV})
1703    if(EXISTS ${one_file})
1704      set(header_file ${one_file})
1705    elseif(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${one_file})
1706      set(header_file ${CMAKE_CURRENT_SOURCE_DIR}/${one_file})
1707    else()
1708      message(FATAL_ERROR "Syscall header file not found: ${one_file}")
1709    endif()
1710
1711    target_sources(
1712      syscalls_interface INTERFACE
1713      ${header_file}
1714    )
1715    add_dependencies(
1716      syscalls_interface
1717      ${header_file}
1718    )
1719
1720    unset(header_file)
1721  endforeach()
1722endfunction()
1723
1724# Function to add header file(s) to the list to be passed to syscall generator
1725# if condition is true.
1726function(zephyr_syscall_header_ifdef feature_toggle)
1727  if(${${feature_toggle}})
1728    zephyr_syscall_header(${ARGN})
1729  endif()
1730endfunction()
1731
1732# Verify blobs fetched using west. If the sha256 checksum isn't valid, a warning/
1733# fatal error message is printed (depends on REQUIRED flag).
1734#
1735# Usage:
1736#   zephyr_blobs_verify(<MODULE module|FILES file [files...]> [REQUIRED])
1737#
1738# Example:
1739# zephyr_blobs_verify(MODULE my_module REQUIRED) # verify all blobs in my_module and fail on error
1740# zephyr_blobs_verify(FILES img/file.bin)        # verify a single file and print on error
1741function(zephyr_blobs_verify)
1742  cmake_parse_arguments(BLOBS_VERIFY "REQUIRED" "MODULE" "FILES" ${ARGN})
1743
1744  if((DEFINED BLOBS_VERIFY_MODULE) EQUAL (DEFINED BLOBS_VERIFY_FILES))
1745    message(FATAL_ERROR "Either MODULE or FILES required when calling ${CMAKE_CURRENT_FUNCTION}")
1746  endif()
1747
1748  if(NOT WEST)
1749    return()
1750  endif()
1751
1752  execute_process(
1753    COMMAND ${WEST} blobs list ${BLOBS_VERIFY_MODULE} --format "{status} {abspath}"
1754    OUTPUT_VARIABLE BLOBS_LIST_OUTPUT
1755    OUTPUT_STRIP_TRAILING_WHITESPACE
1756    COMMAND_ERROR_IS_FATAL ANY
1757  )
1758
1759  if(${BLOBS_VERIFY_REQUIRED})
1760    set(msg_lvl FATAL_ERROR)
1761  else()
1762    set(msg_lvl WARNING)
1763  endif()
1764
1765  string(REPLACE "\n" ";" BLOBS_LIST ${BLOBS_LIST_OUTPUT})
1766
1767  if(DEFINED BLOBS_VERIFY_FILES)
1768    foreach(file ${BLOBS_VERIFY_FILES})
1769      # Resolve path.
1770      if(IS_ABSOLUTE ${file})
1771        file(REAL_PATH "${file}" real_path)
1772      else()
1773        file(REAL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${file}" real_path)
1774      endif()
1775      file(TO_NATIVE_PATH ${real_path} path)
1776
1777      message(VERBOSE "Verifying blob \"${path}\"")
1778
1779      if(NOT EXISTS "${path}")
1780        message(${msg_lvl} "Blob for path \"${path}\" missing. Update with: west blobs fetch")
1781      elseif(NOT "A ${path}" IN_LIST BLOBS_LIST)
1782        # Each path that has a correct sha256 is prefixed with an A
1783        message(${msg_lvl} "Blob for path \"${path}\" isn't valid. Update with: west blobs fetch")
1784      endif()
1785    endforeach()
1786  else()
1787    foreach(blob ${BLOBS_LIST})
1788      separate_arguments(blob)
1789      list(GET blob 0 status)
1790      list(GET blob 1 path)
1791
1792      message(VERBOSE "Verifying blob \"${path}\"")
1793
1794      if(NOT EXISTS "${path}")
1795        message(${msg_lvl} "Blob for path \"${path}\" missing. Update with: west blobs fetch ${BLOBS_VERIFY_MODULE}")
1796      elseif(NOT "${status}" STREQUAL "A")
1797        message(${msg_lvl} "Blob for path \"${path}\" isn't valid. Update with: west blobs fetch ${BLOBS_VERIFY_MODULE}")
1798      endif()
1799    endforeach()
1800  endif()
1801endfunction()
1802
1803########################################################
1804# 2. Kconfig-aware extensions
1805########################################################
1806#
1807# Kconfig is a configuration language developed for the Linux
1808# kernel. The below functions integrate CMake with Kconfig.
1809#
1810
1811# 2.1 Misc
1812#
1813# import_kconfig(<prefix> <kconfig_fragment> [<keys>] [TARGET <target>])
1814#
1815# Parse a KConfig fragment (typically with extension .config) and
1816# introduce all the symbols that are prefixed with 'prefix' into the
1817# CMake namespace. List all created variable names in the 'keys'
1818# output variable if present.
1819#
1820# <prefix>          : symbol prefix of settings in the Kconfig fragment.
1821# <kconfig_fragment>: absolute path to the config fragment file.
1822# <keys>            : output variable which will be populated with variable
1823#                     names loaded from the kconfig fragment.
1824# TARGET <target>   : set all symbols on <target> instead of adding them to the
1825#                     CMake namespace.
1826function(import_kconfig prefix kconfig_fragment)
1827  cmake_parse_arguments(IMPORT_KCONFIG "" "TARGET" "" ${ARGN})
1828  file(
1829    STRINGS
1830    ${kconfig_fragment}
1831    DOT_CONFIG_LIST
1832    ENCODING "UTF-8"
1833  )
1834
1835  foreach (LINE ${DOT_CONFIG_LIST})
1836    if("${LINE}" MATCHES "^(${prefix}[^=]+)=([ymn]|.+$)")
1837      # Matched a normal value assignment, like: CONFIG_NET_BUF=y
1838      # Note: if the value starts with 'y', 'm', or 'n', then we assume it's a
1839      # bool or tristate (we don't know the type from <kconfig_fragment> alone)
1840      # and we only match the first character. This is to align with Kconfiglib.
1841      set(CONF_VARIABLE_NAME "${CMAKE_MATCH_1}")
1842      set(CONF_VARIABLE_VALUE "${CMAKE_MATCH_2}")
1843    elseif("${LINE}" MATCHES "^# (${prefix}[^ ]+) is not set")
1844      # Matched something like: # CONFIG_FOO is not set
1845      # This is interpreted as: CONFIG_FOO=n
1846      set(CONF_VARIABLE_NAME "${CMAKE_MATCH_1}")
1847      set(CONF_VARIABLE_VALUE "n")
1848    else()
1849      # Ignore this line.
1850      # Note: we also ignore assignments which don't have the desired <prefix>.
1851      continue()
1852    endif()
1853
1854    # If the provided value is n, then the corresponding CMake variable or
1855    # target property will be unset.
1856    if("${CONF_VARIABLE_VALUE}" STREQUAL "n")
1857      if(DEFINED IMPORT_KCONFIG_TARGET)
1858        set_property(TARGET ${IMPORT_KCONFIG_TARGET} PROPERTY "${CONF_VARIABLE_NAME}")
1859      else()
1860        unset("${CONF_VARIABLE_NAME}" PARENT_SCOPE)
1861      endif()
1862      list(REMOVE_ITEM keys "${CONF_VARIABLE_NAME}")
1863      continue()
1864    endif()
1865
1866    # Otherwise, the variable/property will be set to the provided value.
1867    # For string values, we also remove the surrounding quotation marks.
1868    if("${CONF_VARIABLE_VALUE}" MATCHES "^\"(.*)\"$")
1869      set(CONF_VARIABLE_VALUE ${CMAKE_MATCH_1})
1870    endif()
1871
1872    if(DEFINED IMPORT_KCONFIG_TARGET)
1873      set_property(TARGET ${IMPORT_KCONFIG_TARGET} PROPERTY "${CONF_VARIABLE_NAME}" "${CONF_VARIABLE_VALUE}")
1874    else()
1875      set("${CONF_VARIABLE_NAME}" "${CONF_VARIABLE_VALUE}" PARENT_SCOPE)
1876    endif()
1877    list(APPEND keys "${CONF_VARIABLE_NAME}")
1878  endforeach()
1879
1880  if(DEFINED IMPORT_KCONFIG_TARGET)
1881    set_property(TARGET ${IMPORT_KCONFIG_TARGET} PROPERTY "kconfigs" "${keys}")
1882  endif()
1883
1884  list(LENGTH IMPORT_KCONFIG_UNPARSED_ARGUMENTS unparsed_length)
1885  if(unparsed_length GREATER 0)
1886    if(unparsed_length GREATER 1)
1887    # Two mandatory arguments and one optional, anything after that is an error.
1888      list(GET IMPORT_KCONFIG_UNPARSED_ARGUMENTS 1 first_invalid)
1889      message(FATAL_ERROR "Unexpected argument after '<keys>': import_kconfig(... ${first_invalid})")
1890    endif()
1891    set(${IMPORT_KCONFIG_UNPARSED_ARGUMENTS} "${keys}" PARENT_SCOPE)
1892  endif()
1893endfunction()
1894
1895########################################################
1896# 3. CMake-generic extensions
1897########################################################
1898#
1899# These functions extend the CMake API in a way that is not particular
1900# to Zephyr. Primarily they work around limitations in the CMake
1901# language to allow cleaner build scripts.
1902
1903# 3.1. *_ifdef
1904#
1905# Functions for conditionally executing CMake functions with oneliners
1906# e.g.
1907#
1908# if(CONFIG_FFT)
1909#   zephyr_library_source(
1910#     fft_32.c
1911#     fft_utils.c
1912#     )
1913# endif()
1914#
1915# Becomes
1916#
1917# zephyr_source_ifdef(
1918#   CONFIG_FFT
1919#   fft_32.c
1920#   fft_utils.c
1921#   )
1922#
1923# More Generally
1924# "<function-name>_ifdef(CONDITION args)"
1925# Becomes
1926# """
1927# if(CONDITION)
1928#   <function-name>(args)
1929# endif()
1930# """
1931#
1932# ifdef functions are added on an as-need basis. See
1933# https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html for
1934# a list of available functions.
1935function(add_subdirectory_ifdef feature_toggle source_dir)
1936  if(${${feature_toggle}})
1937    add_subdirectory(${source_dir} ${ARGN})
1938  endif()
1939endfunction()
1940
1941function(target_sources_ifdef feature_toggle target scope item)
1942  if(${${feature_toggle}})
1943    target_sources(${target} ${scope} ${item} ${ARGN})
1944  endif()
1945endfunction()
1946
1947function(target_compile_definitions_ifdef feature_toggle target scope item)
1948  if(${${feature_toggle}})
1949    target_compile_definitions(${target} ${scope} ${item} ${ARGN})
1950  endif()
1951endfunction()
1952
1953function(target_include_directories_ifdef feature_toggle target scope item)
1954  if(${${feature_toggle}})
1955    target_include_directories(${target} ${scope} ${item} ${ARGN})
1956  endif()
1957endfunction()
1958
1959function(target_link_libraries_ifdef feature_toggle target item)
1960  if(${${feature_toggle}})
1961    target_link_libraries(${target} ${item} ${ARGN})
1962  endif()
1963endfunction()
1964
1965function(add_compile_option_ifdef feature_toggle option)
1966  if(${${feature_toggle}})
1967    add_compile_options(${option})
1968  endif()
1969endfunction()
1970
1971function(target_compile_option_ifdef feature_toggle target scope option)
1972  if(${feature_toggle})
1973    target_compile_options(${target} ${scope} ${option})
1974  endif()
1975endfunction()
1976
1977function(target_cc_option_ifdef feature_toggle target scope option)
1978  if(${feature_toggle})
1979    target_cc_option(${target} ${scope} ${option})
1980  endif()
1981endfunction()
1982
1983function(zephyr_library_sources_ifdef feature_toggle source)
1984  if(${${feature_toggle}})
1985    zephyr_library_sources(${source} ${ARGN})
1986  endif()
1987endfunction()
1988
1989function(zephyr_sources_ifdef feature_toggle)
1990  if(${${feature_toggle}})
1991    zephyr_sources(${ARGN})
1992  endif()
1993endfunction()
1994
1995function(zephyr_cc_option_ifdef feature_toggle)
1996  if(${${feature_toggle}})
1997    zephyr_cc_option(${ARGN})
1998  endif()
1999endfunction()
2000
2001function(zephyr_ld_option_ifdef feature_toggle)
2002  if(${${feature_toggle}})
2003    zephyr_ld_options(${ARGN})
2004  endif()
2005endfunction()
2006
2007function(zephyr_link_libraries_ifdef feature_toggle)
2008  if(${${feature_toggle}})
2009    zephyr_link_libraries(${ARGN})
2010  endif()
2011endfunction()
2012
2013function(zephyr_compile_options_ifdef feature_toggle)
2014  if(${${feature_toggle}})
2015    zephyr_compile_options(${ARGN})
2016  endif()
2017endfunction()
2018
2019function(zephyr_compile_definitions_ifdef feature_toggle)
2020  if(${${feature_toggle}})
2021    zephyr_compile_definitions(${ARGN})
2022  endif()
2023endfunction()
2024
2025function(zephyr_include_directories_ifdef feature_toggle)
2026  if(${${feature_toggle}})
2027    zephyr_include_directories(${ARGN})
2028  endif()
2029endfunction()
2030
2031function(zephyr_library_compile_definitions_ifdef feature_toggle item)
2032  if(${${feature_toggle}})
2033    zephyr_library_compile_definitions(${item} ${ARGN})
2034  endif()
2035endfunction()
2036
2037function(zephyr_library_include_directories_ifdef feature_toggle)
2038  if(${${feature_toggle}})
2039    zephyr_library_include_directories(${ARGN})
2040  endif()
2041endfunction()
2042
2043function(zephyr_library_compile_options_ifdef feature_toggle item)
2044  if(${${feature_toggle}})
2045    zephyr_library_compile_options(${item} ${ARGN})
2046  endif()
2047endfunction()
2048
2049function(zephyr_link_interface_ifdef feature_toggle interface)
2050  if(${${feature_toggle}})
2051    target_link_libraries(${interface} INTERFACE zephyr_interface)
2052  endif()
2053endfunction()
2054
2055function(zephyr_library_link_libraries_ifdef feature_toggle item)
2056  if(${${feature_toggle}})
2057     zephyr_library_link_libraries(${item})
2058  endif()
2059endfunction()
2060
2061function(zephyr_linker_sources_ifdef feature_toggle)
2062  if(${${feature_toggle}})
2063    zephyr_linker_sources(${ARGN})
2064  endif()
2065endfunction()
2066
2067function(zephyr_library_add_dependencies_ifdef feature_toggle)
2068  if(${${feature_toggle}})
2069    zephyr_library_add_dependencies(${ARGN})
2070  endif()
2071endfunction()
2072
2073macro(list_append_ifdef feature_toggle list)
2074  if(${${feature_toggle}})
2075    list(APPEND ${list} ${ARGN})
2076  endif()
2077endmacro()
2078
2079# 3.2. *_ifndef
2080# See 3.1 *_ifdef
2081function(set_ifndef variable value)
2082  if(NOT ${variable})
2083    set(${variable} ${value} ${ARGN} PARENT_SCOPE)
2084  endif()
2085endfunction()
2086
2087function(add_subdirectory_ifndef feature_toggle source_dir)
2088  if(NOT ${feature_toggle})
2089    add_subdirectory(${source_dir} ${ARGN})
2090  endif()
2091endfunction()
2092
2093function(target_sources_ifndef feature_toggle target scope item)
2094  if(NOT ${feature_toggle})
2095    target_sources(${target} ${scope} ${item} ${ARGN})
2096  endif()
2097endfunction()
2098
2099function(target_compile_definitions_ifndef feature_toggle target scope item)
2100  if(NOT ${feature_toggle})
2101    target_compile_definitions(${target} ${scope} ${item} ${ARGN})
2102  endif()
2103endfunction()
2104
2105function(target_include_directories_ifndef feature_toggle target scope item)
2106  if(NOT ${feature_toggle})
2107    target_include_directories(${target} ${scope} ${item} ${ARGN})
2108  endif()
2109endfunction()
2110
2111function(target_link_libraries_ifndef feature_toggle target item)
2112  if(NOT ${feature_toggle})
2113    target_link_libraries(${target} ${item} ${ARGN})
2114  endif()
2115endfunction()
2116
2117function(add_compile_option_ifndef feature_toggle option)
2118  if(NOT ${feature_toggle})
2119    add_compile_options(${option})
2120  endif()
2121endfunction()
2122
2123function(target_compile_option_ifndef feature_toggle target scope option)
2124  if(NOT ${feature_toggle})
2125    target_compile_options(${target} ${scope} ${option})
2126  endif()
2127endfunction()
2128
2129function(target_cc_option_ifndef feature_toggle target scope option)
2130  if(NOT ${feature_toggle})
2131    target_cc_option(${target} ${scope} ${option})
2132  endif()
2133endfunction()
2134
2135function(zephyr_library_sources_ifndef feature_toggle source)
2136  if(NOT ${feature_toggle})
2137    zephyr_library_sources(${source} ${ARGN})
2138  endif()
2139endfunction()
2140
2141function(zephyr_sources_ifndef feature_toggle)
2142   if(NOT ${feature_toggle})
2143    zephyr_sources(${ARGN})
2144  endif()
2145endfunction()
2146
2147function(zephyr_cc_option_ifndef feature_toggle)
2148  if(NOT ${feature_toggle})
2149    zephyr_cc_option(${ARGN})
2150  endif()
2151endfunction()
2152
2153function(zephyr_ld_option_ifndef feature_toggle)
2154  if(NOT ${feature_toggle})
2155    zephyr_ld_options(${ARGN})
2156  endif()
2157endfunction()
2158
2159function(zephyr_link_libraries_ifndef feature_toggle)
2160  if(NOT ${feature_toggle})
2161    zephyr_link_libraries(${ARGN})
2162  endif()
2163endfunction()
2164
2165function(zephyr_compile_options_ifndef feature_toggle)
2166  if(NOT ${feature_toggle})
2167    zephyr_compile_options(${ARGN})
2168  endif()
2169endfunction()
2170
2171function(zephyr_compile_definitions_ifndef feature_toggle)
2172  if(NOT ${feature_toggle})
2173    zephyr_compile_definitions(${ARGN})
2174  endif()
2175endfunction()
2176
2177function(zephyr_include_directories_ifndef feature_toggle)
2178  if(NOT ${feature_toggle})
2179    zephyr_include_directories(${ARGN})
2180  endif()
2181endfunction()
2182
2183function(zephyr_library_compile_definitions_ifndef feature_toggle item)
2184  if(NOT ${feature_toggle})
2185    zephyr_library_compile_definitions(${item} ${ARGN})
2186  endif()
2187endfunction()
2188
2189function(zephyr_library_include_directories_ifndef feature_toggle)
2190  if(NOT ${feature_toggle})
2191    zephyr_library_include_directories(${ARGN})
2192  endif()
2193endfunction()
2194
2195function(zephyr_library_compile_options_ifndef feature_toggle item)
2196  if(NOT ${feature_toggle})
2197    zephyr_library_compile_options(${item} ${ARGN})
2198  endif()
2199endfunction()
2200
2201function(zephyr_link_interface_ifndef feature_toggle interface)
2202  if(NOT ${feature_toggle})
2203    target_link_libraries(${interface} INTERFACE zephyr_interface)
2204  endif()
2205endfunction()
2206
2207function(zephyr_library_link_libraries_ifndef feature_toggle item)
2208  if(NOT ${feature_toggle})
2209     zephyr_library_link_libraries(${item})
2210  endif()
2211endfunction()
2212
2213function(zephyr_linker_sources_ifndef feature_toggle)
2214  if(NOT ${feature_toggle})
2215    zephyr_linker_sources(${ARGN})
2216  endif()
2217endfunction()
2218
2219function(zephyr_library_add_dependencies_ifndef feature_toggle)
2220  if(NOT ${feature_toggle})
2221    zephyr_library_add_dependencies(${ARGN})
2222  endif()
2223endfunction()
2224
2225macro(list_append_ifndef feature_toggle list)
2226  if(NOT ${feature_toggle})
2227    list(APPEND ${list} ${ARGN})
2228  endif()
2229endmacro()
2230
2231# 3.3. *_option Compiler-compatibility checks
2232#
2233# Utility functions for silently omitting compiler flags when the
2234# compiler lacks support. *_cc_option was ported from KBuild, see
2235# cc-option in
2236# https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt
2237
2238# Writes 1 to the output variable 'ok' for the language 'lang' if
2239# the flag is supported, otherwise writes 0.
2240#
2241# lang must be C or CXX
2242#
2243# TODO: Support ASM
2244#
2245# Usage:
2246#
2247# check_compiler_flag(C "-Wall" my_check)
2248# print(my_check) # my_check is now 1
2249function(check_compiler_flag lang option ok)
2250  if(NOT DEFINED CMAKE_REQUIRED_QUIET)
2251    set(CMAKE_REQUIRED_QUIET 1)
2252  endif()
2253
2254  string(MAKE_C_IDENTIFIER
2255    "check${option}_${lang}_${CMAKE_REQUIRED_FLAGS}"
2256    ${ok}
2257    )
2258
2259  if(${lang} STREQUAL C)
2260    check_c_compiler_flag("${option}" ${${ok}})
2261  else()
2262    check_cxx_compiler_flag("${option}" ${${ok}})
2263  endif()
2264
2265  if(${${${ok}}})
2266    set(ret 1)
2267  else()
2268    set(ret 0)
2269  endif()
2270
2271  set(${ok} ${ret} PARENT_SCOPE)
2272endfunction()
2273
2274function(target_cc_option target scope option)
2275  target_cc_option_fallback(${target} ${scope} ${option} "")
2276endfunction()
2277
2278# Support an optional second option for when the first option is not
2279# supported.
2280function(target_cc_option_fallback target scope option1 option2)
2281  if(CONFIG_CPP)
2282    foreach(lang C CXX)
2283      # For now, we assume that all flags that apply to C/CXX also
2284      # apply to ASM.
2285      zephyr_check_compiler_flag(${lang} ${option1} check)
2286      if(${check})
2287        target_compile_options(${target} ${scope}
2288          $<$<COMPILE_LANGUAGE:${lang}>:${option1}>
2289          $<$<COMPILE_LANGUAGE:ASM>:${option1}>
2290          )
2291      elseif(option2)
2292        target_compile_options(${target} ${scope}
2293          $<$<COMPILE_LANGUAGE:${lang}>:${option2}>
2294          $<$<COMPILE_LANGUAGE:ASM>:${option2}>
2295          )
2296      endif()
2297    endforeach()
2298  else()
2299    zephyr_check_compiler_flag(C ${option1} check)
2300    if(${check})
2301      target_compile_options(${target} ${scope} ${option1})
2302    elseif(option2)
2303      target_compile_options(${target} ${scope} ${option2})
2304    endif()
2305  endif()
2306endfunction()
2307
2308function(target_ld_options target scope)
2309  zephyr_get_parse_args(args ${ARGN})
2310  list(REMOVE_ITEM ARGN NO_SPLIT)
2311
2312  foreach(option ${ARGN})
2313    if(args_NO_SPLIT)
2314      set(option ${ARGN})
2315    endif()
2316    string(JOIN "" check_identifier "check" ${option})
2317    string(MAKE_C_IDENTIFIER ${check_identifier} check)
2318
2319    set(SAVED_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
2320    string(JOIN " " CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} ${option})
2321    zephyr_check_compiler_flag(C "" ${check})
2322    set(CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS})
2323
2324    target_link_libraries_ifdef(${check} ${target} ${scope} ${option})
2325
2326    if(args_NO_SPLIT)
2327      break()
2328    endif()
2329  endforeach()
2330endfunction()
2331
2332# 3.3.1 Toolchain integration
2333#
2334# 'toolchain_parse_make_rule' is a function that parses the output of
2335# 'gcc -M'.
2336#
2337# The argument 'input_file' is in input parameter with the path to the
2338# file with the dependency information.
2339#
2340# The argument 'include_files' is an output parameter with the result
2341# of parsing the include files.
2342function(toolchain_parse_make_rule input_file include_files)
2343  file(STRINGS ${input_file} input)
2344
2345  # The file is formatted like this:
2346  # empty_file.o: misc/empty_file.c \
2347  # nrf52840dk_nrf52840/nrf52840dk_nrf52840.dts \
2348  # nrf52840_qiaa.dtsi
2349
2350  # The dep file will contain `\` for line continuation.
2351  # This results in `\;` which is then treated a the char `;` instead of
2352  # the element separator, so let's get the pure `;` back.
2353  string(REPLACE "\;" ";" input_as_list ${input})
2354
2355  # The file might also contain multiple files on one line if one or both of
2356  # the file paths are short, split these up into multiple elements using regex
2357  string(REGEX REPLACE "([^ ])[ ]([^ ])" "\\1;\\2" input_as_list "${input_as_list}")
2358
2359  # Pop the first item containing "empty_file.o:"
2360  list(POP_FRONT input_as_list first_input_line)
2361
2362  # Remove whitespace before and after filename and convert to CMake path.
2363  foreach(file ${input_as_list})
2364    string(STRIP "${file}" file)
2365    file(TO_CMAKE_PATH "${file}" file)
2366    list(APPEND result "${file}")
2367  endforeach()
2368
2369  set(${include_files} ${result} PARENT_SCOPE)
2370endfunction()
2371
2372# 'check_set_linker_property' is a function that check the provided linker
2373# flag and only set the linker property if the check succeeds
2374#
2375# This function is similar in nature to the CMake set_property function, but
2376# with the extension that it will check that the linker supports the flag before
2377# setting the property.
2378#
2379# APPEND: Flag indicated that the property should be appended to the existing
2380#         value list for the property.
2381# TARGET: Name of target on which to add the property (commonly: linker)
2382# PROPERTY: Name of property with the value(s) following immediately after
2383#           property name
2384function(check_set_linker_property)
2385  set(options APPEND)
2386  set(single_args TARGET)
2387  set(multi_args  PROPERTY)
2388  cmake_parse_arguments(LINKER_PROPERTY "${options}" "${single_args}" "${multi_args}" ${ARGN})
2389
2390  if(LINKER_PROPERTY_APPEND)
2391   set(APPEND "APPEND")
2392  endif()
2393
2394  list(GET LINKER_PROPERTY_PROPERTY 0 property)
2395  list(REMOVE_AT LINKER_PROPERTY_PROPERTY 0)
2396  set(option ${LINKER_PROPERTY_PROPERTY})
2397
2398  string(MAKE_C_IDENTIFIER check${option} check)
2399
2400  set(SAVED_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
2401  set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${option}")
2402  zephyr_check_compiler_flag(C "" ${check})
2403  set(CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS})
2404
2405  if(${${check}})
2406    set_property(TARGET ${LINKER_PROPERTY_TARGET} ${APPEND} PROPERTY ${property} ${option})
2407  endif()
2408endfunction()
2409
2410# 'set_compiler_property' is a function that sets the property for the C and
2411# C++ property targets used for toolchain abstraction.
2412#
2413# This function is similar in nature to the CMake set_property function, but
2414# with the extension that it will set the property on both the compile and
2415# compiler-cpp targets.
2416#
2417# APPEND: Flag indicated that the property should be appended to the existing
2418#         value list for the property.
2419# PROPERTY: Name of property with the value(s) following immediately after
2420#           property name
2421function(set_compiler_property)
2422  set(options APPEND)
2423  set(multi_args  PROPERTY)
2424  cmake_parse_arguments(COMPILER_PROPERTY "${options}" "${single_args}" "${multi_args}" ${ARGN})
2425  if(COMPILER_PROPERTY_APPEND)
2426   set(APPEND "APPEND")
2427   set(APPEND-CPP "APPEND")
2428  endif()
2429
2430  set_property(TARGET compiler ${APPEND} PROPERTY ${COMPILER_PROPERTY_PROPERTY})
2431  set_property(TARGET compiler-cpp ${APPEND} PROPERTY ${COMPILER_PROPERTY_PROPERTY})
2432endfunction()
2433
2434# 'check_set_compiler_property' is a function that check the provided compiler
2435# flag and only set the compiler or compiler-cpp property if the check succeeds
2436#
2437# This function is similar in nature to the CMake set_property function, but
2438# with the extension that it will check that the compiler supports the flag
2439# before setting the property on compiler or compiler-cpp targets.
2440#
2441# To test flags together, such as '-Wformat -Wformat-security', an option group
2442# can be specified by using shell-like quoting along with a 'SHELL:' prefix.
2443# The 'SHELL:' prefix will be dropped before testing, so that
2444# '"SHELL:-Wformat -Wformat-security"' becomes '-Wformat -Wformat-security' for
2445# testing.
2446#
2447# APPEND: Flag indicated that the property should be appended to the existing
2448#         value list for the property.
2449# PROPERTY: Name of property with the value(s) following immediately after
2450#           property name
2451function(check_set_compiler_property)
2452  set(options APPEND)
2453  set(multi_args  PROPERTY)
2454  cmake_parse_arguments(COMPILER_PROPERTY "${options}" "${single_args}" "${multi_args}" ${ARGN})
2455  if(COMPILER_PROPERTY_APPEND)
2456   set(APPEND "APPEND")
2457   set(APPEND-CPP "APPEND")
2458  endif()
2459
2460  list(GET COMPILER_PROPERTY_PROPERTY 0 property)
2461  list(REMOVE_AT COMPILER_PROPERTY_PROPERTY 0)
2462
2463  foreach(option ${COMPILER_PROPERTY_PROPERTY})
2464    if(${option} MATCHES "^SHELL:")
2465      string(REGEX REPLACE "^SHELL:" "" option ${option})
2466      separate_arguments(option UNIX_COMMAND ${option})
2467    endif()
2468
2469    if(CONFIG_CPP)
2470      zephyr_check_compiler_flag(CXX "${option}" check)
2471
2472      if(${check})
2473        set_property(TARGET compiler-cpp ${APPEND-CPP} PROPERTY ${property} ${option})
2474        set(APPEND-CPP "APPEND")
2475      endif()
2476    endif()
2477
2478    zephyr_check_compiler_flag(C "${option}" check)
2479
2480    if(${check})
2481      set_property(TARGET compiler ${APPEND} PROPERTY ${property} ${option})
2482      set(APPEND "APPEND")
2483    endif()
2484  endforeach()
2485endfunction()
2486
2487
2488# 3.4. Debugging CMake
2489
2490# Usage:
2491#   print(BOARD)
2492#
2493# will print: "BOARD: nrf52dk"
2494function(print arg)
2495  message(STATUS "${arg}: ${${arg}}")
2496endfunction()
2497
2498# Usage:
2499#   assert(ZEPHYR_TOOLCHAIN_VARIANT "ZEPHYR_TOOLCHAIN_VARIANT not set.")
2500#
2501# will cause a FATAL_ERROR and print an error message if the first
2502# expression is false
2503macro(assert test comment)
2504  if(NOT ${test})
2505    message(FATAL_ERROR "Assertion failed: ${comment}")
2506  endif()
2507endmacro()
2508
2509# Usage:
2510#   assert_not(OBSOLETE_VAR "OBSOLETE_VAR has been removed; use NEW_VAR instead")
2511#
2512# will cause a FATAL_ERROR and print an error message if the first
2513# expression is true
2514macro(assert_not test comment)
2515  if(${test})
2516    message(FATAL_ERROR "Assertion failed: ${comment}")
2517  endif()
2518endmacro()
2519
2520# Usage:
2521#   assert_exists(CMAKE_READELF)
2522#
2523# will cause a FATAL_ERROR if there is no file or directory behind the
2524# variable
2525macro(assert_exists var)
2526  if(NOT EXISTS ${${var}})
2527    message(FATAL_ERROR "No such file or directory: ${var}: '${${var}}'")
2528  endif()
2529endmacro()
2530
2531# 3.5. File system management
2532function(generate_unique_target_name_from_filename filename target_name)
2533  get_filename_component(basename ${filename} NAME)
2534  string(REPLACE "." "_" x ${basename})
2535  string(REPLACE "@" "_" x ${x})
2536
2537  string(MD5 unique_chars ${filename})
2538
2539  set(${target_name} gen_${x}_${unique_chars} PARENT_SCOPE)
2540endfunction()
2541
2542# Usage:
2543#   zephyr_file(<mode> <arg> ...)
2544#
2545# Zephyr file function extension.
2546# This function currently supports the following <modes>
2547#
2548# APPLICATION_ROOT <path>: Check all paths in provided variable, and convert
2549#                          those paths that are defined with `-D<path>=<val>`
2550#                          to absolute path, relative from `APPLICATION_SOURCE_DIR`
2551#                          Issue an error for any relative path not specified
2552#                          by user with `-D<path>`
2553#                          BASE_DIR <base-dir>: convert paths relative to <base-dir>
2554#                                               instead of `APPLICATION_SOURCE_DIR`
2555#
2556# returns an updated list of absolute paths
2557#
2558# Usage:
2559#   zephyr_file(CONF_FILES <paths> [DTS <list>] [KCONF <list>]
2560#               [BOARD <board> [BOARD_REVISION <revision>] | NAMES <name> ...]
2561#               [BUILD <type>] [SUFFIX <suffix>] [REQUIRED]
2562#   )
2563#
2564# CONF_FILES <paths>: Find all configuration files in the list of paths and
2565#                     return them in a list. If paths is empty then no configuration
2566#                     files are returned. Configuration files will be:
2567#                     - DTS:       Overlay files (.overlay)
2568#                     - Kconfig:   Config fragments (.conf)
2569#                     - defconfig: defconfig files (_defconfig)
2570#                     The conf file search will return existing configuration
2571#                     files for the current board.
2572#                     CONF_FILES takes the following additional arguments:
2573#                     BOARD <board>:             Find configuration files for specified board.
2574#                     BOARD_REVISION <revision>: Find configuration files for specified board
2575#                                                revision. Requires BOARD to be specified.
2576#
2577#                                                If no board is given the current BOARD and
2578#                                                BOARD_REVISION will be used, unless NAMES are
2579#                                                specified.
2580#
2581#                     NAMES <name1> [name2] ...  List of file names to look for and instead of
2582#                                                creating file names based on board settings.
2583#                                                Only the first match found in <paths> will be
2584#                                                returned in the <list>
2585#                     DTS <list>:    List to append DTS overlay files in <path> to
2586#                     KCONF <list>:  List to append Kconfig fragment files in <path> to
2587#                     DEFCONF <list>: List to append _defconfig files in <path> to
2588#                     BUILD <type>:  Build type to include for search.
2589#                                    For example:
2590#                                    BUILD debug, will look for <board>_debug.conf
2591#                                    and <board>_debug.overlay, instead of <board>.conf
2592#                     SUFFIX <name>: Suffix name to check for instead of the default name
2593#                                    but with a fallback to the default name if not found.
2594#                                    For example:
2595#                                    SUFFIX fish, will look for <file>_fish.conf and use
2596#                                    if found but will use <file>.conf if not found
2597#                     REQUIRED:      Option to indicate that the <list> specified by DTS or KCONF
2598#                                    must contain at least one element, else an error will be raised.
2599#
2600function(zephyr_file)
2601  set(file_options APPLICATION_ROOT CONF_FILES)
2602  if((ARGC EQUAL 0) OR (NOT (ARGV0 IN_LIST file_options)))
2603    message(FATAL_ERROR "No <mode> given to `zephyr_file(<mode> <args>...)` function,\n \
2604Please provide one of following: APPLICATION_ROOT, CONF_FILES")
2605  endif()
2606
2607  if(${ARGV0} STREQUAL APPLICATION_ROOT)
2608    set(single_args APPLICATION_ROOT BASE_DIR)
2609  elseif(${ARGV0} STREQUAL CONF_FILES)
2610    set(options QUALIFIERS REQUIRED)
2611    set(single_args BOARD BOARD_REVISION BOARD_QUALIFIERS DTS KCONF DEFCONFIG BUILD SUFFIX)
2612    set(multi_args CONF_FILES NAMES)
2613  endif()
2614
2615  cmake_parse_arguments(ZFILE "${options}" "${single_args}" "${multi_args}" ${ARGN})
2616  if(ZFILE_UNPARSED_ARGUMENTS)
2617      message(FATAL_ERROR "zephyr_file(${ARGV0} <val> ...) given unknown arguments: ${ZFILE_UNPARSED_ARGUMENTS}")
2618  endif()
2619
2620  if(ZFILE_APPLICATION_ROOT)
2621    if(NOT DEFINED ZFILE_BASE_DIR)
2622      set(ZFILE_BASE_DIR ${APPLICATION_SOURCE_DIR})
2623    endif()
2624
2625    # Note: user can do: `-D<var>=<relative-path>` and app can at same
2626    # time specify `list(APPEND <var> <abs-path>)`
2627    # Thus need to check and update only CACHED variables (-D<var>).
2628    set(CACHED_PATH $CACHE{${ZFILE_APPLICATION_ROOT}})
2629    foreach(path ${CACHED_PATH})
2630      # The cached variable is relative path, i.e. provided by `-D<var>` or
2631      # `set(<var> CACHE)`, so let's update current scope variable to absolute
2632      # path from  `APPLICATION_SOURCE_DIR`.
2633      if(NOT IS_ABSOLUTE ${path})
2634        list(FIND ${ZFILE_APPLICATION_ROOT} ${path} index)
2635        cmake_path(ABSOLUTE_PATH path BASE_DIRECTORY ${ZFILE_BASE_DIR} NORMALIZE)
2636        if(NOT ${index} LESS 0)
2637          list(REMOVE_AT ${ZFILE_APPLICATION_ROOT} ${index})
2638          list(INSERT ${ZFILE_APPLICATION_ROOT} ${index} ${path})
2639        endif()
2640      endif()
2641    endforeach()
2642
2643    # Now all cached relative paths has been updated.
2644    # Let's check if anyone uses relative path as scoped variable, and fail
2645    foreach(path ${${ZFILE_APPLICATION_ROOT}})
2646      if(NOT IS_ABSOLUTE ${path})
2647        message(FATAL_ERROR
2648"Relative path encountered in scoped variable: ${ZFILE_APPLICATION_ROOT}, value=${path}\n \
2649Please adjust any `set(${ZFILE_APPLICATION_ROOT} ${path})` or `list(APPEND ${ZFILE_APPLICATION_ROOT} ${path})`\n \
2650to absolute path using `\${CMAKE_CURRENT_SOURCE_DIR}/${path}` or similar. \n \
2651Relative paths are only allowed with `-D${ARGV1}=<path>`")
2652      endif()
2653    endforeach()
2654
2655    list(REMOVE_DUPLICATES ${ZFILE_APPLICATION_ROOT})
2656    # This updates the provided argument in parent scope (callers scope)
2657    set(${ZFILE_APPLICATION_ROOT} ${${ZFILE_APPLICATION_ROOT}} PARENT_SCOPE)
2658  endif()
2659
2660  if(ZFILE_CONF_FILES)
2661    if(DEFINED ZFILE_BOARD_REVISION AND NOT ZFILE_BOARD)
2662        message(FATAL_ERROR
2663          "zephyr_file(${ARGV0} <path> BOARD_REVISION ${ZFILE_BOARD_REVISION} ...)"
2664          " given without BOARD argument, please specify BOARD"
2665        )
2666    endif()
2667
2668    if(NOT DEFINED ZFILE_BOARD)
2669      # Defaulting to system wide settings when BOARD is not given as argument
2670      set(ZFILE_BOARD ${BOARD})
2671      if(DEFINED BOARD_REVISION)
2672        set(ZFILE_BOARD_REVISION ${BOARD_REVISION})
2673      endif()
2674
2675      if(DEFINED BOARD_QUALIFIERS)
2676        set(ZFILE_BOARD_QUALIFIERS ${BOARD_QUALIFIERS})
2677      endif()
2678    endif()
2679
2680    if(ZFILE_NAMES)
2681      set(dts_filename_list ${ZFILE_NAMES})
2682      set(kconf_filename_list ${ZFILE_NAMES})
2683    else()
2684      if(NOT ZFILE_QUALIFIERS)
2685        zephyr_build_string(filename_list
2686                            SHORT shortened_filename_list
2687                            BOARD ${ZFILE_BOARD}
2688                            BOARD_REVISION ${ZFILE_BOARD_REVISION}
2689                            BOARD_QUALIFIERS ${ZFILE_BOARD_QUALIFIERS}
2690                            BUILD ${ZFILE_BUILD}
2691                            MERGE REVERSE
2692        )
2693      else()
2694        zephyr_build_string(filename_list
2695                            BOARD_QUALIFIERS ${ZFILE_BOARD_QUALIFIERS}
2696                            BUILD ${ZFILE_BUILD}
2697                            MERGE REVERSE
2698        )
2699      endif()
2700
2701      set(dts_filename_list ${filename_list})
2702      set(dts_shortened_filename_list ${shortened_filename_list})
2703      list(TRANSFORM dts_filename_list APPEND ".overlay")
2704      list(TRANSFORM dts_shortened_filename_list APPEND ".overlay")
2705
2706      set(kconf_filename_list ${filename_list})
2707      set(kconf_shortened_filename_list ${shortened_filename_list})
2708      list(TRANSFORM kconf_filename_list APPEND ".conf")
2709      list(TRANSFORM kconf_shortened_filename_list APPEND ".conf")
2710    endif()
2711
2712    if(ZFILE_DTS)
2713      foreach(path ${ZFILE_CONF_FILES})
2714        foreach(filename IN ZIP_LISTS dts_filename_list dts_shortened_filename_list)
2715          foreach(i RANGE 1)
2716            if(NOT IS_ABSOLUTE filename_${i} AND DEFINED filename_${i})
2717              set(test_file_${i} ${path}/${filename_${i}})
2718            else()
2719              set(test_file_${i} ${filename_${i}})
2720            endif()
2721            zephyr_file_suffix(test_file_${i} SUFFIX ${ZFILE_SUFFIX})
2722
2723            if(NOT EXISTS ${test_file_${i}})
2724              set(test_file_${i})
2725            endif()
2726          endforeach()
2727
2728          if(test_file_0 OR test_file_1)
2729            list(APPEND found_dts_files ${test_file_0})
2730            list(APPEND found_dts_files ${test_file_1})
2731
2732            if(DEFINED ZFILE_BUILD)
2733              set(deprecated_file_found y)
2734            endif()
2735
2736            if(ZFILE_NAMES)
2737              break()
2738            endif()
2739          endif()
2740
2741          if(test_file_1 AND NOT BOARD_${ZFILE_BOARD}_SINGLE_SOC)
2742            message(FATAL_ERROR "Board ${ZFILE_BOARD} defines multiple SoCs.\nShortened file name "
2743                    "(${filename_1}) not allowed, use '<board>_<soc>.overlay' naming"
2744            )
2745          endif()
2746
2747          if(test_file_0 AND test_file_1)
2748            message(FATAL_ERROR "Conflicting file names discovered. Cannot use both ${filename_0} "
2749                    "and ${filename_1}. Please choose one naming style, "
2750                    "${filename_0} is recommended."
2751            )
2752          endif()
2753        endforeach()
2754      endforeach()
2755
2756      list(APPEND ${ZFILE_DTS} ${found_dts_files})
2757
2758      # This updates the provided list in parent scope (callers scope)
2759      set(${ZFILE_DTS} ${${ZFILE_DTS}} PARENT_SCOPE)
2760    endif()
2761
2762    if(ZFILE_KCONF)
2763      set(found_conf_files)
2764      foreach(path ${ZFILE_CONF_FILES})
2765        foreach(filename IN ZIP_LISTS kconf_filename_list kconf_shortened_filename_list)
2766          foreach(i RANGE 1)
2767            if(NOT IS_ABSOLUTE filename_${i} AND DEFINED filename_${i})
2768              set(test_file_${i} ${path}/${filename_${i}})
2769            else()
2770              set(test_file_${i} ${filename_${i}})
2771            endif()
2772            zephyr_file_suffix(test_file_${i} SUFFIX ${ZFILE_SUFFIX})
2773
2774            if(NOT EXISTS ${test_file_${i}})
2775              set(test_file_${i})
2776            endif()
2777          endforeach()
2778
2779          if(test_file_0 OR test_file_1)
2780            list(APPEND found_conf_files ${test_file_0})
2781            list(APPEND found_conf_files ${test_file_1})
2782
2783            if(DEFINED ZFILE_BUILD)
2784              set(deprecated_file_found y)
2785            endif()
2786
2787            if(ZFILE_NAMES)
2788              break()
2789            endif()
2790          endif()
2791
2792          if(test_file_1 AND NOT BOARD_${ZFILE_BOARD}_SINGLE_SOC)
2793            message(FATAL_ERROR "Board ${ZFILE_BOARD} defines multiple SoCs.\nShortened file name "
2794                    "(${filename_1}) not allowed, use '<board>_<soc>.conf' naming"
2795            )
2796          endif()
2797
2798          if(test_file_0 AND test_file_1)
2799            message(FATAL_ERROR "Conflicting file names discovered. Cannot use both ${filename_0} "
2800                    "and ${filename_1}. Please choose one naming style, "
2801                    "${filename_0} is recommended."
2802            )
2803          endif()
2804        endforeach()
2805      endforeach()
2806
2807      list(APPEND ${ZFILE_KCONF} ${found_conf_files})
2808
2809      # This updates the provided list in parent scope (callers scope)
2810      set(${ZFILE_KCONF} ${${ZFILE_KCONF}} PARENT_SCOPE)
2811
2812      if(NOT ${ZFILE_KCONF})
2813        set(not_found ${kconf_filename_list})
2814      endif()
2815    endif()
2816
2817    if(ZFILE_REQUIRED AND DEFINED not_found)
2818      message(FATAL_ERROR
2819              "No ${not_found} file(s) was found in the ${ZFILE_CONF_FILES} folder(s), "
2820              "please read the Zephyr documentation on application development."
2821      )
2822    endif()
2823
2824    if(deprecated_file_found)
2825      message(DEPRECATION "prj_<build>.conf was deprecated after Zephyr 3.5,"
2826                          " you should switch to using -DFILE_SUFFIX instead")
2827    endif()
2828
2829    if(ZFILE_DEFCONFIG)
2830      set(found_defconf_files)
2831      foreach(path ${ZFILE_CONF_FILES})
2832        foreach(filename IN ZIP_LISTS filename_list shortened_filename_list)
2833          foreach(i RANGE 1)
2834            set(test_file_${i} ${path}/${filename_${i}}_defconfig)
2835
2836            if(EXISTS ${test_file_${i}})
2837              list(APPEND found_defconf_files ${test_file_${i}})
2838            else()
2839              set(test_file_${i})
2840            endif()
2841          endforeach()
2842
2843          if(test_file_1 AND NOT BOARD_${ZFILE_BOARD}_SINGLE_SOC)
2844            message(FATAL_ERROR "Board ${ZFILE_BOARD} defines multiple SoCs.\nShortened file name "
2845                    "(${filename_1}_defconfig) not allowed, use '<board>_<soc>_defconfig' naming"
2846            )
2847          endif()
2848
2849          if(test_file_0 AND test_file_1)
2850            message(FATAL_ERROR "Conflicting file names discovered. Cannot use both "
2851                    "${filename_0}_defconfig and ${filename_1}_defconfig. Please choose one "
2852                    "naming style, ${filename_0}_defconfig is recommended."
2853            )
2854          endif()
2855        endforeach()
2856      endforeach()
2857      list(APPEND ${ZFILE_DEFCONFIG} ${found_defconf_files})
2858
2859      # This updates the provided list in parent scope (callers scope)
2860      set(${ZFILE_DEFCONFIG} ${${ZFILE_DEFCONFIG}} PARENT_SCOPE)
2861    endif()
2862  endif()
2863endfunction()
2864
2865# Usage:
2866#   zephyr_file_copy(<oldname> <newname> [ONLY_IF_DIFFERENT])
2867#
2868# Zephyr file copy extension.
2869# This function is similar to CMake function
2870# 'file(COPY_FILE <oldname> <newname> [ONLY_IF_DIFFERENT])'
2871# introduced with CMake 3.21.
2872#
2873# Because the minimal required CMake version with Zephyr is 3.20, this function
2874# is not guaranteed to be available.
2875#
2876# When using CMake version 3.21 or newer 'zephyr_file_copy()' simply calls
2877# 'file(COPY_FILE...)' directly.
2878# When using CMake version 3.20, the implementation will execute using CMake
2879# for running command line tool in a subprocess for identical functionality.
2880function(zephyr_file_copy oldname newname)
2881  set(options ONLY_IF_DIFFERENT)
2882  cmake_parse_arguments(ZEPHYR_FILE_COPY "${options}" "" "" ${ARGN})
2883
2884  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21.0)
2885    if(ZEPHYR_FILE_COPY_ONLY_IF_DIFFERENT)
2886      set(copy_file_options ONLY_IF_DIFFERENT)
2887    endif()
2888    file(COPY_FILE ${oldname} ${newname} ${copy_file_options})
2889  else()
2890    if(ZEPHYR_FILE_COPY_ONLY_IF_DIFFERENT)
2891      set(copy_file_command copy_if_different)
2892    else()
2893      set(copy_file_command copy)
2894    endif()
2895    execute_process(
2896      COMMAND ${CMAKE_COMMAND} -E ${copy_file_command} ${oldname} ${newname}
2897    )
2898  endif()
2899endfunction()
2900
2901# Usage:
2902#   zephyr_file_suffix(<filename> SUFFIX <suffix>)
2903#
2904# Zephyr file add suffix extension.
2905# This function will check the provied filename or list of filenames to see if they have a
2906# `_<suffix>` extension to them and if so, updates the supplied variable/list with the new
2907# path/paths.
2908#
2909# <filename>: Variable (singlular or list) of absolute path filename(s) which should be checked
2910#             and updated if there is a filename which has the <suffix> present.
2911# <suffix>: The suffix to test for and append to the end of the provided filename.
2912#
2913# Returns an updated variable of absolute path(s)
2914#
2915function(zephyr_file_suffix filename)
2916  set(single_args SUFFIX)
2917  cmake_parse_arguments(SFILE "" "${single_args}" "" ${ARGN})
2918
2919  if(NOT DEFINED SFILE_SUFFIX OR NOT DEFINED ${filename})
2920    # If the file suffix variable is not known then there is nothing to do, return early
2921    return()
2922  endif()
2923
2924  set(tmp_new_list)
2925
2926  foreach(file ${${filename}})
2927    if("${file}" STREQUAL "")
2928      # Skip checking empty variables
2929      continue()
2930    endif()
2931
2932    # Search for the full stop so we know where to add the file suffix before the file extension
2933    cmake_path(GET file EXTENSION file_ext)
2934    cmake_path(REMOVE_EXTENSION file OUTPUT_VARIABLE new_filename)
2935    cmake_path(APPEND_STRING new_filename "_${SFILE_SUFFIX}${file_ext}")
2936
2937    # Use the filename with the suffix if it exists, if not then fall back to the default
2938    if(EXISTS "${new_filename}")
2939      list(APPEND tmp_new_list ${new_filename})
2940    else()
2941      list(APPEND tmp_new_list ${file})
2942    endif()
2943  endforeach()
2944
2945  # Update supplied variable if it differs
2946  if(NOT "${${filename}}" STREQUAL "${tmp_new_list}")
2947    set(${filename} "${tmp_new_list}" PARENT_SCOPE)
2948  endif()
2949endfunction()
2950
2951# Usage:
2952#   zephyr_string(<mode> <out-var> <input> ...)
2953#
2954# Zephyr string function extension.
2955# This function extends the CMake string function by providing additional
2956# manipulation arguments to CMake string.
2957#
2958# SANITIZE: Ensure that the output string does not contain any special
2959#           characters. Special characters, such as -, +, =, $, etc. are
2960#           converted to underscores '_'.
2961#
2962# SANITIZE TOUPPER: Ensure that the output string does not contain any special
2963#                   characters. Special characters, such as -, +, =, $, etc. are
2964#                   converted to underscores '_'.
2965#                   The sanitized string will be returned in UPPER case.
2966#
2967# returns the updated string
2968function(zephyr_string)
2969  set(options SANITIZE TOUPPER)
2970  cmake_parse_arguments(ZEPHYR_STRING "${options}" "" "" ${ARGN})
2971
2972  if (NOT ZEPHYR_STRING_UNPARSED_ARGUMENTS)
2973    message(FATAL_ERROR "Function zephyr_string() called without a return variable")
2974  endif()
2975
2976  list(GET ZEPHYR_STRING_UNPARSED_ARGUMENTS 0 return_arg)
2977  list(REMOVE_AT ZEPHYR_STRING_UNPARSED_ARGUMENTS 0)
2978
2979  list(JOIN ZEPHYR_STRING_UNPARSED_ARGUMENTS "" work_string)
2980
2981  if(ZEPHYR_STRING_SANITIZE)
2982    string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" work_string ${work_string})
2983  endif()
2984
2985  if(ZEPHYR_STRING_TOUPPER)
2986    string(TOUPPER ${work_string} work_string)
2987  endif()
2988
2989  set(${return_arg} ${work_string} PARENT_SCOPE)
2990endfunction()
2991
2992# Usage:
2993#    zephyr_list(TRANSFORM <list> <ACTION>
2994#                [OUTPUT_VARIABLE <output variable])
2995#
2996# Example:
2997#
2998#    zephyr_list(TRANSFORM my_input_var NORMALIZE_PATHS
2999#                OUTPUT_VARIABLE my_input_as_list)
3000#
3001# Like CMake's list(TRANSFORM ...). This is intended as a placeholder
3002# for storing current and future Zephyr-related extensions for list
3003# processing.
3004#
3005# <ACTION>: This currently must be NORMALIZE_PATHS. This action
3006#           converts the argument list <list> to a ;-list with
3007#           CMake path names, after passing its contents through
3008#           a configure_file() transformation. The input list
3009#           may be whitespace- or semicolon-separated.
3010#
3011# OUTPUT_VARIABLE: the result is normally stored in place, but
3012#                  an alternative variable to store the result
3013#                  can be provided with this.
3014function(zephyr_list transform list_var action)
3015  # Parse arguments.
3016  if(NOT "${transform}" STREQUAL "TRANSFORM")
3017    message(FATAL_ERROR "the first argument must be TRANSFORM")
3018  endif()
3019  if(NOT "${action}" STREQUAL "NORMALIZE_PATHS")
3020    message(FATAL_ERROR "the third argument must be NORMALIZE_PATHS")
3021  endif()
3022  set(single_args OUTPUT_VARIABLE)
3023  cmake_parse_arguments(ZEPHYR_LIST "" "${single_args}" "" ${ARGN})
3024  if(DEFINED ZEPHYR_LIST_OUTPUT_VARIABLE)
3025    set(out_var ${ZEPHYR_LIST_OUTPUT_VARIABLE})
3026  else()
3027    set(out_var ${list_var})
3028  endif()
3029  set(input ${${list_var}})
3030
3031  # Perform the transformation.
3032  set(ret)
3033  string(CONFIGURE "${input}" input_expanded)
3034  string(REPLACE " " ";" input_raw_list "${input_expanded}")
3035  foreach(file ${input_raw_list})
3036    file(TO_CMAKE_PATH "${file}" cmake_path_file)
3037    list(APPEND ret ${cmake_path_file})
3038  endforeach()
3039
3040  set(${out_var} ${ret} PARENT_SCOPE)
3041endfunction()
3042
3043# Usage:
3044#   zephyr_var_name(<variable> <scope> <out>)
3045#
3046# Internal function for construction of scoped variable name expansion string.
3047# Examples:
3048#   reading a current scope FOO variable is identical to expand ${FOO}.
3049#   reading a cache scope FOO variable is identical to expand $CACHE{FOO}.
3050#
3051# this functions will return the var name in out var for the scope if it is
3052# defined, else it will set the outvar to undefined.
3053function(zephyr_var_name variable scope out)
3054  if(scope STREQUAL "ENV" OR scope STREQUAL "CACHE")
3055    if(DEFINED ${scope}{${variable}})
3056      set(${out} "$${scope}{${variable}}" PARENT_SCOPE)
3057    else()
3058      set(${out} PARENT_SCOPE)
3059    endif()
3060  else()
3061    if(DEFINED ${scope}_${variable})
3062      set(${out} "${${scope}_${variable}}" PARENT_SCOPE)
3063    else()
3064      set(${out} PARENT_SCOPE)
3065    endif()
3066  endif()
3067endfunction()
3068
3069# Usage:
3070#   zephyr_get(<variable> [MERGE [REVERSE]] [SYSBUILD [LOCAL|GLOBAL]] [VAR <var1> ...])
3071#
3072# Return the value of <variable> as local scoped variable of same name. If MERGE
3073# is supplied, will return a list of found items. If REVERSE is supplied
3074# together with MERGE, the order of the list will be reversed before being
3075# returned. Reverse will happen before the list is returned and hence it will
3076# not change the order of precedence in which the list itself is constructed.
3077#
3078# VAR can be used either to store the result in a variable with a different
3079# name, or to look for values from multiple variables.
3080# zephyr_get(FOO VAR FOO_A FOO_B)
3081# zephyr_get(FOO MERGE VAR FOO_A FOO_B)
3082#
3083# zephyr_get() is a common function to provide a uniform way of supporting
3084# build settings that can be set from sysbuild, CMakeLists.txt, CMake cache, or
3085# in environment.
3086#
3087# The order of precedence for variables defined in multiple scopes:
3088# - Sysbuild defined when sysbuild is used.
3089#   Sysbuild variables can be defined as global or local to specific image.
3090#   Examples:
3091#   - BOARD is considered a global sysbuild cache variable
3092#   - blinky_BOARD is considered a local sysbuild cache variable only for the
3093#     blinky image.
3094#   If no sysbuild scope is specified, GLOBAL is assumed.
3095#   If using MERGE then SYSBUILD GLOBAL will get both the local and global
3096#   sysbuild scope variables (in that order, if both exist).
3097# - CMake cache, set by `-D<var>=<value>` or `set(<var> <val> CACHE ...)
3098# - Environment
3099# - Locally in CMakeLists.txt before 'find_package(Zephyr)'
3100#
3101# For example, if ZEPHYR_TOOLCHAIN_VARIANT is set in environment but locally
3102# overridden by setting ZEPHYR_TOOLCHAIN_VARIANT directly in the CMake cache
3103# using `-DZEPHYR_TOOLCHAIN_VARIANT=<val>`, then the value from the cache is
3104# returned.
3105function(zephyr_get variable)
3106  cmake_parse_arguments(GET_VAR "MERGE;REVERSE" "SYSBUILD" "VAR" ${ARGN})
3107
3108  if(DEFINED GET_VAR_SYSBUILD)
3109    if(NOT ("${GET_VAR_SYSBUILD}" STREQUAL "GLOBAL" OR
3110            "${GET_VAR_SYSBUILD}" STREQUAL "LOCAL")
3111    )
3112      message(FATAL_ERROR "zephyr_get(... SYSBUILD) requires GLOBAL or LOCAL.")
3113    endif()
3114  else()
3115    set(GET_VAR_SYSBUILD "GLOBAL")
3116  endif()
3117
3118  if(GET_VAR_REVERSE AND NOT GET_VAR_MERGE)
3119    message(FATAL_ERROR "zephyr_get(... REVERSE) missing a required argument: MERGE")
3120  endif()
3121
3122  if(NOT DEFINED GET_VAR_VAR)
3123    set(GET_VAR_VAR ${variable})
3124  endif()
3125
3126  # Keep current scope variables in internal variables.
3127  # This is needed to properly handle cases where we want to check value against
3128  # environment value or when appending with the MERGE operation.
3129  foreach(var ${GET_VAR_VAR})
3130    set(current_${var} ${${var}})
3131    set(${var})
3132
3133    if(SYSBUILD)
3134      get_property(sysbuild_name TARGET sysbuild_cache PROPERTY SYSBUILD_NAME)
3135      get_property(sysbuild_main_app TARGET sysbuild_cache PROPERTY SYSBUILD_MAIN_APP)
3136      get_property(sysbuild_local_${var} TARGET sysbuild_cache PROPERTY ${sysbuild_name}_${var})
3137      get_property(sysbuild_global_${var} TARGET sysbuild_cache PROPERTY ${var})
3138      if(NOT DEFINED sysbuild_local_${var} AND sysbuild_main_app)
3139        set(sysbuild_local_${var} ${sysbuild_global_${var}})
3140      endif()
3141      if(NOT "${GET_VAR_SYSBUILD}" STREQUAL "GLOBAL")
3142        set(sysbuild_global_${var})
3143      endif()
3144    else()
3145      set(sysbuild_local_${var})
3146      set(sysbuild_global_${var})
3147    endif()
3148
3149    if(TARGET snippets_scope)
3150      get_property(snippets_${var} TARGET snippets_scope PROPERTY ${var})
3151    endif()
3152  endforeach()
3153
3154  set(${variable} "")
3155  set(scopes "sysbuild_local;sysbuild_global;CACHE;snippets;ENV;current")
3156  if(GET_VAR_REVERSE)
3157    list(REVERSE scopes)
3158  endif()
3159  foreach(scope IN LISTS scopes)
3160    foreach(var ${GET_VAR_VAR})
3161      zephyr_var_name("${var}" "${scope}" expansion_var)
3162      if(DEFINED expansion_var)
3163        string(CONFIGURE "${expansion_var}" scope_value)
3164        if(GET_VAR_MERGE)
3165          list(APPEND ${variable} ${scope_value})
3166        else()
3167          set(${variable} ${scope_value} PARENT_SCOPE)
3168
3169          if("${scope}" STREQUAL "ENV")
3170            # Set the environment variable in CMake cache, so that a build
3171            # invocation triggering a CMake rerun doesn't rely on the
3172            # environment variable still being available / have identical value.
3173            set(${var} $ENV{${var}} CACHE INTERNAL "Cached environment variable ${var}")
3174          endif()
3175
3176          if("${scope}" STREQUAL "ENV" AND DEFINED current_${var}
3177             AND NOT "${current_${var}}" STREQUAL "$ENV{${var}}"
3178          )
3179            # Variable exists as current scoped variable, defined in a CMakeLists.txt
3180            # file, however it is also set in environment.
3181            # This might be a surprise to the user, so warn about it.
3182            message(WARNING "environment variable '${var}' is hiding local "
3183                            "variable of same name.\n"
3184                            "Environment value (in use): $ENV{${var}}\n"
3185                            "Current scope value (hidden): ${current_${var}}\n"
3186            )
3187          endif()
3188
3189          return()
3190        endif()
3191      endif()
3192    endforeach()
3193  endforeach()
3194
3195  if(GET_VAR_MERGE)
3196    if(GET_VAR_REVERSE)
3197      list(REVERSE ${variable})
3198      list(REMOVE_DUPLICATES ${variable})
3199      list(REVERSE ${variable})
3200    else()
3201      list(REMOVE_DUPLICATES ${variable})
3202    endif()
3203    set(${variable} ${${variable}} PARENT_SCOPE)
3204  endif()
3205endfunction(zephyr_get variable)
3206
3207# Usage:
3208#   zephyr_create_scope(<scope>)
3209#
3210# Create a new scope for creation of scoped variables.
3211#
3212# <scope>: Name of new scope.
3213#
3214function(zephyr_create_scope scope)
3215  if(TARGET ${scope}_scope)
3216    message(FATAL_ERROR "zephyr_create_scope(${scope}) already exists.")
3217  endif()
3218
3219  add_custom_target(${scope}_scope)
3220endfunction()
3221
3222# Usage:
3223#   zephyr_set(<variable> <value> SCOPE <scope> [APPEND])
3224#
3225# Zephyr extension of CMake set which allows a variable to be set in a specific
3226# scope. The scope is used on later zephyr_get() invocation for precedence
3227# handling when a variable it set in multiple scopes.
3228#
3229# <variable>   : Name of variable
3230# <value>      : Value of variable, multiple values will create a list.
3231#                The SCOPE argument identifies the end of value list.
3232# SCOPE <scope>: Name of scope for the variable
3233# APPEND       : Append values to the already existing variable in <scope>
3234#
3235function(zephyr_set variable)
3236  cmake_parse_arguments(SET_VAR "APPEND" "SCOPE" "" ${ARGN})
3237
3238  zephyr_check_arguments_required_all(zephyr_set SET_VAR SCOPE)
3239
3240  if(NOT TARGET ${SET_VAR_SCOPE}_scope)
3241    message(FATAL_ERROR "zephyr_set(... SCOPE ${SET_VAR_SCOPE}) doesn't exists.")
3242  endif()
3243
3244  if(SET_VAR_APPEND)
3245    set(property_args APPEND)
3246  endif()
3247
3248  set_property(TARGET ${SET_VAR_SCOPE}_scope ${property_args}
3249               PROPERTY ${variable} ${SET_VAR_UNPARSED_ARGUMENTS}
3250  )
3251endfunction()
3252
3253# Usage:
3254#   zephyr_check_cache(<variable> [REQUIRED])
3255#
3256# Check the current CMake cache for <variable> and warn the user if the value
3257# is being modified.
3258#
3259# This can be used to ensure the user does not accidentally try to change
3260# Zephyr build variables, such as:
3261# - BOARD
3262# - SHIELD
3263#
3264# variable: Name of <variable> to check and set, for example BOARD.
3265# REQUIRED: Optional flag. If specified, then an unset <variable> will be
3266#           treated as an error.
3267# WATCH: Optional flag. If specified, watch the variable and print a warning if
3268#        the variable is later being changed.
3269#
3270# Details:
3271#   <variable> can be set by 3 sources.
3272#   - Using CMake argument, -D<variable>
3273#   - Using an environment variable
3274#   - In the project CMakeLists.txt before `find_package(Zephyr)`.
3275#
3276#   CLI has the highest precedence, then comes environment variables,
3277#   and then finally CMakeLists.txt.
3278#
3279#   The value defined on the first CMake invocation will be stored in the CMake
3280#   cache as CACHED_<variable>. This allows the Zephyr build system to detect
3281#   when a user reconfigures a sticky variable.
3282#
3283#   A user can ignore all the precedence rules if the same source is always used
3284#   E.g. always specifies -D<variable>= on the command line,
3285#   always has an environment <variable> set, or always has a set(<variable> foo)
3286#   line in his CMakeLists.txt and avoids mixing sources.
3287#
3288#   The selected <variable> can be accessed through the variable '<variable>' in
3289#   following Zephyr CMake code.
3290#
3291#   If the user tries to change <variable> to a new value, then a warning will
3292#   be printed, and the previously cached value (CACHED_<variable>) will be
3293#   used, as it has precedence.
3294#
3295#   Together with the warning, user is informed that in order to change
3296#   <variable> the build directory must be cleaned.
3297#
3298function(zephyr_check_cache variable)
3299  cmake_parse_arguments(CACHE_VAR "REQUIRED;WATCH" "" "" ${ARGN})
3300  string(TOLOWER ${variable} variable_text)
3301  string(REPLACE "_" " " variable_text ${variable_text})
3302
3303  get_property(cached_value CACHE ${variable} PROPERTY VALUE)
3304
3305  # If the build has already been configured in an earlier CMake invocation,
3306  # then CACHED_${variable} is set. The CACHED_${variable} setting takes
3307  # precedence over any user or CMakeLists.txt input.
3308  # If we detect that user tries to change the setting, then print a warning
3309  # that a pristine build is needed.
3310
3311  # If user uses -D<variable>=<new_value>, then cli_argument will hold the new
3312  # value, otherwise cli_argument will hold the existing (old) value.
3313  set(cli_argument ${cached_value})
3314  if(cli_argument STREQUAL CACHED_${variable})
3315    # The is no changes to the <variable> value.
3316    unset(cli_argument)
3317  endif()
3318
3319  set(app_cmake_lists ${${variable}})
3320  if(cached_value STREQUAL ${variable})
3321    # The app build scripts did not set a default, The variable we are
3322    # reading is the cached value from the CLI
3323    unset(app_cmake_lists)
3324  endif()
3325
3326  if(DEFINED CACHED_${variable})
3327    # Warn the user if it looks like he is trying to change the variable
3328    # without cleaning first
3329    if(cli_argument)
3330      if(NOT ((CACHED_${variable} STREQUAL cli_argument) OR (${variable}_DEPRECATED STREQUAL cli_argument)))
3331        message(WARNING "The build directory must be cleaned pristinely when "
3332                        "changing ${variable_text},\n"
3333                        "Current value=\"${CACHED_${variable}}\", "
3334                        "Ignored value=\"${cli_argument}\"")
3335      endif()
3336    endif()
3337
3338    if(CACHED_${variable})
3339      set(${variable} ${CACHED_${variable}} PARENT_SCOPE)
3340      set(${variable} ${CACHED_${variable}})
3341      # This resets the user provided value with previous (working) value.
3342      set(${variable} ${CACHED_${variable}} CACHE STRING "Selected ${variable_text}" FORCE)
3343    else()
3344      unset(${variable} PARENT_SCOPE)
3345      unset(${variable} CACHE)
3346    endif()
3347  else()
3348    zephyr_get(${variable})
3349  endif()
3350
3351  if(${CACHE_VAR_REQUIRED} AND NOT DEFINED ${variable})
3352    message(FATAL_ERROR "${variable} is not being defined on the CMake command-line,"
3353                        " in the environment or by the app."
3354    )
3355  endif()
3356
3357  if(DEFINED ${variable})
3358    # Store the specified variable in parent scope and the cache
3359    set(${variable} ${${variable}} PARENT_SCOPE)
3360    set(${variable} ${${variable}} CACHE STRING "Selected ${variable_text}")
3361  endif()
3362  set(CACHED_${variable} ${${variable}} CACHE STRING "Selected ${variable_text}")
3363
3364  if(CACHE_VAR_WATCH)
3365    # The variable is now set to its final value.
3366    zephyr_boilerplate_watch(${variable})
3367  endif()
3368endfunction(zephyr_check_cache variable)
3369
3370
3371# Usage:
3372#   zephyr_boilerplate_watch(SOME_BOILERPLATE_VAR)
3373#
3374# Inform the build system that SOME_BOILERPLATE_VAR, a variable
3375# handled in the Zephyr package's boilerplate code, is now fixed and
3376# should no longer be changed.
3377#
3378# This function uses variable_watch() to print a noisy warning
3379# if the variable is set after it returns.
3380function(zephyr_boilerplate_watch variable)
3381  variable_watch(${variable} zephyr_variable_set_too_late)
3382endfunction()
3383
3384function(zephyr_variable_set_too_late variable access value current_list_file)
3385  if (access STREQUAL "MODIFIED_ACCESS")
3386    message(WARNING
3387"
3388   **********************************************************************
3389   *
3390   *                    WARNING
3391   *
3392   * CMake variable ${variable} set to \"${value}\" in:
3393   *     ${current_list_file}
3394   *
3395   * This is too late to make changes! The change was ignored.
3396   *
3397   * Hint: ${variable} must be set before calling find_package(Zephyr ...).
3398   *
3399   **********************************************************************
3400")
3401  endif()
3402endfunction()
3403
3404# Usage:
3405#   zephyr_get_targets(<directory> <types> <targets>)
3406#
3407# Get build targets for a given directory and sub-directories.
3408#
3409# This functions will traverse the build tree, starting from <directory>.
3410# It will read the `BUILDSYSTEM_TARGETS` for each directory in the build tree
3411# and return the build types matching the <types> list.
3412# Example of types: OBJECT_LIBRARY, STATIC_LIBRARY, INTERFACE_LIBRARY, UTILITY.
3413#
3414# returns a list of targets in <targets> matching the required <types>.
3415function(zephyr_get_targets directory types targets)
3416    get_property(sub_directories DIRECTORY ${directory} PROPERTY SUBDIRECTORIES)
3417    get_property(dir_targets DIRECTORY ${directory} PROPERTY BUILDSYSTEM_TARGETS)
3418    foreach(dir_target ${dir_targets})
3419      get_property(target_type TARGET ${dir_target} PROPERTY TYPE)
3420      if(${target_type} IN_LIST types)
3421        list(APPEND ${targets} ${dir_target})
3422      endif()
3423    endforeach()
3424
3425    foreach(directory ${sub_directories})
3426        zephyr_get_targets(${directory} "${types}" ${targets})
3427    endforeach()
3428    set(${targets} ${${targets}} PARENT_SCOPE)
3429endfunction()
3430
3431# Usage:
3432#   test_sysbuild([REQUIRED])
3433#
3434# Test that current sample is invoked through sysbuild.
3435#
3436# This function tests that current CMake configure was invoked through sysbuild.
3437# If CMake configure was not invoked through sysbuild, then a warning is printed
3438# to the user. The warning can be upgraded to an error by setting `REQUIRED` as
3439# argument the `test_sysbuild()`.
3440#
3441# This function allows samples that are multi-image samples by nature to ensure
3442# all samples are correctly built together.
3443function(test_sysbuild)
3444  cmake_parse_arguments(TEST_SYSBUILD "REQUIRED" "" "" ${ARGN})
3445
3446  if(TEST_SYSBUILD_REQUIRED)
3447    set(message_mode FATAL_ERROR)
3448  else()
3449    set(message_mode WARNING)
3450  endif()
3451
3452  if(NOT SYSBUILD)
3453    message(${message_mode}
3454            "Project '${PROJECT_NAME}' is designed for sysbuild.\n"
3455            "For correct user-experiences, please build '${PROJECT_NAME}' "
3456            "using sysbuild."
3457    )
3458  endif()
3459endfunction()
3460
3461# Usage:
3462#   target_byproducts(TARGET <target> BYPRODUCTS <file> [<file>...])
3463#
3464# Specify additional BYPRODUCTS that this target produces.
3465#
3466# This function allows the build system to specify additional byproducts to
3467# target created with `add_executable()`. When linking an executable the linker
3468# may produce additional files, like map files. Those files are not known to the
3469# build system. This function makes it possible to describe such additional
3470# byproducts in an easy manner.
3471function(target_byproducts)
3472  cmake_parse_arguments(TB "" "TARGET" "BYPRODUCTS" ${ARGN})
3473
3474  if(NOT DEFINED TB_TARGET)
3475    message(FATAL_ERROR "target_byproducts() missing parameter: TARGET <target>")
3476  endif()
3477
3478  add_custom_command(TARGET ${TB_TARGET}
3479                     POST_BUILD COMMAND ${CMAKE_COMMAND} -E true
3480                     BYPRODUCTS ${TB_BYPRODUCTS}
3481                     COMMENT "Logical command for additional byproducts on target: ${TB_TARGET}"
3482  )
3483endfunction()
3484
3485# Usage:
3486#   topological_sort(TARGETS <target> [<target> ...]
3487#                    PROPERTY_NAME <property>
3488#                    RESULT <out-variable>)
3489#
3490# This function performs topological sorting of CMake targets using a specific
3491# <property>, which dictates target dependencies. A fatal error occurs if the
3492# provided dependencies cannot be met, e.g., if they contain cycles.
3493#
3494# TARGETS:       List of target names.
3495# PROPERTY_NAME: Name of the target property to be used when sorting. For every
3496#                target listed in TARGETS, this property must contain a list
3497#                (possibly empty) of other targets, which this target depends on
3498#                for a particular purpose. The property must not contain any
3499#                target which is not also found in TARGETS.
3500# RESULT:        Output variable, where the topologically sorted list of target
3501#                names will be returned.
3502#
3503function(topological_sort)
3504  cmake_parse_arguments(TS "" "RESULT;PROPERTY_NAME" "TARGETS" ${ARGN})
3505
3506  set(dep_targets)
3507  set(start_targets)
3508  set(sorted_targets)
3509
3510  foreach(target ${TS_TARGETS})
3511    get_target_property(${target}_dependencies ${target} ${TS_PROPERTY_NAME})
3512
3513    if(${target}_dependencies)
3514      list(APPEND dep_targets ${target})
3515    else()
3516      list(APPEND start_targets ${target})
3517    endif()
3518  endforeach()
3519
3520  while(TRUE)
3521    list(POP_FRONT start_targets node)
3522    list(APPEND sorted_targets ${node})
3523    set(to_remove)
3524    foreach(target ${dep_targets})
3525      if("${node}" IN_LIST ${target}_dependencies)
3526        list(REMOVE_ITEM ${target}_dependencies ${node})
3527        if(NOT ${target}_dependencies)
3528          list(APPEND start_targets ${target})
3529          list(APPEND to_remove ${target})
3530        endif()
3531      endif()
3532    endforeach()
3533
3534    foreach(target ${to_remove})
3535      list(REMOVE_ITEM dep_targets ${target})
3536    endforeach()
3537    if(NOT start_targets)
3538      break()
3539    endif()
3540  endwhile()
3541
3542  if(dep_targets)
3543    foreach(target ${dep_targets})
3544      get_target_property(deps ${target} ${TS_PROPERTY_NAME})
3545      list(JOIN deps " " deps)
3546      list(APPEND dep_string "${target} depends on: ${deps}")
3547    endforeach()
3548    list(JOIN dep_string "\n" dep_string)
3549    message(FATAL_ERROR "Unmet or cyclic dependencies:\n${dep_string}")
3550  endif()
3551
3552  set(${TS_RESULT} "${sorted_targets}" PARENT_SCOPE)
3553endfunction()
3554
3555########################################################
3556# 4. Devicetree extensions
3557########################################################
3558# 4.1. dt_*
3559#
3560# The following methods are for retrieving devicetree information in CMake.
3561#
3562# Notes:
3563#
3564# - In CMake, we refer to the nodes using the node's path, therefore
3565#   there is no dt_path(...) function for obtaining a node identifier
3566#   like there is in the C devicetree.h API.
3567#
3568# - As another difference from the C API, you can generally use an
3569#   alias at the beginning of a path interchangeably with the full
3570#   path to the aliased node in these functions. The usage comments
3571#   will make this clear in each case.
3572
3573# Usage:
3574#   dt_nodelabel(<var> NODELABEL <label>)
3575#
3576# Function for retrieving the node path for the node having nodelabel
3577# <label>.
3578#
3579# Example devicetree fragment:
3580#
3581#   / {
3582#           soc {
3583#                   nvic: interrupt-controller@e000e100  { ... };
3584#           };
3585#   };
3586#
3587# Example usage:
3588#
3589#   # Sets 'nvic_path' to "/soc/interrupt-controller@e000e100"
3590#   dt_nodelabel(nvic_path NODELABEL "nvic")
3591#
3592# The node's path will be returned in the <var> parameter.
3593# <var> will be undefined if node does not exist.
3594#
3595# <var>              : Return variable where the node path will be stored
3596# NODELABEL <label>  : Node label
3597function(dt_nodelabel var)
3598  set(req_single_args "NODELABEL")
3599  cmake_parse_arguments(DT_LABEL "" "${req_single_args}" "" ${ARGN})
3600
3601  if(${ARGV0} IN_LIST req_single_args)
3602    message(FATAL_ERROR "dt_nodelabel(${ARGV0} ...) missing return parameter.")
3603  endif()
3604
3605  foreach(arg ${req_single_args})
3606    if(NOT DEFINED DT_LABEL_${arg})
3607      message(FATAL_ERROR "dt_nodelabel(${ARGV0} ...) "
3608                          "missing required argument: ${arg}"
3609      )
3610    endif()
3611  endforeach()
3612
3613  get_target_property(${var} devicetree_target "DT_NODELABEL|${DT_LABEL_NODELABEL}")
3614  if(${${var}} STREQUAL ${var}-NOTFOUND)
3615    set(${var})
3616  endif()
3617
3618  set(${var} ${${var}} PARENT_SCOPE)
3619endfunction()
3620
3621# Usage:
3622#   dt_alias(<var> PROPERTY <prop>)
3623#
3624# Get a node path for an /aliases node property.
3625#
3626# Example usage:
3627#
3628#   # The full path to the 'led0' alias is returned in 'path'.
3629#   dt_alias(path PROPERTY "led0")
3630#
3631#   # The variable 'path' will be left undefined for a nonexistent
3632#   # alias "does-not-exist".
3633#   dt_alias(path PROPERTY "does-not-exist")
3634#
3635# The node's path will be returned in the <var> parameter. The
3636# variable will be left undefined if the alias does not exist.
3637#
3638# <var>           : Return variable where the node path will be stored
3639# PROPERTY <prop> : The alias to check
3640function(dt_alias var)
3641  set(req_single_args "PROPERTY")
3642  cmake_parse_arguments(DT_ALIAS "" "${req_single_args}" "" ${ARGN})
3643
3644  if(${ARGV0} IN_LIST req_single_args)
3645    message(FATAL_ERROR "dt_alias(${ARGV0} ...) missing return parameter.")
3646  endif()
3647
3648  foreach(arg ${req_single_args})
3649    if(NOT DEFINED DT_ALIAS_${arg})
3650      message(FATAL_ERROR "dt_alias(${ARGV0} ...) "
3651                          "missing required argument: ${arg}"
3652      )
3653    endif()
3654  endforeach()
3655
3656  get_target_property(${var} devicetree_target "DT_ALIAS|${DT_ALIAS_PROPERTY}")
3657  if(${${var}} STREQUAL ${var}-NOTFOUND)
3658    set(${var})
3659  endif()
3660
3661  set(${var} ${${var}} PARENT_SCOPE)
3662endfunction()
3663
3664# Usage:
3665#   dt_node_exists(<var> PATH <path>)
3666#
3667# Tests whether a node with path <path> exists in the devicetree.
3668#
3669# The <path> value may be any of these:
3670#
3671# - absolute path to a node, like '/foo/bar'
3672# - a node alias, like 'my-alias'
3673# - a node alias followed by a path to a child node, like 'my-alias/child-node'
3674#
3675# The result of the check, either TRUE or FALSE, will be returned in
3676# the <var> parameter.
3677#
3678# <var>       : Return variable where the check result will be returned
3679# PATH <path> : Node path
3680function(dt_node_exists var)
3681  set(req_single_args "PATH")
3682  cmake_parse_arguments(DT_NODE "" "${req_single_args}" "" ${ARGN})
3683
3684  if(${ARGV0} IN_LIST req_single_args)
3685    message(FATAL_ERROR "dt_node_exists(${ARGV0} ...) missing return parameter.")
3686  endif()
3687
3688  foreach(arg ${req_single_args})
3689    if(NOT DEFINED DT_NODE_${arg})
3690      message(FATAL_ERROR "dt_node_exists(${ARGV0} ...) "
3691                          "missing required argument: ${arg}"
3692      )
3693    endif()
3694  endforeach()
3695
3696  dt_path_internal(canonical "${DT_NODE_PATH}")
3697  if (DEFINED canonical)
3698    set(${var} TRUE PARENT_SCOPE)
3699  else()
3700    set(${var} FALSE PARENT_SCOPE)
3701  endif()
3702endfunction()
3703
3704# Usage:
3705#   dt_node_has_status(<var> PATH <path> STATUS <status>)
3706#
3707# Tests whether <path> refers to a node which:
3708# - exists in the devicetree, and
3709# - has a status property matching the <status> argument
3710#   (a missing status or an “ok” status is treated as if it
3711#    were “okay” instead)
3712#
3713# The <path> value may be any of these:
3714#
3715# - absolute path to a node, like '/foo/bar'
3716# - a node alias, like 'my-alias'
3717# - a node alias followed by a path to a child node, like 'my-alias/child-node'
3718#
3719# The result of the check, either TRUE or FALSE, will be returned in
3720# the <var> parameter.
3721#
3722# <var>           : Return variable where the check result will be returned
3723# PATH <path>     : Node path
3724# STATUS <status> : Status to check
3725function(dt_node_has_status var)
3726  set(req_single_args "PATH;STATUS")
3727  cmake_parse_arguments(DT_NODE "" "${req_single_args}" "" ${ARGN})
3728
3729  if(${ARGV0} IN_LIST req_single_args)
3730    message(FATAL_ERROR "dt_node_has_status(${ARGV0} ...) missing return parameter.")
3731  endif()
3732
3733  foreach(arg ${req_single_args})
3734    if(NOT DEFINED DT_NODE_${arg})
3735      message(FATAL_ERROR "dt_node_has_status(${ARGV0} ...) "
3736                          "missing required argument: ${arg}"
3737      )
3738    endif()
3739  endforeach()
3740
3741  dt_path_internal(canonical ${DT_NODE_PATH})
3742  if(NOT DEFINED canonical)
3743    set(${var} FALSE PARENT_SCOPE)
3744    return()
3745  endif()
3746
3747  dt_prop(status PATH ${canonical} PROPERTY status)
3748
3749  if(NOT DEFINED status OR status STREQUAL "ok")
3750    set(status "okay")
3751  endif()
3752
3753  if(status STREQUAL "${DT_NODE_STATUS}")
3754    set(${var} TRUE PARENT_SCOPE)
3755  else()
3756    set(${var} FALSE PARENT_SCOPE)
3757  endif()
3758endfunction()
3759
3760# Usage:
3761#
3762#   dt_prop(<var> PATH <path> PROPERTY <prop> [INDEX <idx>])
3763#
3764# Get a devicetree property value. The value will be returned in the
3765# <var> parameter.
3766#
3767# The <path> value may be any of these:
3768#
3769# - absolute path to a node, like '/foo/bar'
3770# - a node alias, like 'my-alias'
3771# - a node alias followed by a path to a child node, like 'my-alias/child-node'
3772#
3773# This function currently only supports properties with the following
3774# devicetree binding types: string, int, boolean, array, uint8-array,
3775# string-array, path.
3776#
3777# For array valued properties (including uint8-array and
3778# string-array), the entire array is returned as a CMake list unless
3779# INDEX is given. If INDEX is given, just the array element at index
3780# <idx> is returned.
3781#
3782# The property value will be returned in the <var> parameter if the
3783# node exists and has a property <prop> with one of the above types.
3784# <var> will be undefined otherwise.
3785#
3786# To test if the property is defined before using it, use DEFINED on
3787# the return <var>, like this:
3788#
3789#   dt_prop(reserved_ranges PATH "/soc/gpio@deadbeef" PROPERTY "gpio-reserved-ranges")
3790#   if(DEFINED reserved_ranges)
3791#     # Node exists and has the "gpio-reserved-ranges" property.
3792#   endif()
3793#
3794# To distinguish a missing node from a missing property, combine
3795# dt_prop() and dt_node_exists(), like this:
3796#
3797#   dt_node_exists(node_exists PATH "/soc/gpio@deadbeef")
3798#   dt_prop(reserved_ranges    PATH "/soc/gpio@deadbeef" PROPERTY "gpio-reserved-ranges")
3799#   if(DEFINED reserved_ranges)
3800#     # Node "/soc/gpio@deadbeef" exists and has the "gpio-reserved-ranges" property
3801#   elseif(node_exists)
3802#     # Node exists, but doesn't have the property, or the property has an unsupported type.
3803#   endif()
3804#
3805# <var>          : Return variable where the property value will be stored
3806# PATH <path>    : Node path
3807# PROPERTY <prop>: Property for which a value should be returned, as it
3808#                  appears in the DTS source
3809# INDEX <idx>    : Optional index when retrieving a value in an array property
3810function(dt_prop var)
3811  set(req_single_args "PATH;PROPERTY")
3812  set(single_args "INDEX")
3813  cmake_parse_arguments(DT_PROP "" "${req_single_args};${single_args}" "" ${ARGN})
3814
3815  if(${ARGV0} IN_LIST req_single_args)
3816    message(FATAL_ERROR "dt_prop(${ARGV0} ...) missing return parameter.")
3817  endif()
3818
3819  foreach(arg ${req_single_args})
3820    if(NOT DEFINED DT_PROP_${arg})
3821      message(FATAL_ERROR "dt_prop(${ARGV0} ...) "
3822                          "missing required argument: ${arg}"
3823      )
3824    endif()
3825  endforeach()
3826
3827  dt_path_internal(canonical "${DT_PROP_PATH}")
3828  get_property(exists TARGET devicetree_target
3829      PROPERTY "DT_PROP|${canonical}|${DT_PROP_PROPERTY}"
3830      SET
3831  )
3832
3833  if(NOT exists)
3834    set(${var} PARENT_SCOPE)
3835    return()
3836  endif()
3837
3838  get_target_property(val devicetree_target
3839      "DT_PROP|${canonical}|${DT_PROP_PROPERTY}"
3840  )
3841
3842  if(DEFINED DT_PROP_INDEX)
3843    list(GET val ${DT_PROP_INDEX} element)
3844    set(${var} "${element}" PARENT_SCOPE)
3845  else()
3846    set(${var} "${val}" PARENT_SCOPE)
3847  endif()
3848endfunction()
3849
3850# Usage:
3851#
3852#   dt_comp_path(<var> COMPATIBLE <compatible> [INDEX <idx>])
3853#
3854# Get a list of paths for the nodes with the given compatible. The value will
3855# be returned in the <var> parameter.
3856# <var> will be undefined if no such compatible exists.
3857#
3858# For details and considerations about the format of <path> and the returned
3859# parameter refer to dt_prop().
3860#
3861# <var>                  : Return variable where the property value will be stored
3862# COMPATIBLE <compatible>: Compatible for which the list of paths should be
3863#                          returned, as it appears in the DTS source
3864# INDEX <idx>            : Optional index when retrieving a value in an array property
3865
3866function(dt_comp_path var)
3867  set(req_single_args "COMPATIBLE")
3868  set(single_args "INDEX")
3869  cmake_parse_arguments(DT_COMP "" "${req_single_args};${single_args}" "" ${ARGN})
3870
3871  if(${ARGV0} IN_LIST req_single_args)
3872    message(FATAL_ERROR "dt_comp_path(${ARGV0} ...) missing return parameter.")
3873  endif()
3874
3875  foreach(arg ${req_single_args})
3876    if(NOT DEFINED DT_COMP_${arg})
3877      message(FATAL_ERROR "dt_comp_path(${ARGV0} ...) "
3878                          "missing required argument: ${arg}"
3879      )
3880    endif()
3881  endforeach()
3882
3883  get_property(exists TARGET devicetree_target
3884      PROPERTY "DT_COMP|${DT_COMP_COMPATIBLE}"
3885      SET
3886  )
3887
3888  if(NOT exists)
3889    set(${var} PARENT_SCOPE)
3890    return()
3891  endif()
3892
3893  get_target_property(val devicetree_target
3894      "DT_COMP|${DT_COMP_COMPATIBLE}"
3895  )
3896
3897  if(DEFINED DT_COMP_INDEX)
3898    list(GET val ${DT_COMP_INDEX} element)
3899    set(${var} "${element}" PARENT_SCOPE)
3900  else()
3901    set(${var} "${val}" PARENT_SCOPE)
3902  endif()
3903endfunction()
3904
3905# Usage:
3906#   dt_num_regs(<var> PATH <path>)
3907#
3908# Get the number of register blocks in the node's reg property;
3909# this may be zero.
3910#
3911# The value will be returned in the <var> parameter.
3912#
3913# The <path> value may be any of these:
3914#
3915# - absolute path to a node, like '/foo/bar'
3916# - a node alias, like 'my-alias'
3917# - a node alias followed by a path to a child node, like 'my-alias/child-node'
3918#
3919# <var>          : Return variable where the property value will be stored
3920# PATH <path>    : Node path
3921function(dt_num_regs var)
3922  set(req_single_args "PATH")
3923  cmake_parse_arguments(DT_REG "" "${req_single_args}" "" ${ARGN})
3924
3925  if(${ARGV0} IN_LIST req_single_args)
3926    message(FATAL_ERROR "dt_num_regs(${ARGV0} ...) missing return parameter.")
3927  endif()
3928
3929  foreach(arg ${req_single_args})
3930    if(NOT DEFINED DT_REG_${arg})
3931      message(FATAL_ERROR "dt_num_regs(${ARGV0} ...) "
3932                          "missing required argument: ${arg}"
3933      )
3934    endif()
3935  endforeach()
3936
3937  dt_path_internal(canonical "${DT_REG_PATH}")
3938  get_target_property(${var} devicetree_target "DT_REG|${canonical}|NUM")
3939
3940  set(${var} ${${var}} PARENT_SCOPE)
3941endfunction()
3942
3943# Usage:
3944#   dt_reg_addr(<var> PATH <path> [INDEX <idx>] [NAME <name>])
3945#
3946# Get the base address of the register block at index <idx>, or with
3947# name <name>. If <idx> and <name> are both omitted, the value at
3948# index 0 will be returned. Do not give both <idx> and <name>.
3949#
3950# The value will be returned in the <var> parameter.
3951#
3952# The <path> value may be any of these:
3953#
3954# - absolute path to a node, like '/foo/bar'
3955# - a node alias, like 'my-alias'
3956# - a node alias followed by a path to a child node, like 'my-alias/child-node'
3957#
3958# Results can be:
3959# - The base address of the register block
3960# - <var> will be undefined if node does not exists or does not have a register
3961#   block at the requested index or with the requested name
3962#
3963# <var>          : Return variable where the address value will be stored
3964# PATH <path>    : Node path
3965# INDEX <idx>    : Register block index number
3966# NAME <name>    : Register block name
3967function(dt_reg_addr var)
3968  set(req_single_args "PATH")
3969  set(single_args "INDEX;NAME")
3970  cmake_parse_arguments(DT_REG "" "${req_single_args};${single_args}" "" ${ARGN})
3971
3972  if(${ARGV0} IN_LIST req_single_args)
3973    message(FATAL_ERROR "dt_reg_addr(${ARGV0} ...) missing return parameter.")
3974  endif()
3975
3976  foreach(arg ${req_single_args})
3977    if(NOT DEFINED DT_REG_${arg})
3978      message(FATAL_ERROR "dt_reg_addr(${ARGV0} ...) "
3979                          "missing required argument: ${arg}"
3980      )
3981    endif()
3982  endforeach()
3983
3984  if(DEFINED DT_REG_INDEX AND DEFINED DT_REG_NAME)
3985    message(FATAL_ERROR "dt_reg_addr(${ARGV0} ...) given both INDEX and NAME")
3986  elseif(NOT DEFINED DT_REG_INDEX AND NOT DEFINED DT_REG_NAME)
3987    set(DT_REG_INDEX 0)
3988  elseif(DEFINED DT_REG_NAME)
3989    dt_reg_index_private(DT_REG_INDEX "${DT_REG_PATH}" "${DT_REG_NAME}")
3990    if(DT_REG_INDEX EQUAL "-1")
3991      set(${var} PARENT_SCOPE)
3992      return()
3993    endif()
3994  endif()
3995
3996  dt_path_internal(canonical "${DT_REG_PATH}")
3997  get_target_property(${var}_list devicetree_target "DT_REG|${canonical}|ADDR")
3998
3999  list(GET ${var}_list ${DT_REG_INDEX} ${var})
4000
4001  if("${var}" STREQUAL NONE)
4002    set(${var})
4003  endif()
4004
4005  set(${var} ${${var}} PARENT_SCOPE)
4006endfunction()
4007
4008# Usage:
4009#   dt_reg_size(<var> PATH <path> [INDEX <idx>] [NAME <name>])
4010#
4011# Get the size of the register block at index <idx>, or with
4012# name <name>. If <idx> and <name> are both omitted, the value at
4013# index 0 will be returned. Do not give both <idx> and <name>.
4014#
4015# The value will be returned in the <value> parameter.
4016#
4017# The <path> value may be any of these:
4018#
4019# - absolute path to a node, like '/foo/bar'
4020# - a node alias, like 'my-alias'
4021# - a node alias followed by a path to a child node, like 'my-alias/child-node'
4022#
4023# <var>          : Return variable where the size value will be stored
4024# PATH <path>    : Node path
4025# INDEX <idx>    : Register block index number
4026# NAME <name>    : Register block name
4027function(dt_reg_size var)
4028  set(req_single_args "PATH")
4029  set(single_args "INDEX;NAME")
4030  cmake_parse_arguments(DT_REG "" "${req_single_args};${single_args}" "" ${ARGN})
4031
4032  if(${ARGV0} IN_LIST req_single_args)
4033    message(FATAL_ERROR "dt_reg_size(${ARGV0} ...) missing return parameter.")
4034  endif()
4035
4036  foreach(arg ${req_single_args})
4037    if(NOT DEFINED DT_REG_${arg})
4038      message(FATAL_ERROR "dt_reg_size(${ARGV0} ...) "
4039                          "missing required argument: ${arg}"
4040      )
4041    endif()
4042  endforeach()
4043
4044  if(DEFINED DT_REG_INDEX AND DEFINED DT_REG_NAME)
4045    message(FATAL_ERROR "dt_reg_size(${ARGV0} ...) given both INDEX and NAME")
4046  elseif(NOT DEFINED DT_REG_INDEX AND NOT DEFINED DT_REG_NAME)
4047    set(DT_REG_INDEX 0)
4048  elseif(DEFINED DT_REG_NAME)
4049    dt_reg_index_private(DT_REG_INDEX "${DT_REG_PATH}" "${DT_REG_NAME}")
4050    if(DT_REG_INDEX EQUAL "-1")
4051      set(${var} PARENT_SCOPE)
4052      return()
4053    endif()
4054  endif()
4055
4056  dt_path_internal(canonical "${DT_REG_PATH}")
4057  get_target_property(${var}_list devicetree_target "DT_REG|${canonical}|SIZE")
4058
4059  list(GET ${var}_list ${DT_REG_INDEX} ${var})
4060
4061  if("${var}" STREQUAL NONE)
4062    set(${var})
4063  endif()
4064
4065  set(${var} ${${var}} PARENT_SCOPE)
4066endfunction()
4067
4068# Internal helper for dt_reg_addr/dt_reg_size; not meant to be used directly
4069function(dt_reg_index_private var path name)
4070  dt_prop(reg_names PATH "${path}" PROPERTY "reg-names")
4071  if(NOT DEFINED reg_names)
4072    set(index "-1")
4073  else()
4074    list(FIND reg_names "${name}" index)
4075  endif()
4076  set(${var} "${index}" PARENT_SCOPE)
4077endfunction()
4078
4079# Usage:
4080#   dt_has_chosen(<var> PROPERTY <prop>)
4081#
4082# Test if the devicetree's /chosen node has a given property
4083# <prop> which contains the path to a node.
4084#
4085# Example devicetree fragment:
4086#
4087#       chosen {
4088#               foo = &bar;
4089#       };
4090#
4091# Example usage:
4092#
4093#       # Sets 'result' to TRUE
4094#       dt_has_chosen(result PROPERTY "foo")
4095#
4096#       # Sets 'result' to FALSE
4097#       dt_has_chosen(result PROPERTY "baz")
4098#
4099# The result of the check, either TRUE or FALSE, will be stored in the
4100# <var> parameter.
4101#
4102# <var>           : Return variable
4103# PROPERTY <prop> : Chosen property
4104function(dt_has_chosen var)
4105  set(req_single_args "PROPERTY")
4106  cmake_parse_arguments(DT_CHOSEN "" "${req_single_args}" "" ${ARGN})
4107
4108  if(${ARGV0} IN_LIST req_single_args)
4109    message(FATAL_ERROR "dt_has_chosen(${ARGV0} ...) missing return parameter.")
4110  endif()
4111
4112  foreach(arg ${req_single_args})
4113    if(NOT DEFINED DT_CHOSEN_${arg})
4114      message(FATAL_ERROR "dt_has_chosen(${ARGV0} ...) "
4115                          "missing required argument: ${arg}"
4116      )
4117    endif()
4118  endforeach()
4119
4120  get_target_property(exists devicetree_target "DT_CHOSEN|${DT_CHOSEN_PROPERTY}")
4121
4122  if(${exists} STREQUAL exists-NOTFOUND)
4123    set(${var} FALSE PARENT_SCOPE)
4124  else()
4125    set(${var} TRUE PARENT_SCOPE)
4126  endif()
4127endfunction()
4128
4129# Usage:
4130#   dt_chosen(<var> PROPERTY <prop>)
4131#
4132# Get a node path for a /chosen node property.
4133#
4134# The node's path will be returned in the <var> parameter. The
4135# variable will be left undefined if the chosen node does not exist.
4136#
4137# <var>           : Return variable where the node path will be stored
4138# PROPERTY <prop> : Chosen property
4139function(dt_chosen var)
4140  set(req_single_args "PROPERTY")
4141  cmake_parse_arguments(DT_CHOSEN "" "${req_single_args}" "" ${ARGN})
4142
4143  if(${ARGV0} IN_LIST req_single_args)
4144    message(FATAL_ERROR "dt_chosen(${ARGV0} ...) missing return parameter.")
4145  endif()
4146
4147  foreach(arg ${req_single_args})
4148    if(NOT DEFINED DT_CHOSEN_${arg})
4149      message(FATAL_ERROR "dt_chosen(${ARGV0} ...) "
4150                          "missing required argument: ${arg}"
4151      )
4152    endif()
4153  endforeach()
4154
4155  get_target_property(${var} devicetree_target "DT_CHOSEN|${DT_CHOSEN_PROPERTY}")
4156
4157  if(${${var}} STREQUAL ${var}-NOTFOUND)
4158    set(${var} PARENT_SCOPE)
4159  else()
4160    set(${var} ${${var}} PARENT_SCOPE)
4161  endif()
4162endfunction()
4163
4164# Internal helper. Canonicalizes a path 'path' into the output
4165# variable 'var'. This resolves aliases, if any. Child nodes may be
4166# accessed via alias as well. 'var' is left undefined if the path does
4167# not refer to an existing node.
4168#
4169# Example devicetree:
4170#
4171#   / {
4172#           foo {
4173#                   my-label: bar {
4174#                           baz {};
4175#                   };
4176#           };
4177#           aliases {
4178#                   my-alias = &my-label;
4179#           };
4180#   };
4181#
4182# Example usage:
4183#
4184#   dt_path_internal(ret "/foo/bar")     # sets ret to "/foo/bar"
4185#   dt_path_internal(ret "my-alias")     # sets ret to "/foo/bar"
4186#   dt_path_internal(ret "my-alias/baz") # sets ret to "/foo/bar/baz"
4187#   dt_path_internal(ret "/blub")        # ret is undefined
4188function(dt_path_internal var path)
4189  string(FIND "${path}" "/" slash_index)
4190
4191  if("${slash_index}" EQUAL 0)
4192    # If the string starts with a slash, it should be an existing
4193    # canonical path.
4194    dt_path_internal_exists(check "${path}")
4195    if (check)
4196      set(${var} "${path}" PARENT_SCOPE)
4197      return()
4198    endif()
4199  else()
4200    # Otherwise, try to expand a leading alias.
4201    string(SUBSTRING "${path}" 0 "${slash_index}" alias_name)
4202    dt_alias(alias_path PROPERTY "${alias_name}")
4203
4204    # If there is a leading alias, append the rest of the string
4205    # onto it and see if that's an existing node.
4206    if (DEFINED alias_path)
4207      set(rest)
4208      if (NOT "${slash_index}" EQUAL -1)
4209        string(SUBSTRING "${path}" "${slash_index}" -1 rest)
4210      endif()
4211      dt_path_internal_exists(expanded_path_exists "${alias_path}${rest}")
4212      if (expanded_path_exists)
4213        set(${var} "${alias_path}${rest}" PARENT_SCOPE)
4214        return()
4215      endif()
4216    endif()
4217  endif()
4218
4219  # Failed search; ensure return variable is undefined.
4220  set(${var} PARENT_SCOPE)
4221endfunction()
4222
4223# Internal helper. Set 'var' to TRUE if a canonical path 'path' refers
4224# to an existing node. Set it to FALSE otherwise. See
4225# dt_path_internal for a definition and examples of 'canonical' paths.
4226function(dt_path_internal_exists var path)
4227  get_target_property(path_prop devicetree_target "DT_NODE|${path}")
4228  if (path_prop)
4229    set(${var} TRUE PARENT_SCOPE)
4230  else()
4231    set(${var} FALSE PARENT_SCOPE)
4232  endif()
4233endfunction()
4234
4235# 4.2. *_if_dt_node
4236#
4237# This section is similar to the extensions named *_ifdef, except
4238# actions are performed if the devicetree contains some node.
4239# *_if_dt_node functions may be added as needed, or if they are likely
4240# to be useful for user applications.
4241
4242# Add item(s) to a target's SOURCES list if a devicetree node exists.
4243#
4244# Example usage:
4245#
4246#   # If the devicetree alias "led0" refers to a node, this
4247#   # adds "blink_led.c" to the sources list for the "app" target.
4248#   target_sources_if_dt_node("led0" app PRIVATE blink_led.c)
4249#
4250#   # If the devicetree path "/soc/serial@4000" is a node, this
4251#   # adds "uart.c" to the sources list for the "lib" target,
4252#   target_sources_if_dt_node("/soc/serial@4000" lib PRIVATE uart.c)
4253#
4254# <path>    : Path to devicetree node to check
4255# <target>  : Build system target whose sources to add to
4256# <scope>   : Scope to add items to
4257# <item>    : Item (or items) to add to the target
4258function(target_sources_if_dt_node path target scope item)
4259  dt_node_exists(check PATH "${path}")
4260  if(${check})
4261    target_sources(${target} ${scope} ${item} ${ARGN})
4262  endif()
4263endfunction()
4264
4265########################################################
4266# 4.3 zephyr_dt_*
4267#
4268# The following methods are common code for dealing
4269# with devicetree related files in CMake.
4270#
4271# Note that functions related to accessing the
4272# *contents* of the devicetree belong in section 4.1.
4273# This section is just for DT file processing at
4274# configuration time.
4275########################################################
4276
4277# Usage:
4278#   zephyr_dt_preprocess(CPP <path> [<argument...>]
4279#                        SOURCE_FILES <file...>
4280#                        OUT_FILE <file>
4281#                        [DEPS_FILE <file>]
4282#                        [EXTRA_CPPFLAGS <flag...>]
4283#                        [INCLUDE_DIRECTORIES <dir...>]
4284#                        [WORKING_DIRECTORY <dir>]
4285#
4286# Preprocess one or more devicetree source files. The preprocessor
4287# symbol __DTS__ will be defined. If the preprocessor command fails, a
4288# fatal error occurs.
4289#
4290# Mandatory arguments:
4291#
4292# CPP <path> [<argument...>]: path to C preprocessor, followed by any
4293#                             additional arguments
4294#
4295# SOURCE_FILES <file...>: The source files to run the preprocessor on.
4296#                         These will, in effect, be concatenated in order
4297#                         and used as the preprocessor input.
4298#
4299# OUT_FILE <file>: Where to store the preprocessor output.
4300#
4301# Optional arguments:
4302#
4303# DEPS_FILE <file>: If set, generate a dependency file here.
4304#
4305# EXTRA_CPPFLAGS <flag...>: Additional flags to pass the preprocessor.
4306#
4307# INCLUDE_DIRECTORIES <dir...>: Additional #include file directories.
4308#
4309# WORKING_DIRECTORY <dir>: where to run the preprocessor.
4310function(zephyr_dt_preprocess)
4311  set(req_single_args "OUT_FILE")
4312  set(single_args "DEPS_FILE;WORKING_DIRECTORY")
4313  set(req_multi_args "CPP;SOURCE_FILES")
4314  set(multi_args "EXTRA_CPPFLAGS;INCLUDE_DIRECTORIES")
4315  cmake_parse_arguments(DT_PREPROCESS "" "${req_single_args};${single_args}" "${req_multi_args};${multi_args}" ${ARGN})
4316
4317  foreach(arg ${req_single_args} ${req_multi_args})
4318    if(NOT DEFINED DT_PREPROCESS_${arg})
4319      message(FATAL_ERROR "dt_preprocess() missing required argument: ${arg}")
4320    endif()
4321  endforeach()
4322
4323  set(include_opts)
4324  foreach(dir ${DT_PREPROCESS_INCLUDE_DIRECTORIES})
4325    list(APPEND include_opts -isystem ${dir})
4326  endforeach()
4327
4328  set(source_opts)
4329  foreach(file ${DT_PREPROCESS_SOURCE_FILES})
4330    list(APPEND source_opts -include ${file})
4331  endforeach()
4332
4333  set(deps_opts)
4334  if(DEFINED DT_PREPROCESS_DEPS_FILE)
4335    list(APPEND deps_opts -MD -MF ${DT_PREPROCESS_DEPS_FILE})
4336  endif()
4337
4338  set(workdir_opts)
4339  if(DEFINED DT_PREPROCESS_WORKING_DIRECTORY)
4340    list(APPEND workdir_opts WORKING_DIRECTORY ${DT_PREPROCESS_WORKING_DIRECTORY})
4341  endif()
4342
4343  # We are leaving linemarker directives enabled on purpose. This tells
4344  # dtlib where each line actually came from, which improves error
4345  # reporting.
4346  set(preprocess_cmd ${DT_PREPROCESS_CPP}
4347    -x assembler-with-cpp
4348    -nostdinc
4349    ${include_opts}
4350    ${source_opts}
4351    ${NOSYSDEF_CFLAG}
4352    -D__DTS__
4353    ${DT_PREPROCESS_EXTRA_CPPFLAGS}
4354    -E   # Stop after preprocessing
4355    ${deps_opts}
4356    -o ${DT_PREPROCESS_OUT_FILE}
4357    ${ZEPHYR_BASE}/misc/empty_file.c
4358    ${workdir_opts})
4359
4360  execute_process(COMMAND ${preprocess_cmd} RESULT_VARIABLE ret)
4361  if(NOT "${ret}" STREQUAL "0")
4362    message(FATAL_ERROR "failed to preprocess devicetree files (error code ${ret}): ${DT_PREPROCESS_SOURCE_FILES}")
4363  endif()
4364endfunction()
4365
4366########################################################
4367# 5. Zephyr linker functions
4368########################################################
4369# 5.1. zephyr_linker*
4370#
4371# The following methods are for defining linker structure using CMake functions.
4372#
4373# This allows Zephyr developers to define linker sections and their content and
4374# have this configuration rendered into an appropriate linker script based on
4375# the toolchain in use.
4376# For example:
4377# ld linker scripts with GNU ld
4378# ARM scatter files with ARM linker.
4379#
4380# Example usage:
4381# zephyr_linker_section(
4382#   NAME my_data
4383#   VMA  RAM
4384#   LMA  FLASH
4385# )
4386#
4387# and to configure special input sections for the section
4388# zephyr_linker_section_configure(
4389#   SECTION my_data
4390#   INPUT   "my_custom_data"
4391#   KEEP
4392# )
4393
4394
4395# Usage:
4396#   zephyr_linker([FORMAT <format>]
4397#     [ENTRY <entry symbol>]
4398#   )
4399#
4400# Zephyr linker general settings.
4401# This function specifies general settings for the linker script to be generated.
4402#
4403# FORMAT <format>:            The output format of the linked executable.
4404# ENTRY <entry symbolformat>: The code entry symbol.
4405#
4406function(zephyr_linker)
4407  set(single_args "ENTRY;FORMAT")
4408  cmake_parse_arguments(LINKER "" "${single_args}" "" ${ARGN})
4409
4410  if(LINKER_UNPARSED_ARGUMENTS)
4411    message(FATAL_ERROR "zephyr_linker(${ARGV0} ...) given unknown "
4412                        "arguments: ${LINKER_UNPARSED_ARGUMENTS}"
4413    )
4414  endif()
4415
4416  if(DEFINED LINKER_FORMAT)
4417    get_property(format_defined TARGET linker PROPERTY FORMAT SET)
4418    if(format_defined)
4419      message(FATAL_ERROR "zephyr_linker(FORMAT ...) already configured.")
4420    else()
4421      set_property(TARGET linker PROPERTY FORMAT ${LINKER_FORMAT})
4422    endif()
4423  endif()
4424
4425  if(DEFINED LINKER_ENTRY)
4426    get_property(entry_defined TARGET linker PROPERTY ENTRY SET)
4427    if(entry_defined)
4428      message(FATAL_ERROR "zephyr_linker(ENTRY ...) already configured.")
4429    else()
4430      set_property(TARGET linker PROPERTY ENTRY ${LINKER_ENTRY})
4431    endif()
4432  endif()
4433endfunction()
4434
4435# Usage:
4436#   zephyr_linker_memory(NAME <name> START <address> SIZE <size> FLAGS <flags>)
4437#
4438# Zephyr linker memory.
4439# This function specifies a memory region for the platform in use.
4440#
4441# Note:
4442#   This function should generally be called with values obtained from
4443#   devicetree or Kconfig.
4444#
4445# NAME <name>    : Name of the memory region, for example FLASH.
4446# START <address>: Start address of the memory region.
4447#                  Start address can be given as decimal or hex value.
4448# SIZE <size>    : Size of the memory region.
4449#                  Size can be given as decimal value, hex value, or decimal with postfix k or m.
4450#                  All the following are valid values:
4451#                    1048576, 0x10000, 1024k, 1024K, 1m, and 1M.
4452# FLAGS <flags>  : Flags describing properties of the memory region.
4453#                  Currently supported:
4454#                  r: Read-only region
4455#                  w: Read-write region
4456#                  x: Executable region
4457#                  The flags r and x, or w and x may be combined like: rx, wx.
4458function(zephyr_linker_memory)
4459  set(single_args "FLAGS;NAME;SIZE;START")
4460  cmake_parse_arguments(MEMORY "" "${single_args}" "" ${ARGN})
4461
4462  if(MEMORY_UNPARSED_ARGUMENTS)
4463    message(FATAL_ERROR "zephyr_linker_memory(${ARGV0} ...) given unknown "
4464                        "arguments: ${MEMORY_UNPARSED_ARGUMENTS}"
4465    )
4466  endif()
4467
4468  foreach(arg ${single_args})
4469    if(NOT DEFINED MEMORY_${arg})
4470      message(FATAL_ERROR "zephyr_linker_memory(${ARGV0} ...) missing required "
4471                          "argument: ${arg}"
4472      )
4473    endif()
4474  endforeach()
4475
4476  set(MEMORY)
4477  zephyr_linker_arg_val_list(MEMORY "${single_args}")
4478
4479  string(REPLACE ";" "\;" MEMORY "${MEMORY}")
4480  set_property(TARGET linker
4481               APPEND PROPERTY MEMORY_REGIONS "{${MEMORY}}"
4482  )
4483endfunction()
4484
4485# Usage:
4486#   zephyr_linker_memory_ifdef(<setting> NAME <name> START <address> SIZE <size> FLAGS <flags>)
4487#
4488# Will create memory region if <setting> is enabled.
4489#
4490# <setting>: Setting to check for True value before invoking
4491#            zephyr_linker_memory()
4492#
4493# See zephyr_linker_memory() description for other supported arguments.
4494#
4495macro(zephyr_linker_memory_ifdef feature_toggle)
4496  if(${${feature_toggle}})
4497    zephyr_linker_memory(${ARGN})
4498  endif()
4499endmacro()
4500
4501# Usage:
4502#   zephyr_linker_dts_section(PATH <path>)
4503#
4504# Zephyr linker devicetree memory section from path.
4505#
4506# This function specifies an output section for the platform in use based on its
4507# devicetree configuration.
4508#
4509# The section will only be defined if the devicetree exists and has status okay.
4510#
4511# PATH <path>      : Devicetree node path.
4512#
4513function(zephyr_linker_dts_section)
4514  set(single_args "PATH")
4515  cmake_parse_arguments(DTS_SECTION "" "${single_args}" "" ${ARGN})
4516
4517  if(DTS_SECTION_UNPARSED_ARGUMENTS)
4518    message(FATAL_ERROR "zephyr_linker_dts_section(${ARGV0} ...) given unknown "
4519            "arguments: ${DTS_SECTION_UNPARSED_ARGUMENTS}"
4520    )
4521  endif()
4522
4523  if(NOT DEFINED DTS_SECTION_PATH)
4524    message(FATAL_ERROR "zephyr_linker_dts_section(${ARGV0} ...) missing "
4525                        "required argument: PATH"
4526    )
4527  endif()
4528
4529  dt_node_has_status(okay PATH ${DTS_SECTION_PATH} STATUS okay)
4530  if(NOT ${okay})
4531    return()
4532  endif()
4533
4534  dt_prop(name PATH ${DTS_SECTION_PATH} PROPERTY "zephyr,memory-region")
4535  if(NOT DEFINED name)
4536    message(FATAL_ERROR "zephyr_linker_dts_section(${ARGV0} ...) missing "
4537                        "\"zephyr,memory-region\" property"
4538    )
4539  endif()
4540  zephyr_string(SANITIZE name ${name})
4541
4542  dt_reg_addr(addr PATH ${DTS_SECTION_PATH})
4543
4544  zephyr_linker_section(NAME ${name} ADDRESS ${addr} VMA ${name} TYPE NOLOAD)
4545
4546endfunction()
4547
4548# Usage:
4549#   zephyr_linker_dts_memory(PATH <path> FLAGS <flags>)
4550#   zephyr_linker_dts_memory(NODELABEL <nodelabel> FLAGS <flags>)
4551#   zephyr_linker_dts_memory(CHOSEN <prop> FLAGS <flags>)
4552#
4553# Zephyr linker devicetree memory.
4554# This function specifies a memory region for the platform in use based on its
4555# devicetree configuration.
4556#
4557# The memory will only be defined if the devicetree node or a devicetree node
4558# matching the nodelabel exists and has status okay.
4559#
4560# Only one of PATH, NODELABEL, and CHOSEN parameters may be given.
4561#
4562# PATH <path>      : Devicetree node identifier.
4563# NODELABEL <label>: Node label
4564# CHOSEN <prop>    : Chosen property, add memory section described by the
4565#                    /chosen property if it exists.
4566# FLAGS <flags>  : Flags describing properties of the memory region.
4567#                  Currently supported:
4568#                  r: Read-only region
4569#                  w: Read-write region
4570#                  x: Executable region
4571#                  The flags r and x, or w and x may be combined like: rx, wx.
4572#
4573function(zephyr_linker_dts_memory)
4574  set(single_args "CHOSEN;FLAGS;PATH;NODELABEL")
4575  cmake_parse_arguments(DTS_MEMORY "" "${single_args}" "" ${ARGN})
4576
4577  if(DTS_MEMORY_UNPARSED_ARGUMENTS)
4578    message(FATAL_ERROR "zephyr_linker_dts_memory(${ARGV0} ...) given unknown "
4579                        "arguments: ${DTS_MEMORY_UNPARSED_ARGUMENTS}"
4580    )
4581  endif()
4582
4583  if((DEFINED DTS_MEMORY_PATH AND (DEFINED DTS_MEMORY_NODELABEL OR DEFINED DTS_MEMORY_CHOSEN))
4584     OR (DEFINED DTS_MEMORY_NODELABEL AND DEFINED DTS_MEMORY_CHOSEN))
4585    message(FATAL_ERROR "zephyr_linker_dts_memory(${ARGV0} ...), only one of "
4586                        "PATH, NODELABEL, and CHOSEN is allowed."
4587    )
4588  endif()
4589
4590  if(DEFINED DTS_MEMORY_NODELABEL)
4591    dt_nodelabel(DTS_MEMORY_PATH NODELABEL ${DTS_MEMORY_NODELABEL})
4592  endif()
4593
4594  if(DEFINED DTS_MEMORY_CHOSEN)
4595    dt_chosen(DTS_MEMORY_PATH PROPERTY ${DTS_MEMORY_CHOSEN})
4596  endif()
4597
4598  if(NOT DEFINED DTS_MEMORY_PATH)
4599    return()
4600  endif()
4601
4602  dt_node_has_status(okay PATH ${DTS_MEMORY_PATH} STATUS okay)
4603  if(NOT ${okay})
4604    return()
4605  endif()
4606
4607  dt_reg_addr(addr PATH ${DTS_MEMORY_PATH})
4608  dt_reg_size(size PATH ${DTS_MEMORY_PATH})
4609  dt_prop(name PATH ${DTS_MEMORY_PATH} PROPERTY "zephyr,memory-region")
4610  if(NOT DEFINED name)
4611    message(FATAL_ERROR "zephyr_linker_dts_memory(${ARGV0} ...) missing "
4612                        "\"zephyr,memory-region\" property"
4613    )
4614  endif()
4615  zephyr_string(SANITIZE name ${name})
4616
4617  zephyr_linker_memory(
4618    NAME  ${name}
4619    START ${addr}
4620    SIZE  ${size}
4621    FLAGS ${DTS_MEMORY_FLAGS}
4622  )
4623endfunction()
4624
4625# Usage:
4626#   zephyr_linker_group(NAME <name> [VMA <region|group>] [LMA <region|group>] [SYMBOL <SECTION>])
4627#   zephyr_linker_group(NAME <name> GROUP <group> [SYMBOL <SECTION>])
4628#
4629# Zephyr linker group.
4630# This function specifies a group inside a memory region or another group.
4631#
4632# The group ensures that all section inside the group are located together inside
4633# the specified group.
4634#
4635# This also allows for section placement inside a given group without the section
4636# itself needing the precise knowledge regarding the exact memory region this
4637# section will be placed in, as that will be determined by the group setup.
4638#
4639# Each group will define the following linker symbols:
4640# __<name>_start      : Start address of the group
4641# __<name>_end        : End address of the group
4642# __<name>_size       : Size of the group
4643#
4644# Note: <name> will be converted to lower casing for linker symbols definitions.
4645#
4646# NAME <name>         : Name of the group.
4647# VMA <region|group>  : VMA Memory region or group to be used for this group.
4648#                       If a group is used then the VMA region of that group will be used.
4649# LMA <region|group>  : Memory region or group to be used for this group.
4650# GROUP <group>       : Place the new group inside the existing group <group>
4651# SYMBOL <SECTION>    : Specify that start symbol of the region should be identical
4652#                       to the start address of the first section in the group.
4653#
4654# Note: VMA and LMA are mutual exclusive with GROUP
4655#
4656# Example:
4657#   zephyr_linker_memory(NAME memA START ... SIZE ... FLAGS ...)
4658#   zephyr_linker_group(NAME groupA LMA memA)
4659#   zephyr_linker_group(NAME groupB LMA groupA)
4660#
4661# will create two groups in same memory region as groupB will inherit the LMA
4662# from groupA:
4663#
4664# +-----------------+
4665# | memory region A |
4666# |                 |
4667# | +-------------+ |
4668# | | groupA      | |
4669# | +-------------+ |
4670# |                 |
4671# | +-------------+ |
4672# | | groupB      | |
4673# | +-------------+ |
4674# |                 |
4675# +-----------------+
4676#
4677# whereas
4678#   zephyr_linker_memory(NAME memA START ... SIZE ... FLAGS ...)
4679#   zephyr_linker_group(NAME groupA LMA memA)
4680#   zephyr_linker_group(NAME groupB GROUP groupA)
4681#
4682# will create groupB inside groupA:
4683#
4684# +---------------------+
4685# | memory region A     |
4686# |                     |
4687# | +-----------------+ |
4688# | | groupA          | |
4689# | |                 | |
4690# | | +-------------+ | |
4691# | | | groupB      | | |
4692# | | +-------------+ | |
4693# | |                 | |
4694# | +-----------------+ |
4695# |                     |
4696# +---------------------+
4697function(zephyr_linker_group)
4698  set(single_args "NAME;GROUP;LMA;SYMBOL;VMA")
4699  set(symbol_values SECTION)
4700  cmake_parse_arguments(GROUP "" "${single_args}" "" ${ARGN})
4701
4702  if(GROUP_UNPARSED_ARGUMENTS)
4703    message(FATAL_ERROR "zephyr_linker_group(${ARGV0} ...) given unknown "
4704                        "arguments: ${GROUP_UNPARSED_ARGUMENTS}"
4705    )
4706  endif()
4707
4708  if(DEFINED GROUP_GROUP AND (DEFINED GROUP_VMA OR DEFINED GROUP_LMA))
4709    message(FATAL_ERROR "zephyr_linker_group(GROUP ...) cannot be used with "
4710                        "VMA or LMA"
4711    )
4712  endif()
4713
4714  if(DEFINED GROUP_SYMBOL)
4715    if(NOT ${GROUP_SYMBOL} IN_LIST symbol_values)
4716      message(FATAL_ERROR "zephyr_linker_group(SYMBOL ...) given unknown value")
4717    endif()
4718  endif()
4719
4720  set(GROUP)
4721  zephyr_linker_arg_val_list(GROUP "${single_args}")
4722
4723  string(REPLACE ";" "\;" GROUP "${GROUP}")
4724  set_property(TARGET linker
4725               APPEND PROPERTY GROUPS "{${GROUP}}"
4726  )
4727endfunction()
4728
4729# Usage:
4730#   zephyr_linker_section(NAME <name> [GROUP <group>]
4731#                         [VMA <region|group>] [LMA <region|group>]
4732#                         [ADDRESS <address>] [ALIGN <alignment>]
4733#                         [SUBALIGN <alignment>] [FLAGS <flags>]
4734#                         [HIDDEN] [NOINPUT] [NOINIT]
4735#                         [PASS [NOT] <name>]
4736#   )
4737#
4738# Zephyr linker output section.
4739# This function specifies an output section for the linker.
4740#
4741# When using zephyr_linker_section(NAME <name>) an output section with <name>
4742# will be configured. This section will default include input sections of the
4743# same name, unless NOINPUT is specified.
4744# This means the section named `foo` will default include the sections matching
4745# `foo` and `foo.*`
4746# Each output section will define the following linker symbols:
4747# __<name>_start      : Start address of the section
4748# __<name>_end        : End address of the section
4749# __<name>_size       : Size of the section
4750# __<name>_load_start : Load address of the section, if VMA = LMA then this
4751#                       value will be identical to `__<name>_start`
4752#
4753# The location of the output section can be controlled using LMA, VMA, and
4754# address parameters
4755#
4756# NAME <name>         : Name of the output section.
4757# VMA <region|group>  : VMA Memory region or group where code / data is located runtime (VMA)
4758#                       If <group> is used here it means the section will use the
4759#                       same VMA memory region as <group> but will not be placed
4760#                       inside the group itself, see also GROUP argument.
4761# KVMA <region|group> : Kernel VMA Memory region or group where code / data is located runtime (VMA)
4762#                       When MMU is active and Kernel VM base and offset is different
4763#                       from SRAM base and offset, then the region defined by KVMA will
4764#                       be used as VMA.
4765#                       If <group> is used here it means the section will use the
4766#                       same VMA memory region as <group> but will not be placed
4767#                       inside the group itself, see also GROUP argument.
4768# LMA <region|group>  : Memory region or group where code / data is loaded (LMA)
4769#                       If VMA is different from LMA, the code / data will be loaded
4770#                       from LMA into VMA at bootup, this is usually the case for
4771#                       global or static variables that are loaded in rom and copied
4772#                       to ram at boot time.
4773#                       If <group> is used here it means the section will use the
4774#                       same LMA memory region as <group> but will not be placed
4775#                       inside the group itself, see also GROUP argument.
4776# GROUP <group>       : Place this section inside the group <group>
4777# ADDRESS <address>   : Specific address to use for this section.
4778# ALIGN_WITH_INPUT    : The alignment difference between VMA and LMA is kept
4779#                       intact for this section.
4780# ALIGN <alignment>   : Align the execution address with alignment.
4781# SUBALIGN <alignment>: Align input sections with alignment value.
4782# ENDALIGN <alignment>: Align the end so that next output section will start aligned.
4783#                       This only has effect on Scatter scripts.
4784#  Note: Regarding all alignment attributes. Not all linkers may handle alignment
4785#        in identical way. For example the Scatter file will align both load and
4786#        execution address (LMA and VMA) to be aligned when given the ALIGN attribute.
4787# NOINPUT             : No default input sections will be defined, to setup input
4788#                       sections for section <name>, the corresponding
4789#                       `zephyr_linker_section_configure()` must be used.
4790# PASS [NOT] <name>   : Linker pass iteration where this section should be active.
4791#                       Default a section will be present during all linker passes
4792#                       but in cases a section shall only be present at a specific
4793#                       pass, this argument can be used. For example to only have
4794#                       this section present on the `TEST` linker pass, use `PASS TEST`.
4795#                       It is possible to negate <name>, such as `PASS NOT <name>`.
4796#                       For example, `PASS NOT TEST` means the call is effective
4797#                       on all but the `TEST` linker pass iteration.
4798#
4799# Note: VMA and LMA are mutual exclusive with GROUP
4800#
4801function(zephyr_linker_section)
4802  set(options     "ALIGN_WITH_INPUT;HIDDEN;NOINIT;NOINPUT")
4803  set(single_args "ADDRESS;ALIGN;ENDALIGN;GROUP;KVMA;LMA;NAME;SUBALIGN;TYPE;VMA")
4804  set(multi_args  "PASS")
4805  cmake_parse_arguments(SECTION "${options}" "${single_args}" "${multi_args}" ${ARGN})
4806
4807  if(SECTION_UNPARSED_ARGUMENTS)
4808    message(WARNING "zephyr_linker_section(${ARGV0} ...) given unknown "
4809                    "arguments: ${SECTION_UNPARSED_ARGUMENTS}"
4810    )
4811  endif()
4812
4813  if(DEFINED SECTION_GROUP AND (DEFINED SECTION_VMA OR DEFINED SECTION_LMA))
4814    message(FATAL_ERROR "zephyr_linker_section(GROUP ...) cannot be used with "
4815                        "VMA or LMA"
4816    )
4817  endif()
4818
4819  if(DEFINED SECTION_KVMA)
4820    # If KVMA is set and the Kernel virtual memory settings reqs are met, we
4821    # substitute the VMA setting with the specified KVMA value.
4822    if(CONFIG_MMU)
4823      math(EXPR KERNEL_MEM_VM_OFFSET
4824           "(${CONFIG_KERNEL_VM_BASE} + ${CONFIG_KERNEL_VM_OFFSET})\
4825            - (${CONFIG_SRAM_BASE_ADDRESS} + ${CONFIG_SRAM_OFFSET})"
4826      )
4827
4828      if(NOT (${KERNEL_MEM_VM_OFFSET} EQUAL 0))
4829        set(SECTION_VMA ${SECTION_KVMA})
4830        set(SECTION_KVMA)
4831      endif()
4832    endif()
4833  endif()
4834
4835  if(DEFINED SECTION_PASS)
4836    list(LENGTH SECTION_PASS pass_length)
4837    if(${pass_length} GREATER 1)
4838      list(GET SECTION_PASS 0 pass_elem_0)
4839      if((NOT (${pass_elem_0} STREQUAL "NOT")) OR (${pass_length} GREATER 2))
4840        message(FATAL_ERROR "zephyr_linker_section(PASS takes maximum "
4841          "a single argument of the form: '<pass name>' or 'NOT <pass_name>'.")
4842      endif()
4843    endif()
4844  endif()
4845
4846  set(SECTION)
4847  zephyr_linker_arg_val_list(SECTION "${single_args}")
4848  zephyr_linker_arg_val_list(SECTION "${options}")
4849  zephyr_linker_arg_val_list(SECTION "${multi_args}")
4850
4851  string(REPLACE ";" "\;" SECTION "${SECTION}")
4852  set_property(TARGET linker
4853               APPEND PROPERTY SECTIONS "{${SECTION}}"
4854  )
4855endfunction()
4856
4857# Usage:
4858#   zephyr_linker_section_ifdef(<setting>
4859#                               NAME <name> [VMA <region>] [LMA <region>]
4860#                               [ADDRESS <address>] [ALIGN <alignment>]
4861#                               [SUBALIGN <alignment>] [FLAGS <flags>]
4862#                               [HIDDEN] [NOINPUT] [NOINIT]
4863#                               [PASS <no> [<no>...]
4864#   )
4865#
4866# Will create an output section if <setting> is enabled.
4867#
4868# <setting>: Setting to check for True value before invoking
4869#            zephyr_linker_section()
4870#
4871# See zephyr_linker_section() description for other supported arguments.
4872#
4873macro(zephyr_linker_section_ifdef feature_toggle)
4874  if(${${feature_toggle}})
4875    zephyr_linker_section(${ARGN})
4876  endif()
4877endmacro()
4878
4879# Usage:
4880#   zephyr_iterable_section(NAME <name> [GROUP <group>]
4881#                           [VMA <region|group>] [LMA <region|group>]
4882#                           [ALIGN_WITH_INPUT] [SUBALIGN <alignment>]
4883#   )
4884#
4885#
4886# Define an output section which will set up an iterable area
4887# of equally-sized data structures. For use with STRUCT_SECTION_ITERABLE.
4888# Input sections will be sorted by name in lexicographical order.
4889#
4890# Each list for an input section will define the following linker symbols:
4891# _<name>_list_start: Start of the iterable list
4892# _<name>_list_end  : End of the iterable list
4893#
4894# The output section will be named `<name>_area` and define the following linker
4895# symbols:
4896# __<name>_area_start      : Start address of the section
4897# __<name>_area_end        : End address of the section
4898# __<name>_area_size       : Size of the section
4899# __<name>_area_load_start : Load address of the section, if VMA = LMA then this
4900#                            value will be identical to `__<name>_area_start`
4901#
4902# NAME <name>         : Name of the struct type, the output section be named
4903#                       accordingly as: <name>_area.
4904# VMA <region|group>  : VMA Memory region where code / data is located runtime (VMA)
4905# LMA <region|group>  : Memory region where code / data is loaded (LMA)
4906#                       If VMA is different from LMA, the code / data will be loaded
4907#                       from LMA into VMA at bootup, this is usually the case for
4908#                       global or static variables that are loaded in rom and copied
4909#                       to ram at boot time.
4910# GROUP <group>       : Place this section inside the group <group>
4911# ADDRESS <address>   : Specific address to use for this section.
4912# ALIGN_WITH_INPUT    : The alignment difference between VMA and LMA is kept
4913#                       intact for this section.
4914# NUMERIC             : Use numeric sorting.
4915# SUBALIGN <alignment>: Force input alignment with size <alignment>
4916#  Note: Regarding all alignment attributes. Not all linkers may handle alignment
4917#        in identical way. For example the Scatter file will align both load and
4918#        execution address (LMA and VMA) to be aligned when given the ALIGN attribute.
4919#/
4920function(zephyr_iterable_section)
4921  # ToDo - Should we use ROM, RAM, etc as arguments ?
4922  set(options     "ALIGN_WITH_INPUT;NUMERIC")
4923  set(single_args "GROUP;LMA;NAME;SUBALIGN;VMA")
4924  set(multi_args  "")
4925  set(align_input)
4926  cmake_parse_arguments(SECTION "${options}" "${single_args}" "${multi_args}" ${ARGN})
4927
4928  if(NOT DEFINED SECTION_NAME)
4929    message(FATAL_ERROR "zephyr_iterable_section(${ARGV0} ...) missing "
4930                        "required argument: NAME"
4931    )
4932  endif()
4933
4934  if(NOT DEFINED SECTION_SUBALIGN)
4935    message(FATAL_ERROR "zephyr_iterable_section(${ARGV0} ...) missing "
4936                        "required argument: SUBALIGN"
4937    )
4938  endif()
4939
4940  if(SECTION_ALIGN_WITH_INPUT)
4941    set(align_input ALIGN_WITH_INPUT)
4942  endif()
4943
4944  if(SECTION_NUMERIC)
4945    set(INPUT "._${SECTION_NAME}.static.*_?_*;._${SECTION_NAME}.static.*_??_*")
4946  else()
4947    set(INPUT "._${SECTION_NAME}.static.*")
4948  endif()
4949
4950  zephyr_linker_section(
4951    NAME ${SECTION_NAME}_area
4952    GROUP "${SECTION_GROUP}"
4953    VMA "${SECTION_VMA}" LMA "${SECTION_LMA}"
4954    NOINPUT ${align_input} SUBALIGN ${SECTION_SUBALIGN}
4955  )
4956  zephyr_linker_section_configure(
4957    SECTION ${SECTION_NAME}_area
4958    INPUT "${INPUT}"
4959    SYMBOLS _${SECTION_NAME}_list_start _${SECTION_NAME}_list_end
4960    KEEP SORT NAME
4961  )
4962endfunction()
4963
4964# Usage:
4965#   zephyr_linker_section_obj_level(SECTION <section> LEVEL <level>)
4966#
4967# generate a symbol to mark the start of the objects array for
4968# the specified object and level, then link all of those objects
4969# (sorted by priority). Ensure the objects aren't discarded if there is
4970# no direct reference to them.
4971#
4972# This is useful content such as struct devices.
4973#
4974# For example: zephyr_linker_section_obj_level(SECTION init LEVEL PRE_KERNEL_1)
4975# will create an input section matching `.z_init_PRE_KERNEL_1?_` and
4976# `.z_init_PRE_KERNEL_1??_`.
4977#
4978# SECTION <section>: Section in which the objects shall be placed
4979# LEVEL <level>    : Priority level, all input sections matching the level
4980#                    will be sorted.
4981#
4982function(zephyr_linker_section_obj_level)
4983  set(single_args "SECTION;LEVEL")
4984  cmake_parse_arguments(OBJ "" "${single_args}" "" ${ARGN})
4985
4986  if(NOT DEFINED OBJ_SECTION)
4987    message(FATAL_ERROR "zephyr_linker_section_obj_level(${ARGV0} ...) "
4988                        "missing required argument: SECTION"
4989    )
4990  endif()
4991
4992  if(NOT DEFINED OBJ_LEVEL)
4993    message(FATAL_ERROR "zephyr_linker_section_obj_level(${ARGV0} ...) "
4994                        "missing required argument: LEVEL"
4995    )
4996  endif()
4997
4998  zephyr_linker_section_configure(
4999    SECTION ${OBJ_SECTION}
5000    INPUT ".z_${OBJ_SECTION}_${OBJ_LEVEL}?_*"
5001    SYMBOLS __${OBJ_SECTION}_${OBJ_LEVEL}_start
5002    KEEP SORT NAME
5003  )
5004  zephyr_linker_section_configure(
5005    SECTION ${OBJ_SECTION}
5006    INPUT ".z_${OBJ_SECTION}_${OBJ_LEVEL}??_*"
5007    KEEP SORT NAME
5008  )
5009endfunction()
5010
5011# Usage:
5012#   zephyr_linker_section_configure(SECTION <section> [ALIGN <alignment>]
5013#                                   [PASS [NOT] <name>] [PRIO <no>] [SORT <sort>]
5014#                                   [ANY] [FIRST] [KEEP]
5015#   )
5016#
5017# Configure an output section with additional input sections.
5018# An output section can be configured with additional input sections besides its
5019# default section.
5020# For example, to add the input section `foo` to the output section bar, with KEEP
5021# attribute, call:
5022#   zephyr_linker_section_configure(SECTION bar INPUT foo KEEP)
5023#
5024# ALIGN <alignment>   : Will align the input section placement inside the load
5025#                       region with <alignment>
5026# FIRST               : The first input section in the list should be marked as
5027#                       first section in output.
5028# SORT <NAME>         : Sort the input sections according to <type>.
5029#                       Currently only `NAME` is supported.
5030# KEEP                : Do not eliminate input section during linking
5031# PRIO                : The priority of the input section. Per default, input
5032#                       sections order is not guaranteed by all linkers, but
5033#                       using priority Zephyr CMake linker will create sections
5034#                       such that order can be guaranteed. All unprioritized
5035#                       sections will internally be given a CMake process order
5036#                       priority counting from 100, so first unprioritized section
5037#                       is handled internal prio 100, next 101, and so on.
5038#                       To ensure a specific input section come before those,
5039#                       you may use `PRIO 50`, `PRIO 20` and so on.
5040#                       To ensure an input section is at the end, it is advised
5041#                       to use `PRIO 200` and above.
5042# PASS [NOT] <name>   : The call should only be considered for linker pass where
5043#                       <name> is defined. It is possible to negate <name>, such
5044#                       as `PASS NOT <name>.
5045#                       For example, `PASS TEST` means the call is only effective
5046#                       on the `TEST` linker pass iteration. `PASS NOT TEST` on
5047#                       all iterations the are not `TEST`.
5048# FLAGS <flags>       : Special section flags such as "+RO", +XO, "+ZI".
5049# ANY                 : ANY section flag in scatter file.
5050#                       The FLAGS and ANY arguments only has effect for scatter files.
5051#
5052function(zephyr_linker_section_configure)
5053  set(options     "ANY;FIRST;KEEP")
5054  set(single_args "ALIGN;OFFSET;PRIO;SECTION;SORT")
5055  set(multi_args  "FLAGS;INPUT;PASS;SYMBOLS")
5056  cmake_parse_arguments(SECTION "${options}" "${single_args}" "${multi_args}" ${ARGN})
5057
5058  if(SECTION_UNPARSED_ARGUMENTS)
5059    message(FATAL_ERROR "zephyr_linker_section_configure(${ARGV0} ...) given unknown arguments: ${SECTION_UNPARSED_ARGUMENTS}")
5060  endif()
5061
5062  if(DEFINED SECTION_SYMBOLS)
5063    list(LENGTH SECTION_SYMBOLS symbols_count)
5064    if(${symbols_count} GREATER 2)
5065      message(FATAL_ERROR "zephyr_linker_section_configure(SYMBOLS [start_sym [end_sym]]) takes maximum two symbol names (start and end).")
5066
5067    endif()
5068  endif()
5069
5070  if(DEFINED SECTION_PASS)
5071    list(LENGTH SECTION_PASS pass_length)
5072    if(${pass_length} GREATER 1)
5073      list(GET SECTION_PASS 0 pass_elem_0)
5074      if((NOT (${pass_elem_0} STREQUAL "NOT")) OR (${pass_length} GREATER 2))
5075        message(FATAL_ERROR "zephyr_linker_section_configure(PASS takes maximum "
5076          "a single argument of the form: '<pass name>' or 'NOT <pass_name>'.")
5077      endif()
5078    endif()
5079  endif()
5080
5081  set(SECTION)
5082  zephyr_linker_arg_val_list(SECTION "${single_args}")
5083  zephyr_linker_arg_val_list(SECTION "${options}")
5084  zephyr_linker_arg_val_list(SECTION "${multi_args}")
5085
5086  string(REPLACE ";" "\;" SECTION "${SECTION}")
5087  set_property(TARGET linker
5088               APPEND PROPERTY SECTION_SETTINGS "{${SECTION}}"
5089  )
5090endfunction()
5091
5092# Usage:
5093#   zephyr_linker_symbol(SYMBOL <name> EXPR <expr>)
5094#
5095# Add additional user defined symbol to the generated linker script.
5096#
5097# SYMBOL <name>: Symbol name to be available.
5098# EXPR <expr>  : Expression that defines the symbol. Due to linker limitations
5099#                all expressions should only contain simple math, such as
5100#                `+, -, *` and similar. The expression will go directly into the
5101#                linker, and all `@<symbol>@` will be replaced with the referred
5102#                symbol.
5103#
5104# Example:
5105#   To create a new symbol `bar` pointing to the start VMA address of section
5106#   `foo` + 1024, one can write:
5107#     zephyr_linker_symbol(SYMBOL bar EXPR "(@foo@ + 1024)")
5108#
5109function(zephyr_linker_symbol)
5110  set(single_args "EXPR;SYMBOL")
5111  cmake_parse_arguments(SYMBOL "" "${single_args}" "" ${ARGN})
5112
5113  if(SECTION_UNPARSED_ARGUMENTS)
5114    message(WARNING "zephyr_linker_symbol(${ARGV0} ...) given unknown "
5115                    "arguments: ${SECTION_UNPARSED_ARGUMENTS}"
5116    )
5117  endif()
5118
5119  set(SYMBOL)
5120  zephyr_linker_arg_val_list(SYMBOL "${single_args}")
5121
5122  string(REPLACE ";" "\;" SYMBOL "${SYMBOL}")
5123  set_property(TARGET linker
5124               APPEND PROPERTY SYMBOLS "{${SYMBOL}}"
5125  )
5126endfunction()
5127
5128# Internal helper macro for zephyr_linker*() functions.
5129# The macro will create a list of argument-value pairs for defined arguments
5130# that can be passed on to linker script generators and processed as a CMake
5131# function call using cmake_parse_arguments.
5132#
5133# For example, having the following argument and value:
5134# FOO: bar
5135# BAZ: <undefined>
5136# QUX: option set
5137#
5138# will create a list as: "FOO;bar;QUX;TRUE" which can then be parsed as argument
5139# list later.
5140macro(zephyr_linker_arg_val_list list arguments)
5141  foreach(arg ${arguments})
5142    if(DEFINED ${list}_${arg})
5143      list(APPEND ${list} ${arg} "${${list}_${arg}}")
5144    endif()
5145  endforeach()
5146endmacro()
5147
5148########################################################
5149# 6. Function helper macros
5150########################################################
5151#
5152# Set of CMake macros to facilitate argument processing when defining functions.
5153#
5154
5155#
5156# Helper macro for verifying that at least one of the required arguments has
5157# been provided by the caller.
5158#
5159# A FATAL_ERROR will be raised if not one of the required arguments has been
5160# passed by the caller.
5161#
5162# Usage:
5163#   zephyr_check_arguments_required(<function_name> <prefix> <arg1> [<arg2> ...])
5164#
5165macro(zephyr_check_arguments_required function prefix)
5166  set(check_defined DEFINED)
5167  zephyr_check_flags_required(${function} ${prefix} ${ARGN})
5168  set(check_defined)
5169endmacro()
5170
5171#
5172# Helper macro for verifying that at least one of the required flags has
5173# been provided by the caller.
5174#
5175# A FATAL_ERROR will be raised if not one of the required arguments has been
5176# passed by the caller.
5177#
5178# Usage:
5179#   zephyr_check_flags_required(<function_name> <prefix> <flag1> [<flag2> ...])
5180#
5181macro(zephyr_check_flags_required function prefix)
5182  set(required_found FALSE)
5183  foreach(required ${ARGN})
5184    if(${check_defined} ${prefix}_${required})
5185      set(required_found TRUE)
5186    endif()
5187  endforeach()
5188
5189  if(NOT required_found)
5190    message(FATAL_ERROR "${function}(...) missing a required argument: ${ARGN}")
5191  endif()
5192endmacro()
5193
5194#
5195# Helper macro for verifying that all the required arguments have been
5196# provided by the caller.
5197#
5198# A FATAL_ERROR will be raised if one of the required arguments is missing.
5199#
5200# Usage:
5201#   zephyr_check_arguments_required_all(<function_name> <prefix> <arg1> [<arg2> ...])
5202#
5203macro(zephyr_check_arguments_required_all function prefix)
5204  foreach(required ${ARGN})
5205    if(NOT DEFINED ${prefix}_${required})
5206      message(FATAL_ERROR "${function}(...) missing a required argument: ${required}")
5207    endif()
5208  endforeach()
5209endmacro()
5210
5211#
5212# Helper macro for verifying that none of the mutual exclusive arguments are
5213# provided together.
5214#
5215# A FATAL_ERROR will be raised if any of the arguments are given together.
5216#
5217# Usage:
5218#   zephyr_check_arguments_exclusive(<function_name> <prefix> <arg1> <arg2> [<arg3> ...])
5219#
5220macro(zephyr_check_arguments_exclusive function prefix)
5221  set(check_defined DEFINED)
5222  zephyr_check_flags_exclusive(${function} ${prefix} ${ARGN})
5223  set(check_defined)
5224endmacro()
5225
5226#
5227# Helper macro for verifying that none of the mutual exclusive flags are
5228# provided together.
5229#
5230# A FATAL_ERROR will be raised if any of the flags are given together.
5231#
5232# Usage:
5233#   zephyr_check_flags_exclusive(<function_name> <prefix> <flag1> <flag2> [<flag3> ...])
5234#
5235macro(zephyr_check_flags_exclusive function prefix)
5236  set(args_defined)
5237  foreach(arg ${ARGN})
5238    if(${check_defined} ${prefix}_${arg})
5239      list(APPEND args_defined ${arg})
5240    endif()
5241  endforeach()
5242  list(LENGTH args_defined exclusive_length)
5243  if(exclusive_length GREATER 1)
5244    list(POP_FRONT args_defined argument)
5245    message(FATAL_ERROR "${function}(${argument} ...) cannot be used with "
5246        "argument: ${args_defined}"
5247      )
5248  endif()
5249endmacro()
5250
5251########################################################
5252# 7. Linkable loadable extensions (llext)
5253########################################################
5254#
5255# These functions simplify the creation and management of linkable
5256# loadable extensions (llexts).
5257#
5258
5259# 7.1 Configuration functions
5260#
5261# The following functions simplify access to the compilation/link stage
5262# properties of an llext using the same API of the target_* functions.
5263#
5264
5265function(llext_compile_definitions target_name)
5266  target_compile_definitions(${target_name}_llext_lib PRIVATE ${ARGN})
5267endfunction()
5268
5269function(llext_compile_features target_name)
5270  target_compile_features(${target_name}_llext_lib PRIVATE ${ARGN})
5271endfunction()
5272
5273function(llext_compile_options target_name)
5274  target_compile_options(${target_name}_llext_lib PRIVATE ${ARGN})
5275endfunction()
5276
5277function(llext_include_directories target_name)
5278  target_include_directories(${target_name}_llext_lib PRIVATE ${ARGN})
5279endfunction()
5280
5281function(llext_link_options target_name)
5282  target_link_options(${target_name}_llext_lib PRIVATE ${ARGN})
5283endfunction()
5284
5285# 7.2 Build control functions
5286#
5287# The following functions add targets and subcommands to the build system
5288# to compile and link an llext.
5289#
5290
5291# Usage:
5292#   add_llext_target(<target_name>
5293#                    OUTPUT  <output_file>
5294#                    SOURCES <source_files>
5295#   )
5296#
5297# Add a custom target that compiles a set of source files to a .llext file.
5298#
5299# Output and source files must be specified using the OUTPUT and SOURCES
5300# arguments. Only one source file is supported when LLEXT_TYPE_ELF_OBJECT is
5301# selected, since there is no linking step in that case.
5302#
5303# The llext code will be compiled with mostly the same C compiler flags used
5304# in the Zephyr build, but with some important modifications. The list of
5305# flags to remove and flags to append is controlled respectively by the
5306# LLEXT_REMOVE_FLAGS and LLEXT_APPEND_FLAGS global variables.
5307#
5308# The following custom properties of <target_name> are defined and can be
5309# retrieved using the get_target_property() function:
5310#
5311# - lib_target  Target name for the source compilation and/or link step.
5312# - lib_output  The binary file resulting from compilation and/or
5313#               linking steps.
5314# - pkg_input   The file to be used as input for the packaging step.
5315# - pkg_output  The final .llext file.
5316#
5317# Example usage:
5318#   add_llext_target(hello_world
5319#     OUTPUT  ${PROJECT_BINARY_DIR}/hello_world.llext
5320#     SOURCES ${PROJECT_SOURCE_DIR}/src/llext/hello_world.c
5321#   )
5322# will compile the source file src/llext/hello_world.c to a file
5323# named "${PROJECT_BINARY_DIR}/hello_world.llext".
5324#
5325function(add_llext_target target_name)
5326  set(single_args OUTPUT)
5327  set(multi_args SOURCES)
5328  cmake_parse_arguments(PARSE_ARGV 1 LLEXT "${options}" "${single_args}" "${multi_args}")
5329
5330  # Check that the llext subsystem is enabled for this build
5331  if (NOT CONFIG_LLEXT)
5332    message(FATAL_ERROR "add_llext_target: CONFIG_LLEXT must be enabled")
5333  endif()
5334
5335  # Source and output files must be provided
5336  zephyr_check_arguments_required_all("add_llext_target" LLEXT OUTPUT SOURCES)
5337
5338  list(LENGTH LLEXT_SOURCES source_count)
5339  if(CONFIG_LLEXT_TYPE_ELF_OBJECT AND NOT (source_count EQUAL 1))
5340    message(FATAL_ERROR "add_llext_target: only one source file is supported "
5341                        "for ELF object file builds")
5342  endif()
5343
5344  set(llext_pkg_output ${LLEXT_OUTPUT})
5345  set(source_files ${LLEXT_SOURCES})
5346
5347  set(zephyr_flags
5348      "$<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_OPTIONS>"
5349  )
5350  llext_filter_zephyr_flags(LLEXT_REMOVE_FLAGS ${zephyr_flags}
5351      zephyr_filtered_flags)
5352
5353  # Compile the source file using current Zephyr settings but a different
5354  # set of flags to obtain the desired llext object type.
5355  set(llext_lib_target ${target_name}_llext_lib)
5356  if(CONFIG_LLEXT_TYPE_ELF_OBJECT)
5357
5358    # Create an object library to compile the source file
5359    add_library(${llext_lib_target} OBJECT ${source_files})
5360    set(llext_lib_output $<TARGET_OBJECTS:${llext_lib_target}>)
5361
5362  elseif(CONFIG_LLEXT_TYPE_ELF_RELOCATABLE)
5363
5364    # CMake does not directly support a "RELOCATABLE" library target.
5365    # The "SHARED" target would be similar, but that unavoidably adds
5366    # a "-shared" flag to the linker command line which does firmly
5367    # conflict with "-r".
5368    # A workaround is to use an executable target and make the linker
5369    # output a relocatable file. The output file suffix is changed so
5370    # the result looks like the object file it actually is.
5371    add_executable(${llext_lib_target} EXCLUDE_FROM_ALL ${source_files})
5372    target_link_options(${llext_lib_target} PRIVATE
5373      $<TARGET_PROPERTY:linker,partial_linking>)
5374    set_target_properties(${llext_lib_target} PROPERTIES
5375      SUFFIX ${CMAKE_C_OUTPUT_EXTENSION})
5376    set(llext_lib_output $<TARGET_FILE:${llext_lib_target}>)
5377
5378    # Add the llext flags to the linking step as well
5379    target_link_options(${llext_lib_target} PRIVATE
5380      ${LLEXT_APPEND_FLAGS}
5381    )
5382
5383  elseif(CONFIG_LLEXT_TYPE_ELF_SHAREDLIB)
5384
5385    # Create a shared library
5386    add_library(${llext_lib_target} SHARED ${source_files})
5387    set(llext_lib_output $<TARGET_FILE:${llext_lib_target}>)
5388
5389    # Add the llext flags to the linking step as well
5390    target_link_options(${llext_lib_target} PRIVATE
5391      ${LLEXT_APPEND_FLAGS}
5392    )
5393
5394  endif()
5395
5396  target_compile_definitions(${llext_lib_target} PRIVATE
5397    $<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_DEFINITIONS>
5398    LL_EXTENSION_BUILD
5399  )
5400  target_compile_options(${llext_lib_target} PRIVATE
5401    ${zephyr_filtered_flags}
5402    ${LLEXT_APPEND_FLAGS}
5403  )
5404  target_include_directories(${llext_lib_target} PRIVATE
5405    $<TARGET_PROPERTY:zephyr_interface,INTERFACE_INCLUDE_DIRECTORIES>
5406  )
5407  target_include_directories(${llext_lib_target} SYSTEM PUBLIC
5408    $<TARGET_PROPERTY:zephyr_interface,INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>
5409  )
5410  add_dependencies(${llext_lib_target}
5411    zephyr_interface
5412    zephyr_generated_headers
5413  )
5414
5415  # Set up an intermediate processing step between compilation and packaging
5416  # to be used to support POST_BUILD commands on targets that do not use a
5417  # dynamic library.
5418  set(llext_proc_target ${target_name}_llext_proc)
5419  set(llext_pkg_input ${PROJECT_BINARY_DIR}/${target_name}.llext.pkg_input)
5420  add_custom_target(${llext_proc_target} DEPENDS ${llext_pkg_input})
5421  set_property(TARGET ${llext_proc_target} PROPERTY has_post_build_cmds 0)
5422
5423  # By default this target must copy the `lib_output` binary file to the
5424  # expected `pkg_input` location. If actual POST_BUILD commands are defined,
5425  # they will take care of this and the default copy is replaced by a no-op.
5426  set(has_post_build_cmds "$<TARGET_PROPERTY:${llext_proc_target},has_post_build_cmds>")
5427  set(noop_cmd ${CMAKE_COMMAND} -E true)
5428  set(copy_cmd ${CMAKE_COMMAND} -E copy ${llext_lib_output} ${llext_pkg_input})
5429  add_custom_command(
5430    OUTPUT ${llext_pkg_input}
5431    COMMAND "$<IF:${has_post_build_cmds},${noop_cmd},${copy_cmd}>"
5432    DEPENDS ${llext_lib_target} ${llext_lib_output}
5433    COMMAND_EXPAND_LISTS
5434  )
5435
5436  # LLEXT ELF processing for importing via SLID
5437  #
5438  # This command must be executed as last step of the packaging process,
5439  # to ensure that the ELF processed for binary generation contains SLIDs.
5440  # If executed too early, it is possible that some tools executed to modify
5441  # the ELF file (e.g., strip) undo the work performed here.
5442  if (CONFIG_LLEXT_EXPORT_BUILTINS_BY_SLID)
5443    set(slid_inject_cmd
5444      ${PYTHON_EXECUTABLE}
5445      ${ZEPHYR_BASE}/scripts/build/llext_inject_slids.py
5446      --elf-file ${llext_pkg_output}
5447    )
5448  else()
5449    set(slid_inject_cmd ${CMAKE_COMMAND} -E true)
5450  endif()
5451
5452  # Type-specific packaging of the built binary file into an .llext file
5453  if(CONFIG_LLEXT_TYPE_ELF_OBJECT)
5454
5455    # No packaging required, simply copy the object file
5456    add_custom_command(
5457      OUTPUT ${llext_pkg_output}
5458      COMMAND ${CMAKE_COMMAND} -E copy ${llext_pkg_input} ${llext_pkg_output}
5459      COMMAND ${slid_inject_cmd}
5460      DEPENDS ${llext_proc_target} ${llext_pkg_input}
5461    )
5462
5463  elseif(CONFIG_LLEXT_TYPE_ELF_RELOCATABLE)
5464
5465    # Need to remove just some sections from the relocatable object
5466    # (using strip in this case would remove _all_ symbols)
5467    add_custom_command(
5468      OUTPUT ${llext_pkg_output}
5469      COMMAND $<TARGET_PROPERTY:bintools,elfconvert_command>
5470              $<TARGET_PROPERTY:bintools,elfconvert_flag>
5471              $<TARGET_PROPERTY:bintools,elfconvert_flag_section_remove>.xt.*
5472              $<TARGET_PROPERTY:bintools,elfconvert_flag_infile>${llext_pkg_input}
5473              $<TARGET_PROPERTY:bintools,elfconvert_flag_outfile>${llext_pkg_output}
5474              $<TARGET_PROPERTY:bintools,elfconvert_flag_final>
5475      COMMAND ${slid_inject_cmd}
5476      DEPENDS ${llext_proc_target} ${llext_pkg_input}
5477    )
5478
5479  elseif(CONFIG_LLEXT_TYPE_ELF_SHAREDLIB)
5480
5481    # Need to strip the shared library of some sections
5482    add_custom_command(
5483      OUTPUT ${llext_pkg_output}
5484      COMMAND $<TARGET_PROPERTY:bintools,strip_command>
5485              $<TARGET_PROPERTY:bintools,strip_flag>
5486              $<TARGET_PROPERTY:bintools,strip_flag_remove_section>.xt.*
5487              $<TARGET_PROPERTY:bintools,strip_flag_infile>${llext_pkg_input}
5488              $<TARGET_PROPERTY:bintools,strip_flag_outfile>${llext_pkg_output}
5489              $<TARGET_PROPERTY:bintools,strip_flag_final>
5490      COMMAND ${slid_inject_cmd}
5491      DEPENDS ${llext_proc_target} ${llext_pkg_input}
5492    )
5493
5494  endif()
5495
5496  # Add user-visible target and dependency, and fill in properties
5497  get_filename_component(output_name ${llext_pkg_output} NAME)
5498  add_custom_target(${target_name}
5499    COMMENT "Generating ${output_name}"
5500    DEPENDS ${llext_pkg_output}
5501  )
5502  set_target_properties(${target_name} PROPERTIES
5503    lib_target ${llext_lib_target}
5504    lib_output ${llext_lib_output}
5505    pkg_input  ${llext_pkg_input}
5506    pkg_output ${llext_pkg_output}
5507  )
5508endfunction()
5509
5510# Usage:
5511#   add_llext_command(
5512#     TARGET <target_name>
5513#     PRE_BUILD | POST_BUILD | POST_PKG
5514#     COMMAND <command> [...]
5515#   )
5516#
5517# Add a custom command to an llext target that will be executed during
5518# the build. The command will be executed at the specified build step and
5519# can refer to <target>'s properties for build-specific details.
5520#
5521# The different build steps are:
5522# - PRE_BUILD:  Before the llext code is linked, if the architecture uses
5523#               dynamic libraries. This step can access `lib_target` and
5524#               its own properties.
5525# - POST_BUILD: After the llext code is built, but before packaging
5526#               it in an .llext file. This step is expected to create a
5527#               `pkg_input` file by reading the contents of `lib_output`.
5528# - POST_PKG:   After the .llext file has been created. This can operate on
5529#               the final llext file `pkg_output`.
5530#
5531# Anything else after COMMAND will be passed to add_custom_command() as-is
5532# (including multiple commands and other options).
5533function(add_llext_command)
5534  set(options     PRE_BUILD POST_BUILD POST_PKG)
5535  set(single_args TARGET)
5536  # COMMAND and other options are passed to add_custom_command() as-is
5537
5538  cmake_parse_arguments(PARSE_ARGV 0 LLEXT "${options}" "${single_args}" "${multi_args}")
5539  zephyr_check_arguments_required_all("add_llext_command" LLEXT TARGET)
5540
5541  # Check the target exists and refers to an llext target
5542  set(target_name ${LLEXT_TARGET})
5543  set(llext_lib_target  ${target_name}_llext_lib)
5544  set(llext_proc_target ${target_name}_llext_proc)
5545  if(NOT TARGET ${llext_lib_target})
5546    message(FATAL_ERROR "add_llext_command: not an llext target: ${target_name}")
5547  endif()
5548
5549  # ARM uses an object file representation so there is no link step.
5550  if(CONFIG_ARM AND LLEXT_PRE_BUILD)
5551    message(FATAL_ERROR
5552	    "add_llext_command: PRE_BUILD not supported on this arch")
5553  endif()
5554
5555  # Determine the build step and the target to attach the command to
5556  # based on the provided options
5557  if(LLEXT_PRE_BUILD)
5558    # > before the object files are linked:
5559    #   - execute user command(s) before the lib target's link step.
5560    set(cmd_target ${llext_lib_target})
5561    set(build_step PRE_LINK)
5562  elseif(LLEXT_POST_BUILD)
5563    # > after linking, but before llext packaging:
5564    #   - stop default file copy to prevent user files from being clobbered;
5565    #   - execute user command(s) after the (now empty) `llext_proc_target`.
5566    set_property(TARGET ${llext_proc_target} PROPERTY has_post_build_cmds 1)
5567    set(cmd_target ${llext_proc_target})
5568    set(build_step POST_BUILD)
5569  elseif(LLEXT_POST_PKG)
5570    # > after the final llext binary is ready:
5571    #   - execute user command(s) after the main target is done.
5572    set(cmd_target ${target_name})
5573    set(build_step POST_BUILD)
5574  else()
5575    message(FATAL_ERROR "add_llext_command: build step must be provided")
5576  endif()
5577
5578  # Check that the first unparsed argument is the word COMMAND
5579  list(GET LLEXT_UNPARSED_ARGUMENTS 0 command_str)
5580  if(NOT command_str STREQUAL "COMMAND")
5581    message(FATAL_ERROR "add_llext_command: COMMAND argument must be provided")
5582  endif()
5583
5584  # Add the actual command(s) to the target
5585  add_custom_command(
5586    TARGET ${cmd_target} ${build_step}
5587    ${LLEXT_UNPARSED_ARGUMENTS}
5588    COMMAND_EXPAND_LISTS
5589  )
5590endfunction()
5591
5592# 7.3 llext helper functions
5593
5594# Usage:
5595#   llext_filter_zephyr_flags(<filter> <flags> <outvar>)
5596#
5597# Filter out flags from a list of flags. The filter is a list of regular
5598# expressions that will be used to exclude flags from the input list.
5599#
5600# The resulting generator expression will be stored in the variable <outvar>.
5601#
5602# Example:
5603#   llext_filter_zephyr_flags(LLEXT_REMOVE_FLAGS zephyr_flags zephyr_filtered_flags)
5604#
5605function(llext_filter_zephyr_flags filter flags outvar)
5606  list(TRANSFORM ${filter}
5607       REPLACE "(.+)" "^\\1$"
5608       OUTPUT_VARIABLE llext_remove_flags_regexp
5609  )
5610  list(JOIN llext_remove_flags_regexp "|" llext_remove_flags_regexp)
5611  if ("${llext_remove_flags_regexp}" STREQUAL "")
5612    # an empty regexp would match anything, we actually need the opposite
5613    # so set it to match empty strings
5614    set(llext_remove_flags_regexp "^$")
5615  endif()
5616
5617  set(zephyr_filtered_flags
5618      "$<FILTER:${flags},EXCLUDE,${llext_remove_flags_regexp}>"
5619  )
5620
5621  set(${outvar} ${zephyr_filtered_flags} PARENT_SCOPE)
5622endfunction()
5623