blob: c6f821ad2d1fe0f336cb048e50ddf0b398344ad7 [file] [log] [blame]
bin.chen1a426d32021-10-13 10:52:36 +08001# SPDX-License-Identifier: Apache-2.0
2
3########################################################
4# Table of contents
5########################################################
6# 1. Amlogic-aware extensions
7# 1.1. aml_*
8# 1.2. aml_library_*
9# 1.2.1 aml_interface_library_*
10# 1.3. generate_inc_*
11# 1.4. board_*
12# 1.5. Misc.
13# 2. Kconfig-aware extensions
14# 2.1 *_if_kconfig
15# 2.2 Misc
16# 3. CMake-generic extensions
17# 3.1. *_ifdef
18# 3.2. *_ifndef
19# 3.3. *_option compiler compatibility checks
20# 3.4. Debugging CMake
21# 3.5. File system management
22
23########################################################
24# 1. Amlogic-aware extensions
25########################################################
26# 1.1. aml_*
27#
28# The following methods are for modifying the CMake library[0] called
29# "aml". aml is a catch-all CMake library for source files that
30# can be built purely with the include paths, defines, and other
31# compiler flags that all aml source files use.
32# [0] https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html
33#
34# Example usage:
35# aml_sources(
36# random_esp32.c
37# utils.c
38# )
39#
40# Is short for:
41# target_sources(aml PRIVATE
42# ${CMAKE_CURRENT_SOURCE_DIR}/random_esp32.c
43# ${CMAKE_CURRENT_SOURCE_DIR}/utils.c
44# )
45
46# https://cmake.org/cmake/help/latest/command/target_sources.html
47function(aml_sources)
48 foreach(arg ${ARGV})
49 if(IS_ABSOLUTE ${arg})
50 set(path ${arg})
51 else()
52 set(path ${CMAKE_CURRENT_SOURCE_DIR}/${arg})
53 endif()
54
55 if(IS_DIRECTORY ${path})
56 message(FATAL_ERROR "aml_sources() was called on a directory")
57 endif()
58
59 target_sources(${TARGET_NAME} PRIVATE ${path})
60 endforeach()
61endfunction()
62
63# https://cmake.org/cmake/help/latest/command/target_include_directories.html
64function(aml_include_directories)
65 foreach(arg ${ARGV})
66 if(IS_ABSOLUTE ${arg})
67 set(path ${arg})
68 else()
69 set(path ${CMAKE_CURRENT_SOURCE_DIR}/${arg})
70 endif()
71 target_include_directories(aml_interface INTERFACE ${path})
72 endforeach()
73endfunction()
74
75# https://cmake.org/cmake/help/latest/command/target_include_directories.html
76function(aml_system_include_directories)
77 foreach(arg ${ARGV})
78 if(IS_ABSOLUTE ${arg})
79 set(path ${arg})
80 else()
81 set(path ${CMAKE_CURRENT_SOURCE_DIR}/${arg})
82 endif()
83 target_include_directories(aml_interface SYSTEM INTERFACE ${path})
84 endforeach()
85endfunction()
86
87# https://cmake.org/cmake/help/latest/command/target_compile_definitions.html
88function(aml_compile_definitions)
89 target_compile_definitions(aml_interface INTERFACE ${ARGV})
90endfunction()
91
92# https://cmake.org/cmake/help/latest/command/target_compile_options.html
93function(aml_compile_options)
94 target_compile_options(aml_interface INTERFACE ${ARGV})
95endfunction()
96
97# https://cmake.org/cmake/help/latest/command/target_link_libraries.html
98function(aml_link_libraries)
99 target_link_libraries(${TARGET_NAME} ${CURRENT_LIBRARY})
100endfunction()
101
102function(aml_link_interfaces)
103 target_link_libraries(${TARGET_NAME} ${CURRENT_INTERFACE})
104endfunction()
105
106# See this file section 3.1. target_cc_option
107function(aml_cc_option)
108 foreach(arg ${ARGV})
109 target_cc_option(aml_interface INTERFACE ${arg})
110 endforeach()
111endfunction()
112
113function(aml_cc_option_fallback option1 option2)
114 target_cc_option_fallback(aml_interface INTERFACE ${option1} ${option2})
115endfunction()
116
117function(aml_ld_options)
118 target_ld_options(aml_interface INTERFACE ${ARGV})
119endfunction()
120
121# Getter functions for extracting build information from
122# aml_interface. Returning lists, and strings is supported, as is
123# requesting specific categories of build information (defines,
124# includes, options).
125#
126# The naming convention follows:
127# aml_get_${build_information}_for_lang${format}(lang x [SKIP_PREFIX])
128# Where
129# the argument 'x' is written with the result
130# and
131# ${build_information} can be one of
132# - include_directories # -I directories
133# - system_include_directories # -isystem directories
134# - compile_definitions # -D'efines
135# - compile_options # misc. compiler flags
136# and
137# ${format} can be
138# - the empty string '', signifying that it should be returned as a list
139# - _as_string signifying that it should be returned as a string
140# and
141# ${lang} can be one of
142# - C
143# - CXX
144# - ASM
145#
146# SKIP_PREFIX
147#
148# By default the result will be returned ready to be passed directly
149# to a compiler, e.g. prefixed with -D, or -I, but it is possible to
150# omit this prefix by specifying 'SKIP_PREFIX' . This option has no
151# effect for 'compile_options'.
152#
153# e.g.
154# aml_get_include_directories_for_lang(ASM x)
155# writes "-Isome_dir;-Isome/other/dir" to x
156
157function(aml_get_include_directories_for_lang_as_string lang i)
158 aml_get_include_directories_for_lang(${lang} list_of_flags ${ARGN})
159
160 convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags)
161
162 set(${i} ${str_of_flags} PARENT_SCOPE)
163endfunction()
164
165function(aml_get_system_include_directories_for_lang_as_string lang i)
166 aml_get_system_include_directories_for_lang(${lang} list_of_flags ${ARGN})
167
168 convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags)
169
170 set(${i} ${str_of_flags} PARENT_SCOPE)
171endfunction()
172
173function(aml_get_compile_definitions_for_lang_as_string lang i)
174 aml_get_compile_definitions_for_lang(${lang} list_of_flags ${ARGN})
175
176 convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags)
177
178 set(${i} ${str_of_flags} PARENT_SCOPE)
179endfunction()
180
181function(aml_get_compile_options_for_lang_as_string lang i)
182 aml_get_compile_options_for_lang(${lang} list_of_flags)
183
184 convert_list_of_flags_to_string_of_flags(list_of_flags str_of_flags)
185
186 set(${i} ${str_of_flags} PARENT_SCOPE)
187endfunction()
188
189function(aml_get_include_directories_for_lang lang i)
190 get_property_and_add_prefix(flags aml_interface INTERFACE_INCLUDE_DIRECTORIES
191 "-I"
192 ${ARGN}
193 )
194
195 process_flags(${lang} flags output_list)
196
197 set(${i} ${output_list} PARENT_SCOPE)
198endfunction()
199
200function(aml_get_system_include_directories_for_lang lang i)
201 get_property_and_add_prefix(flags aml_interface INTERFACE_SYSTEM_INCLUDE_DIRECTORIES
202 "-isystem"
203 ${ARGN}
204 )
205
206 process_flags(${lang} flags output_list)
207
208 set(${i} ${output_list} PARENT_SCOPE)
209endfunction()
210
211function(aml_get_compile_definitions_for_lang lang i)
212 get_property_and_add_prefix(flags aml_interface INTERFACE_COMPILE_DEFINITIONS
213 "-D"
214 ${ARGN}
215 )
216
217 process_flags(${lang} flags output_list)
218
219 set(${i} ${output_list} PARENT_SCOPE)
220endfunction()
221
222function(aml_get_compile_options_for_lang lang i)
223 get_property(flags TARGET aml_interface PROPERTY INTERFACE_COMPILE_OPTIONS)
224
225 process_flags(${lang} flags output_list)
226
227 set(${i} ${output_list} PARENT_SCOPE)
228endfunction()
229
230# This function writes a dict to it's output parameter
231# 'return_dict'. The dict has information about the parsed arguments,
232#
233# Usage:
234# aml_get_parse_args(foo ${ARGN})
235# print(foo_STRIP_PREFIX) # foo_STRIP_PREFIX might be set to 1
236function(aml_get_parse_args return_dict)
237 foreach(x ${ARGN})
238 if(x STREQUAL STRIP_PREFIX)
239 set(${return_dict}_STRIP_PREFIX 1 PARENT_SCOPE)
240 endif()
241 endforeach()
242endfunction()
243
244function(process_flags lang input output)
245 # The flags might contains compile language generator expressions that
246 # look like this:
247 # $<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions>
248 #
249 # Flags that don't specify a language like this apply to all
250 # languages.
251 #
252 # See COMPILE_LANGUAGE in
253 # https://cmake.org/cmake/help/v3.3/manual/cmake-generator-expressions.7.html
254 #
255 # To deal with this, we apply a regex to extract the flag and also
256 # to find out if the language matches.
257 #
258 # If this doesn't work out we might need to ban the use of
259 # COMPILE_LANGUAGE and instead partition C, CXX, and ASM into
260 # different libraries
261 set(languages C CXX ASM)
262
263 set(tmp_list "")
264
265 foreach(flag ${${input}})
266 set(is_compile_lang_generator_expression 0)
267 foreach(l ${languages})
268 if(flag MATCHES "<COMPILE_LANGUAGE:${l}>:([^>]+)>")
269 set(is_compile_lang_generator_expression 1)
270 if(${l} STREQUAL ${lang})
271 list(APPEND tmp_list ${CMAKE_MATCH_1})
272 break()
273 endif()
274 endif()
275 endforeach()
276
277 if(NOT is_compile_lang_generator_expression)
278 list(APPEND tmp_list ${flag})
279 endif()
280 endforeach()
281
282 set(${output} ${tmp_list} PARENT_SCOPE)
283endfunction()
284
285function(convert_list_of_flags_to_string_of_flags ptr_list_of_flags string_of_flags)
286 # Convert the list to a string so we can do string replace
287 # operations on it and replace the ";" list separators with a
288 # whitespace so the flags are spaced out
289 string(REPLACE ";" " " locally_scoped_string_of_flags "${${ptr_list_of_flags}}")
290
291 # Set the output variable in the parent scope
292 set(${string_of_flags} ${locally_scoped_string_of_flags} PARENT_SCOPE)
293endfunction()
294
295macro(get_property_and_add_prefix result target property prefix)
296 aml_get_parse_args(args ${ARGN})
297
298 if(args_STRIP_PREFIX)
299 set(maybe_prefix "")
300 else()
301 set(maybe_prefix ${prefix})
302 endif()
303
304 get_property(target_property TARGET ${target} PROPERTY ${property})
305 foreach(x ${target_property})
306 list(APPEND ${result} ${maybe_prefix}${x})
307 endforeach()
308endmacro()
309
310# 1.2 aml_library_*
311#
312# Amlogic libraries use CMake's library concept and a set of
313# assumptions about how aml code is organized to cut down on
314# boilerplate code.
315#
316# A Amlogic library can be constructed by the function aml_library
317# or aml_library_named. The constructors create a CMake library
318# with a name accessible through the variable CURRENT_LIBRARY.
319#
320# The variable CURRENT_LIBRARY should seldomly be needed since
321# the aml libraries have methods that modify the libraries. These
322# methods have the signature: aml_library_<target-function>
323#
324# The methods are wrappers around the CMake target_* functions. See
325# https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html for
326# documentation on the underlying target_* functions.
327#
328# The methods modify the CMake target_* API to reduce boilerplate;
329# PRIVATE is assumed
330# The target is assumed to be CURRENT_LIBRARY
331#
332# When a flag that is given through the aml_* API conflicts with
333# the aml_library_* API then precedence will be given to the
334# aml_library_* API. In other words, local configuration overrides
335# global configuration.
336
337# Constructor with a directory-inferred name
338macro(aml_library)
339 aml_library_get_current_dir_lib_name(lib_name)
340 aml_library_named(${lib_name})
341endmacro()
342
343# Determines what the current directory's lib name would be and writes
344# it to the argument "lib_name"
345macro(aml_library_get_current_dir_lib_name lib_name)
346 # Remove the prefix (/home/sebo/aml/driver/serial/CMakeLists.txt => driver/serial/CMakeLists.txt)
347 file(RELATIVE_PATH name ${ZEPHYR_BASE} ${CMAKE_CURRENT_LIST_FILE})
348
349 # Remove the filename (driver/serial/CMakeLists.txt => driver/serial)
350 get_filename_component(name ${name} DIRECTORY)
351
352 # Replace / with __ (driver/serial => driver__serial)
353 string(REGEX REPLACE "/" "__" name ${name})
354
355 set(${lib_name} ${name})
356endmacro()
357
358# Constructor with an explicitly given name.
359macro(aml_library_named name)
360 # This is a macro because we need add_library() to be executed
361 # within the scope of the caller.
362 set(CURRENT_LIBRARY ${name})
363 add_library(${name} STATIC "")
364
365 aml_append_cmake_library(${name})
366
367 target_link_libraries(${name} PUBLIC aml_interface)
368endmacro()
369
370macro(aml_add_library)
371 STRING( REGEX REPLACE ".*/(.*)/.*" "\\1" LAYER ${CMAKE_CURRENT_SOURCE_DIR} )
372 STRING( REGEX REPLACE ".*/(.*)" "\\1" MODULE ${CMAKE_CURRENT_SOURCE_DIR} )
373
374 set(CURRENT_LIBRARY ${LAYER}__${MODULE})
375 message(STATUS "@@CURRENT_LIBRARY: ${CURRENT_LIBRARY}")
376 add_library(${CURRENT_LIBRARY} STATIC "")
377endmacro()
378
379macro(aml_add_interface)
380 STRING( REGEX REPLACE ".*/(.*)/.*" "\\1" LAYER ${CMAKE_CURRENT_SOURCE_DIR} )
381 STRING( REGEX REPLACE ".*/(.*)" "\\1" MODULE ${CMAKE_CURRENT_SOURCE_DIR} )
382
383 set(CURRENT_INTERFACE ${LAYER}__${MODULE})
384 message(STATUS "@@CURRENT_INTERFACE: ${CURRENT_INTERFACE}")
385 add_library(${CURRENT_INTERFACE} INTERFACE)
386endmacro()
387
388function(aml_link_interface interface)
389 target_link_libraries(${interface} INTERFACE aml_interface)
390endfunction()
391
392#
393# aml_library versions of normal CMake target_<func> functions
394#
395function(aml_library_sources source)
396 target_sources(${CURRENT_LIBRARY} PRIVATE ${source} ${ARGN})
397endfunction()
398
399function(aml_library_include_directories)
400 target_include_directories(${CURRENT_LIBRARY} PUBLIC ${ARGN})
401endfunction()
402
403function(aml_interface_include_directories)
404 target_include_directories(${CURRENT_INTERFACE} INTERFACE ${ARGN})
405endfunction()
406
407function(aml_library_link_libraries item)
408 target_link_libraries(${CURRENT_LIBRARY} PUBLIC ${item} ${ARGN})
409endfunction()
410
411function(aml_interface_link_interfaces item)
412 target_link_libraries(${CURRENT_INTERFACE} INTERFACE ${item} ${ARGN})
413endfunction()
414
415function(aml_library_compile_definitions item)
416 target_compile_definitions(${CURRENT_LIBRARY} PRIVATE ${item} ${ARGN})
417endfunction()
418
419function(aml_library_compile_options item)
420 # The compiler is relied upon for sane behaviour when flags are in
421 # conflict. Compilers generally give precedence to flags given late
422 # on the command line. So to ensure that aml_library_* flags are
423 # placed late on the command line we create a dummy interface
424 # library and link with it to obtain the flags.
425 #
426 # Linking with a dummy interface library will place flags later on
427 # the command line than the the flags from aml_interface because
428 # aml_interface will be the first interface library that flags
429 # are taken from.
430
431 string(MD5 uniqueness ${item})
432 set(lib_name options_interface_lib_${uniqueness})
433
434 if (TARGET ${lib_name})
435 # ${item} already added, ignoring duplicate just like CMake does
436 return()
437 endif()
438
439 add_library( ${lib_name} INTERFACE)
440 target_compile_options(${lib_name} INTERFACE ${item} ${ARGN})
441
442 target_link_libraries(${CURRENT_LIBRARY} PRIVATE ${lib_name})
443endfunction()
444
445function(aml_library_cc_option)
446 foreach(option ${ARGV})
447 string(MAKE_C_IDENTIFIER check${option} check)
448 aml_check_compiler_flag(C ${option} ${check})
449
450 if(${check})
451 aml_library_compile_options(${option})
452 endif()
453 endforeach()
454endfunction()
455
456# Add the existing CMake library 'library' to the global list of
457# Amlogic CMake libraries. This is done automatically by the
458# constructor but must called explicitly on CMake libraries that do
459# not use a aml library constructor, but have source files that
460# need to be included in the build.
461function(aml_append_cmake_library library)
462 set_property(GLOBAL APPEND PROPERTY ZEPHYR_LIBS ${library})
463endfunction()
464
465# 1.2.1 aml_interface_library_*
466#
467# A Amlogic interface library is a thin wrapper over a CMake INTERFACE
468# library. The most important responsibility of this abstraction is to
469# ensure that when a user KConfig-enables a library then the header
470# files of this library will be accessible to the 'app' library.
471#
472# This is done because when a user uses Kconfig to enable a library he
473# expects to be able to include it's header files and call it's
474# functions out-of-the box.
475#
476# A Amlogic interface library should be used when there exists some
477# build information (include directories, defines, compiler flags,
478# etc.) that should be applied to a set of Amlogic libraries and 'app'
479# might be one of these libraries.
480#
481# Amlogic libraries must explicitly call
482# aml_library_link_libraries(<interface_library>) to use this build
483# information. 'app' is treated as a special case for usability
484# reasons; a Kconfig option (CONFIG_APP_LINK_WITH_<interface_library>)
485# should exist for each interface_library and will determine if 'app'
486# links with the interface_library.
487#
488# This API has a constructor like the aml_library API has, but it
489# does not have wrappers over the other cmake target functions.
490macro(aml_interface_library_named name)
491 add_library(${name} INTERFACE)
492 set_property(GLOBAL APPEND PROPERTY ZEPHYR_INTERFACE_LIBS ${name})
493endmacro()
494
495# 1.3 generate_inc_*
496
497# These functions are useful if there is a need to generate a file
498# that can be included into the application at build time. The file
499# can also be compressed automatically when embedding it.
500#
501# See tests/application_development/gen_inc_file for an example of
502# usage.
503function(generate_inc_file
504 source_file # The source file to be converted to hex
505 generated_file # The generated file
506 )
507 add_custom_command(
508 OUTPUT ${generated_file}
509 COMMAND
510 ${PYTHON_EXECUTABLE}
511 ${ZEPHYR_BASE}/scripts/file2hex.py
512 ${ARGN} # Extra arguments are passed to file2hex.py
513 --file ${source_file}
514 > ${generated_file} # Does pipe redirection work on Windows?
515 DEPENDS ${source_file}
516 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
517 )
518endfunction()
519
520function(generate_inc_file_for_gen_target
521 target # The cmake target that depends on the generated file
522 source_file # The source file to be converted to hex
523 generated_file # The generated file
524 gen_target # The generated file target we depend on
525 # Any additional arguments are passed on to file2hex.py
526 )
527 generate_inc_file(${source_file} ${generated_file} ${ARGN})
528
529 # Ensure 'generated_file' is generated before 'target' by creating a
530 # dependency between the two targets
531
532 add_dependencies(${target} ${gen_target})
533endfunction()
534
535function(generate_inc_file_for_target
536 target # The cmake target that depends on the generated file
537 source_file # The source file to be converted to hex
538 generated_file # The generated file
539 # Any additional arguments are passed on to file2hex.py
540 )
541 # Ensure 'generated_file' is generated before 'target' by creating a
542 # 'custom_target' for it and setting up a dependency between the two
543 # targets
544
545 # But first create a unique name for the custom target
546 generate_unique_target_name_from_filename(${generated_file} generated_target_name)
547
548 add_custom_target(${generated_target_name} DEPENDS ${generated_file})
549 generate_inc_file_for_gen_target(${target} ${source_file} ${generated_file} ${generated_target_name} ${ARGN})
550endfunction()
551
552# 1.4. board_*
553
554# 1.5. Misc.
555
556# aml_check_compiler_flag is a part of Amlogic's toolchain
557# infrastructure. It should be used when testing toolchain
558# capabilities and it should normally be used in place of the
559# functions:
560#
561# check_compiler_flag
562# check_c_compiler_flag
563# check_cxx_compiler_flag
564#
565# See check_compiler_flag() for API documentation as it has the same
566# API.
567#
568# It is implemented as a wrapper on top of check_compiler_flag, which
569# again wraps the CMake-builtin's check_c_compiler_flag and
570# check_cxx_compiler_flag.
571#
572# It takes time to check for compatibility of flags against toolchains
573# so we cache the capability test results in USER_CACHE_DIR (This
574# caching comes in addition to the caching that CMake does in the
575# build folder's CMakeCache.txt)
576function(aml_check_compiler_flag lang option check)
577 # Locate the cache directory
578 set_ifndef(
579 ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE_DIR
580 ${USER_CACHE_DIR}/ToolchainCapabilityDatabase
581 )
582 if(DEFINED ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE)
583 assert(0
584 "The deprecated ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE is now a directory"
585 "and is named ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE_DIR"
586 )
587 # Remove this deprecation warning in version 1.14.
588 endif()
589
590 # The toolchain capability database/cache is maintained as a
591 # directory of files. The filenames in the directory are keys, and
592 # the file contents are the values in this key-value store.
593
594 # We need to create a unique key wrt. testing the toolchain
595 # capability. This key must include everything that can affect the
596 # toolchain test.
597 #
598 # Also, to fit the key into a filename we calculate the MD5 sum of
599 # the key.
600
601 # The 'cacheformat' must be bumped if a bug in the caching mechanism
602 # is detected and all old keys must be invalidated.
603 set(cacheformat 3)
604
605 set(key_string "")
606 set(key_string "${key_string}${cacheformat}_")
607 set(key_string "${key_string}${TOOLCHAIN_SIGNATURE}_")
608 set(key_string "${key_string}${lang}_")
609 set(key_string "${key_string}${option}_")
610 set(key_string "${key_string}${CMAKE_REQUIRED_FLAGS}_")
611
612 string(MD5 key ${key_string})
613
614 # Check the cache
615 set(key_path ${ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE_DIR}/${key})
616 if(EXISTS ${key_path})
617 file(READ
618 ${key_path} # File to be read
619 key_value # Output variable
620 LIMIT 1 # Read at most 1 byte ('0' or '1')
621 )
622
623 set(${check} ${key_value} PARENT_SCOPE)
624 return()
625 endif()
626
627 # Test the flag
628 check_compiler_flag(${lang} "${option}" inner_check)
629
630 set(${check} ${inner_check} PARENT_SCOPE)
631
632 # Populate the cache
633 if(NOT (EXISTS ${key_path}))
634 file(
635 WRITE
636 ${key_path}
637 ${inner_check}
638 )
639
640 # Populate a metadata file (only intended for trouble shooting)
641 # with information about the hash, the toolchain capability
642 # result, and the toolchain test.
643 file(
644 APPEND
645 ${ZEPHYR_TOOLCHAIN_CAPABILITY_CACHE_DIR}/log.txt
646 "${inner_check} ${key} ${key_string}\n"
647 )
648 endif()
649endfunction()
650
651# Helper function for CONFIG_CODE_DATA_RELOCATION
652# Call this function with 2 arguments file and then memory location
653function(aml_code_relocate file location)
654 set_property(TARGET code_data_relocation_target
655 APPEND PROPERTY COMPILE_DEFINITIONS
656 "${location}:${CMAKE_CURRENT_SOURCE_DIR}/${file}")
657endfunction()
658
659# Usage:
660# check_dtc_flag("-Wtest" DTC_WARN_TEST)
661#
662# Writes 1 to the output variable 'ok' if
663# the flag is supported, otherwise writes 0.
664#
665# using
666function(check_dtc_flag flag ok)
667 execute_process(
668 COMMAND
669 ${DTC} ${flag} -v
670 ERROR_QUIET
671 OUTPUT_QUIET
672 RESULT_VARIABLE dtc_check_ret
673 )
674 if (dtc_check_ret EQUAL 0)
675 set(${ok} 1 PARENT_SCOPE)
676 else()
677 set(${ok} 0 PARENT_SCOPE)
678 endif()
679endfunction()
680
681########################################################
682# 2. Kconfig-aware extensions
683########################################################
684#
685# Kconfig is a configuration language developed for the Linux
686# kernel. The below functions integrate CMake with Kconfig.
687#
688# 2.1 *_if_kconfig
689#
690# Functions for conditionally including directories and source files
691# that have matching KConfig values.
692#
693# aml_library_sources_if_kconfig(fft.c)
694# is the same as
695# aml_library_sources_ifdef(CONFIG_FFT fft.c)
696#
697# add_subdirectory_if_kconfig(serial)
698# is the same as
699# add_subdirectory_ifdef(CONFIG_SERIAL serial)
700function(add_subdirectory_if_kconfig dir)
701 string(TOUPPER config_${dir} UPPER_CASE_CONFIG)
702 add_subdirectory_ifdef(${UPPER_CASE_CONFIG} ${dir})
703endfunction()
704
705function(target_sources_if_kconfig target scope item)
706 get_filename_component(item_basename ${item} NAME_WE)
707 string(TOUPPER CONFIG_${item_basename} UPPER_CASE_CONFIG)
708 target_sources_ifdef(${UPPER_CASE_CONFIG} ${target} ${scope} ${item})
709endfunction()
710
711function(aml_library_sources_if_kconfig item)
712 get_filename_component(item_basename ${item} NAME_WE)
713 string(TOUPPER CONFIG_${item_basename} UPPER_CASE_CONFIG)
714 aml_library_sources_ifdef(${UPPER_CASE_CONFIG} ${item})
715endfunction()
716
717function(aml_sources_if_kconfig item)
718 get_filename_component(item_basename ${item} NAME_WE)
719 string(TOUPPER CONFIG_${item_basename} UPPER_CASE_CONFIG)
720 aml_sources_ifdef(${UPPER_CASE_CONFIG} ${item})
721endfunction()
722
723# 2.2 Misc
724#
725# Parse a KConfig fragment (typically with extension .config) and
726# introduce all the symbols that are prefixed with 'prefix' into the
727# CMake namespace
728function(import_kconfig prefix kconfig_fragment)
729 # Parse the lines prefixed with 'prefix' in ${kconfig_fragment}
730 file(
731 STRINGS
732 ${kconfig_fragment}
733 DOT_CONFIG_LIST
734 REGEX "^${prefix}"
735 ENCODING "UTF-8"
736 )
737
738 foreach (CONFIG ${DOT_CONFIG_LIST})
739 # CONFIG could look like: CONFIG_NET_BUF=y
740
741 # Match the first part, the variable name
742 string(REGEX MATCH "[^=]+" CONF_VARIABLE_NAME ${CONFIG})
743
744 # Match the second part, variable value
745 string(REGEX MATCH "=(.+$)" CONF_VARIABLE_VALUE ${CONFIG})
746 # The variable name match we just did included the '=' symbol. To just get the
747 # part on the RHS we use match group 1
748 set(CONF_VARIABLE_VALUE ${CMAKE_MATCH_1})
749
750 if("${CONF_VARIABLE_VALUE}" MATCHES "^\"(.*)\"$") # Is surrounded by quotes
751 set(CONF_VARIABLE_VALUE ${CMAKE_MATCH_1})
752 endif()
753
754 set("${CONF_VARIABLE_NAME}" "${CONF_VARIABLE_VALUE}" PARENT_SCOPE)
755 endforeach()
756endfunction()
757
758########################################################
759# 3. CMake-generic extensions
760########################################################
761#
762# These functions extend the CMake API in a way that is not particular
763# to Amlogic. Primarily they work around limitations in the CMake
764# language to allow cleaner build scripts.
765
766# 3.1. *_ifdef
767#
768# Functions for conditionally executing CMake functions with oneliners
769# e.g.
770#
771# if(CONFIG_FFT)
772# aml_library_source(
773# fft_32.c
774# fft_utils.c
775# )
776# endif()
777#
778# Becomes
779#
780# aml_source_ifdef(
781# CONFIG_FFT
782# fft_32.c
783# fft_utils.c
784# )
785#
786# More Generally
787# "<function-name>_ifdef(CONDITION args)"
788# Becomes
789# """
790# if(CONDITION)
791# <function-name>(args)
792# endif()
793# """
794#
795# ifdef functions are added on an as-need basis. See
796# https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html for
797# a list of available functions.
798function(add_subdirectory_ifdef feature_toggle dir)
799 if(${${feature_toggle}})
800 add_subdirectory(${dir})
801 endif()
802endfunction()
803
804function(target_sources_ifdef feature_toggle target scope item)
805 if(${${feature_toggle}})
806 target_sources(${target} ${scope} ${item} ${ARGN})
807 endif()
808endfunction()
809
810function(target_compile_definitions_ifdef feature_toggle target scope item)
811 if(${${feature_toggle}})
812 target_compile_definitions(${target} ${scope} ${item} ${ARGN})
813 endif()
814endfunction()
815
816function(target_include_directories_ifdef feature_toggle target scope item)
817 if(${${feature_toggle}})
818 target_include_directories(${target} ${scope} ${item} ${ARGN})
819 endif()
820endfunction()
821
822function(target_link_libraries_ifdef feature_toggle target item)
823 if(${${feature_toggle}})
824 target_link_libraries(${target} ${item} ${ARGN})
825 endif()
826endfunction()
827
828function(add_compile_option_ifdef feature_toggle option)
829 if(${${feature_toggle}})
830 add_compile_options(${option})
831 endif()
832endfunction()
833
834function(target_compile_option_ifdef feature_toggle target scope option)
835 if(${feature_toggle})
836 target_compile_options(${target} ${scope} ${option})
837 endif()
838endfunction()
839
840function(target_cc_option_ifdef feature_toggle target scope option)
841 if(${feature_toggle})
842 target_cc_option(${target} ${scope} ${option})
843 endif()
844endfunction()
845
846function(aml_library_sources_ifdef feature_toggle source)
847 if(${${feature_toggle}})
848 aml_library_sources(${source} ${ARGN})
849 endif()
850endfunction()
851
852function(aml_sources_ifdef feature_toggle)
853 if(${${feature_toggle}})
854 aml_sources(${ARGN})
855 endif()
856endfunction()
857
858function(aml_sources_ifndef feature_toggle)
859 if(NOT ${feature_toggle})
860 aml_sources(${ARGN})
861 endif()
862endfunction()
863
864function(aml_cc_option_ifdef feature_toggle)
865 if(${${feature_toggle}})
866 aml_cc_option(${ARGN})
867 endif()
868endfunction()
869
870function(aml_ld_option_ifdef feature_toggle)
871 if(${${feature_toggle}})
872 aml_ld_options(${ARGN})
873 endif()
874endfunction()
875
876function(aml_link_libraries_ifdef feature_toggle)
877 if(${${feature_toggle}})
878 aml_link_libraries(${ARGN})
879 endif()
880endfunction()
881
882function(aml_compile_options_ifdef feature_toggle)
883 if(${${feature_toggle}})
884 aml_compile_options(${ARGN})
885 endif()
886endfunction()
887
888function(aml_compile_definitions_ifdef feature_toggle)
889 if(${${feature_toggle}})
890 aml_compile_definitions(${ARGN})
891 endif()
892endfunction()
893
894function(aml_include_directories_ifdef feature_toggle)
895 if(${${feature_toggle}})
896 aml_include_directories(${ARGN})
897 endif()
898endfunction()
899
900function(aml_library_compile_definitions_ifdef feature_toggle item)
901 if(${${feature_toggle}})
902 aml_library_compile_definitions(${item} ${ARGN})
903 endif()
904endfunction()
905
906function(aml_library_compile_options_ifdef feature_toggle item)
907 if(${${feature_toggle}})
908 aml_library_compile_options(${item} ${ARGN})
909 endif()
910endfunction()
911
912function(aml_link_interface_ifdef feature_toggle interface)
913 if(${${feature_toggle}})
914 target_link_libraries(${interface} INTERFACE aml_interface)
915 endif()
916endfunction()
917
918function(aml_library_link_libraries_ifdef feature_toggle item)
919 if(${${feature_toggle}})
920 aml_library_link_libraries(${item})
921 endif()
922endfunction()
923
924macro(list_append_ifdef feature_toggle list)
925 if(${${feature_toggle}})
926 list(APPEND ${list} ${ARGN})
927 endif()
928endmacro()
929
930# 3.2. *_ifndef
931# See 3.1 *_ifdef
932function(set_ifndef variable value)
933 if(NOT ${variable})
934 set(${variable} ${value} ${ARGN} PARENT_SCOPE)
935 endif()
936endfunction()
937
938function(target_cc_option_ifndef feature_toggle target scope option)
939 if(NOT ${feature_toggle})
940 target_cc_option(${target} ${scope} ${option})
941 endif()
942endfunction()
943
944function(aml_cc_option_ifndef feature_toggle)
945 if(NOT ${feature_toggle})
946 aml_cc_option(${ARGN})
947 endif()
948endfunction()
949
950function(aml_compile_options_ifndef feature_toggle)
951 if(NOT ${feature_toggle})
952 aml_compile_options(${ARGN})
953 endif()
954endfunction()
955
956# 3.2. *_option Compiler-compatibility checks
957#
958# Utility functions for silently omitting compiler flags when the
959# compiler lacks support. *_cc_option was ported from KBuild, see
960# cc-option in
961# https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt
962
963# Writes 1 to the output variable 'ok' for the language 'lang' if
964# the flag is supported, otherwise writes 0.
965#
966# lang must be C or CXX
967#
968# TODO: Support ASM
969#
970# Usage:
971#
972# check_compiler_flag(C "-Wall" my_check)
973# print(my_check) # my_check is now 1
974function(check_compiler_flag lang option ok)
975 if(NOT DEFINED CMAKE_REQUIRED_QUIET)
976 set(CMAKE_REQUIRED_QUIET 1)
977 endif()
978
979 string(MAKE_C_IDENTIFIER
980 check${option}_${lang}_${CMAKE_REQUIRED_FLAGS}
981 ${ok}
982 )
983
984 if(${lang} STREQUAL C)
985 check_c_compiler_flag("${option}" ${${ok}})
986 else()
987 check_cxx_compiler_flag("${option}" ${${ok}})
988 endif()
989
990 if(${${${ok}}})
991 set(ret 1)
992 else()
993 set(ret 0)
994 endif()
995
996 set(${ok} ${ret} PARENT_SCOPE)
997endfunction()
998
999function(target_cc_option target scope option)
1000 target_cc_option_fallback(${target} ${scope} ${option} "")
1001endfunction()
1002
1003# Support an optional second option for when the first option is not
1004# supported.
1005function(target_cc_option_fallback target scope option1 option2)
1006 if(CONFIG_CPLUSPLUS)
1007 foreach(lang C CXX)
1008 # For now, we assume that all flags that apply to C/CXX also
1009 # apply to ASM.
1010 aml_check_compiler_flag(${lang} ${option1} check)
1011 if(${check})
1012 target_compile_options(${target} ${scope}
1013 $<$<COMPILE_LANGUAGE:${lang}>:${option1}>
1014 $<$<COMPILE_LANGUAGE:ASM>:${option1}>
1015 )
1016 elseif(option2)
1017 target_compile_options(${target} ${scope}
1018 $<$<COMPILE_LANGUAGE:${lang}>:${option2}>
1019 $<$<COMPILE_LANGUAGE:ASM>:${option2}>
1020 )
1021 endif()
1022 endforeach()
1023 else()
1024 aml_check_compiler_flag(C ${option1} check)
1025 if(${check})
1026 target_compile_options(${target} ${scope} ${option1})
1027 elseif(option2)
1028 target_compile_options(${target} ${scope} ${option2})
1029 endif()
1030 endif()
1031endfunction()
1032
1033function(target_ld_options target scope)
1034 foreach(option ${ARGN})
1035 string(MAKE_C_IDENTIFIER check${option} check)
1036
1037 set(SAVED_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
1038 set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${option}")
1039 aml_check_compiler_flag(C "" ${check})
1040 set(CMAKE_REQUIRED_FLAGS ${SAVED_CMAKE_REQUIRED_FLAGS})
1041
1042 target_link_libraries_ifdef(${check} ${target} ${scope} ${option})
1043 endforeach()
1044endfunction()
1045
1046# 3.4. Debugging CMake
1047
1048# Usage:
1049# print(BOARD)
1050#
1051# will print: "BOARD: nrf52_pca10040"
1052function(print arg)
1053 message(STATUS "${arg}: ${${arg}}")
1054endfunction()
1055
1056# Usage:
1057# assert(ZEPHYR_TOOLCHAIN_VARIANT "ZEPHYR_TOOLCHAIN_VARIANT not set.")
1058#
1059# will cause a FATAL_ERROR and print an error message if the first
1060# expression is false
1061macro(assert test comment)
1062 if(NOT ${test})
1063 message(FATAL_ERROR "Assertion failed: ${comment}")
1064 endif()
1065endmacro()
1066
1067# Usage:
1068# assert_not(FLASH_SCRIPT "FLASH_SCRIPT has been removed; use BOARD_FLASH_RUNNER")
1069#
1070# will cause a FATAL_ERROR and print an errorm essage if the first
1071# espression is true
1072macro(assert_not test comment)
1073 if(${test})
1074 message(FATAL_ERROR "Assertion failed: ${comment}")
1075 endif()
1076endmacro()
1077
1078# Usage:
1079# assert_exists(CMAKE_READELF)
1080#
1081# will cause a FATAL_ERROR if there is no file or directory behind the
1082# variable
1083macro(assert_exists var)
1084 if(NOT EXISTS ${${var}})
1085 message(FATAL_ERROR "No such file or directory: ${var}: '${${var}}'")
1086 endif()
1087endmacro()
1088
1089function(print_usage)
1090 message("see usage:")
1091 string(REPLACE ";" " " BOARD_ROOT_SPACE_SEPARATED "${BOARD_ROOT}")
1092 string(REPLACE ";" " " SHIELD_LIST_SPACE_SEPARATED "${SHIELD_LIST}")
1093 execute_process(
1094 COMMAND
1095 ${CMAKE_COMMAND}
1096 -DBOARD_ROOT_SPACE_SEPARATED=${BOARD_ROOT_SPACE_SEPARATED}
1097 -DSHIELD_LIST_SPACE_SEPARATED=${SHIELD_LIST_SPACE_SEPARATED}
1098 -P ${ZEPHYR_BASE}/cmake/usage/usage.cmake
1099 )
1100endfunction()
1101
1102# 3.5. File system management
1103function(check_if_directory_is_writeable dir ok)
1104 execute_process(
1105 COMMAND
1106 ${PYTHON_EXECUTABLE}
1107 ${ZEPHYR_BASE}/scripts/dir_is_writeable.py
1108 ${dir}
1109 RESULT_VARIABLE ret
1110 )
1111
1112 if("${ret}" STREQUAL "0")
1113 # The directory is write-able
1114 set(${ok} 1 PARENT_SCOPE)
1115 else()
1116 set(${ok} 0 PARENT_SCOPE)
1117 endif()
1118endfunction()
1119
1120function(find_appropriate_cache_directory dir)
1121 set(env_suffix_LOCALAPPDATA .cache)
1122
1123 if(CMAKE_HOST_APPLE)
1124 # On macOS, ~/Library/Caches is the preferred cache directory.
1125 set(env_suffix_HOME Library/Caches)
1126 else()
1127 set(env_suffix_HOME .cache)
1128 endif()
1129
1130 # Determine which env vars should be checked
1131 if(CMAKE_HOST_APPLE)
1132 set(dirs HOME)
1133 elseif(CMAKE_HOST_WIN32)
1134 set(dirs LOCALAPPDATA)
1135 else()
1136 # Assume Linux when we did not detect 'mac' or 'win'
1137 #
1138 # On Linux, freedesktop.org recommends using $XDG_CACHE_HOME if
1139 # that is defined and defaulting to $HOME/.cache otherwise.
1140 set(dirs
1141 XDG_CACHE_HOME
1142 HOME
1143 )
1144 endif()
1145
1146 foreach(env_var ${dirs})
1147 if(DEFINED ENV{${env_var}})
1148 set(env_dir $ENV{${env_var}})
1149
1150 check_if_directory_is_writeable(${env_dir} ok)
1151 if(${ok})
1152 # The directory is write-able
1153 set(user_dir ${env_dir}/${env_suffix_${env_var}})
1154 break()
1155 else()
1156 # The directory was not writeable, keep looking for a suitable
1157 # directory
1158 endif()
1159 endif()
1160 endforeach()
1161
1162 # Populate local_dir with a suitable directory for caching
1163 # files. Prefer a directory outside of the git repository because it
1164 # is good practice to have clean git repositories.
1165 if(DEFINED user_dir)
1166 # Amlogic's cache files go in the "aml" subdirectory of the
1167 # user's cache directory.
1168 set(local_dir ${user_dir}/aml)
1169 else()
1170 set(local_dir ${ZEPHYR_BASE}/.cache)
1171 endif()
1172
1173 set(${dir} ${local_dir} PARENT_SCOPE)
1174endfunction()
1175
1176function(generate_unique_target_name_from_filename filename target_name)
1177 get_filename_component(basename ${filename} NAME)
1178 string(REPLACE "." "_" x ${basename})
1179 string(REPLACE "@" "_" x ${x})
1180
1181 string(MD5 unique_chars ${filename})
1182
1183 set(${target_name} gen_${x}_${unique_chars} PARENT_SCOPE)
1184endfunction()
1185
1186function(compiler_generate_binary_output TARGET)
1187 add_custom_command(TARGET ${TARGET} POST_BUILD COMMAND ${CMAKE_OBJCOPY_COMPILER} ARGS -O binary $<TARGET_FILE:${TARGET}> $<TARGET_FILE_DIR:${TARGET}>/$ENV{KERNEL}.bin)
1188endfunction()
1189
1190function(compiler_generate_lst_output TARGET)
1191 add_custom_command(TARGET ${TARGET} POST_BUILD COMMAND ${CMAKE_OBJDUMP_COMPILER} ARGS -S $<TARGET_FILE:${TARGET}> > $<TARGET_FILE_DIR:${TARGET}>/$ENV{KERNEL}.lst)
1192endfunction()