include(CheckCCompilerFlag) ######################################################## # Table of contents ######################################################## # 1. Zephyr-aware extensions # 1.1. zephyr_* # 1.2. zephyr_library_* # 1.3. generate_inc_* # 2. Kconfig-aware extensions # 2.1 *_if_kconfig # 2.2 Misc # 3. CMake-generic extensions # 3.1. *_ifdef # 3.2. *_ifndef # 3.3. *_option compiler compatibility checks # 3.4. Debugging CMake ######################################################## # 1. Zephyr-aware extensions ######################################################## # 1.1. zephyr_* # # The following methods are for modifying the CMake library[0] called # "zephyr". zephyr is a catchall CMake library for source files that # can be built purely with the include paths, defines, and other # compiler flags that all zephyr source files use. # [0] https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html # # Example usage: # zephyr_sources( # random_esp32.c # utils.c # ) # # Is short for: # target_sources(zephyr PRIVATE # ${CMAKE_CURRENT_SOURCE_DIR}/random_esp32.c # ${CMAKE_CURRENT_SOURCE_DIR}/utils.c # ) # https://cmake.org/cmake/help/latest/command/target_sources.html function(zephyr_sources) foreach(arg ${ARGV}) if(IS_ABSOLUTE ${arg}) set(path ${arg}) else() set(path ${CMAKE_CURRENT_SOURCE_DIR}/${arg}) endif() if(IS_DIRECTORY ${path}) message(FATAL_ERROR "zephyr_sources() was called on a directory") endif() target_sources(zephyr PRIVATE ${path}) endforeach() endfunction() # https://cmake.org/cmake/help/latest/command/target_include_directories.html function(zephyr_include_directories) foreach(arg ${ARGV}) if(IS_ABSOLUTE ${arg}) set(path ${arg}) else() set(path ${CMAKE_CURRENT_SOURCE_DIR}/${arg}) endif() target_include_directories(zephyr_interface INTERFACE ${path}) endforeach() endfunction() # https://cmake.org/cmake/help/latest/command/target_include_directories.html function(zephyr_system_include_directories) foreach(arg ${ARGV}) if(IS_ABSOLUTE ${arg}) set(path ${arg}) else() set(path ${CMAKE_CURRENT_SOURCE_DIR}/${arg}) endif() target_include_directories(zephyr_interface SYSTEM INTERFACE ${path}) endforeach() endfunction() # https://cmake.org/cmake/help/latest/command/target_compile_definitions.html function(zephyr_compile_definitions) target_compile_definitions(zephyr_interface INTERFACE ${ARGV}) endfunction() # https://cmake.org/cmake/help/latest/command/target_compile_options.html function(zephyr_compile_options) target_compile_options(zephyr_interface INTERFACE ${ARGV}) endfunction() # https://cmake.org/cmake/help/latest/command/target_link_libraries.html function(zephyr_link_libraries) target_link_libraries(zephyr_interface INTERFACE ${ARGV}) endfunction() # See this file section 3.1. target_cc_option function(zephyr_cc_option) foreach(arg ${ARGV}) target_cc_option(zephyr_interface INTERFACE ${arg}) endforeach() endfunction() function(zephyr_cc_option_fallback option1 option2) target_cc_option_fallback(zephyr_interface INTERFACE ${option1} ${option2}) endfunction() function(zephyr_ld_options) target_ld_options(zephyr_interface INTERFACE ${ARGV}) endfunction() # Getter functions for extracting build information from # zephyr_interface. Returning lists, and strings is supported, as is # requesting specific categories of build information (defines, # includes, options). # # The naming convention follows: # zephyr_get_${build_information}${format}(x) # Where # the argument 'x' is written with the result # and # ${build_information} can be one of # - include_directories # -I directories # - system_include_directories # -isystem directories # - compile_definitions # -D'efines # - compile_options # misc. compiler flags # and # ${format} can be # the empty string '', signifying that it should be returned as a list # _as_string signifying that it should be returned as a string # # e.g. # zephyr_get_include_directories(x) # writes "-Isome_dir;-Isome/other/dir" to x # Utility macro used by the below macros. macro(get_property_and_add_prefix result target property prefix) get_property(target_property TARGET ${target} PROPERTY ${property}) foreach(x ${target_property}) list(APPEND ${result} ${prefix}${x}) endforeach() endmacro() macro(zephyr_get_include_directories i) get_property_and_add_prefix(${i} zephyr_interface INTERFACE_INCLUDE_DIRECTORIES -I) endmacro() macro(zephyr_get_system_include_directories i) get_property_and_add_prefix(${i} zephyr_interface INTERFACE_SYSTEM_INCLUDE_DIRECTORIES -isystem) endmacro() macro(zephyr_get_compile_definitions i) get_property_and_add_prefix(${i} zephyr_interface INTERFACE_COMPILE_DEFINITIONS -D) endmacro() macro(zephyr_get_compile_options i) get_property(${i} TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_OPTIONS) endmacro() macro(zephyr_get_include_directories_as_string i) zephyr_get_include_directories(${i}) string(REPLACE ";" " " ${i} ${${i}}) string(REPLACE "-I" " -I" ${i} ${${i}}) endmacro() macro(zephyr_get_system_include_directories_as_string i) get_property_and_add_prefix(${i} zephyr_interface INTERFACE_SYSTEM_INCLUDE_DIRECTORIES -isystem) string(REPLACE ";" " " ${i} ${${i}}) string(REPLACE "-isystem" " -isystem" ${i} ${${i}}) endmacro() macro(zephyr_get_compile_definitions_as_string i) get_property_and_add_prefix(${i} zephyr_interface INTERFACE_COMPILE_DEFINITIONS -D) string(REPLACE ";" " " ${i} ${${i}}) string(REPLACE "-D" " -D" ${i} ${${i}}) endmacro() macro(zephyr_get_compile_options_as_string i) zephyr_get_compile_options(j) foreach(__opt__ ${j}) if(__opt__ MATCHES " ${generated_file} # Does pipe redirection work on Windows? DEPENDS ${source_file} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) endfunction() function(generate_inc_file_for_target target # The cmake target that depends on the generated file source_file # The source file to be converted to hex generated_file # The generated file # Any additional arguments are passed on to file2hex.py ) generate_inc_file(${source_file} ${generated_file} ${ARGN}) # Ensure 'generated_file' is generated before 'target' by creating a # 'custom_target' for it and setting up a dependency between the two # targets # But first create a unique name for the custom target # Replace / with _ (driver/serial => driver_serial) and . with _ set(generated_target_name ${generated_file}) string(REPLACE "/" "_" generated_target_name ${generated_target_name}) string(REPLACE "." "_" generated_target_name ${generated_target_name}) add_custom_target(${generated_target_name} DEPENDS ${generated_file}) add_dependencies(${target} ${generated_target_name}) endfunction() # 2.1 zephyr_library_* # # Zephyr libraries use CMake's library concept and a set of # assumptions about how zephyr code is organized to cut down on # boilerplate code. # # A Zephyr library can be constructed by the function zephyr_library # or zephyr_library_named. The constructors create a CMake library # with a name accessible through the variable ZEPHYR_CURRENT_LIBRARY. # # The variable ZEPHYR_CURRENT_LIBRARY should seldomly be needed since # the zephyr libraries have methods that modify the libraries. These # methods have the signature: zephyr_library_ # # The methods are wrappers around the CMake target_* functions. See # https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html for # documentation on the underlying target_* functions. # # The methods modify the CMake target_* API to reduce boilerplate; # PRIVATE is assumed # The target is assumed to be ZEPHYR_CURRENT_LIBRARY # Constructor with a directory-inferred name macro(zephyr_library) zephyr_library_get_current_dir_lib_name(lib_name) zephyr_library_named(${lib_name}) endmacro() # Determines what the current directory's lib name would be and writes # it to the argument "lib_name" macro(zephyr_library_get_current_dir_lib_name lib_name) # Remove the prefix (/home/sebo/zephyr/driver/serial/CMakeLists.txt => driver/serial/CMakeLists.txt) file(RELATIVE_PATH name $ENV{ZEPHYR_BASE} ${CMAKE_CURRENT_LIST_FILE}) # Remove the filename (driver/serial/CMakeLists.txt => driver/serial) get_filename_component(name ${name} DIRECTORY) # Replace / with __ (driver/serial => driver__serial) string(REGEX REPLACE "/" "__" name ${name}) set(${lib_name} ${name}) endmacro() # Constructor with an explicitly given name. macro(zephyr_library_named name) # This is a macro because we need add_library() to be executed # within the scope of the caller. set(ZEPHYR_CURRENT_LIBRARY ${name}) add_library(${name} STATIC "") zephyr_append_cmake_library(${name}) target_link_libraries(${name} zephyr_interface) endmacro() function(zephyr_link_interface interface) target_link_libraries(interface INTERFACE zephyr_interface) endfunction() # # zephyr_library versions of normal CMake target_ functions # function(zephyr_library_sources source) target_sources(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${source} ${ARGN}) endfunction() function(zephyr_library_include_directories) target_include_directories(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${ARGN}) endfunction() function(zephyr_library_link_libraries item) target_link_libraries(${ZEPHYR_CURRENT_LIBRARY} ${item} ${ARGN}) endfunction() function(zephyr_library_compile_definitions item) target_compile_definitions(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${item} ${ARGN}) endfunction() function(zephyr_library_compile_options item) target_compile_options(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${item} ${ARGN}) endfunction() function(zephyr_library_cc_option) foreach(arg ${ARGV}) target_cc_option(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${arg}) endforeach() endfunction() # Add the existing CMake library 'library' to the global list of # Zephyr CMake libraries. This is done automatically by the # constructor but must called explicitly on CMake libraries that do # not use a zephyr library constructor, but have source files that # need to be included in the build. function(zephyr_append_cmake_library library) set_property(GLOBAL APPEND PROPERTY ZEPHYR_LIBS ${library}) endfunction() ######################################################## # 2. Kconfig-aware extensions ######################################################## # # Kconfig is a configuration language developed for the Linux # kernel. The below functions integrate CMake with Kconfig. # # 2.1 *_if_kconfig # # Functions for conditionally including directories and source files # that have matching KConfig values. # # zephyr_library_sources_if_kconfig(fft.c) # is the same as # zephyr_library_sources_ifdef(CONFIG_FFT fft.c) # # add_subdirectory_if_kconfig(serial) # is the same as # add_subdirectory_ifdef(CONFIG_SERIAL serial) function(add_subdirectory_if_kconfig dir) string(TOUPPER config_${dir} UPPER_CASE_CONFIG) add_subdirectory_ifdef(${UPPER_CASE_CONFIG} ${dir}) endfunction() function(target_sources_if_kconfig target scope item) get_filename_component(item_basename ${item} NAME_WE) string(TOUPPER CONFIG_${item_basename} UPPER_CASE_CONFIG) target_sources_ifdef(${UPPER_CASE_CONFIG} ${target} ${scope} ${item}) endfunction() function(zephyr_library_sources_if_kconfig item) get_filename_component(item_basename ${item} NAME_WE) string(TOUPPER CONFIG_${item_basename} UPPER_CASE_CONFIG) zephyr_library_sources_ifdef(${UPPER_CASE_CONFIG} ${item}) endfunction() function(zephyr_sources_if_kconfig item) get_filename_component(item_basename ${item} NAME_WE) string(TOUPPER CONFIG_${item_basename} UPPER_CASE_CONFIG) zephyr_sources_ifdef(${UPPER_CASE_CONFIG} ${item}) endfunction() # 2.2 Misc # # Parse a KConfig formatted file (typically named *.config) and # introduce all the CONF_ variables into the CMake namespace function(import_kconfig config_file) # Parse the lines prefixed with CONFIG_ in ${config_file} file( STRINGS ${config_file} DOT_CONFIG_LIST REGEX "^CONFIG_" ) foreach (CONFIG ${DOT_CONFIG_LIST}) # CONFIG looks like: CONFIG_NET_BUF=y # Match the first part, the variable name string(REGEX MATCH "[^=]+" CONF_VARIABLE_NAME ${CONFIG}) # Match the second part, variable value string(REGEX MATCH "=(.+$)" CONF_VARIABLE_VALUE ${CONFIG}) # The variable name match we just did included the '=' symbol. To just get the # part on the RHS we use match group 1 set(CONF_VARIABLE_VALUE ${CMAKE_MATCH_1}) if("${CONF_VARIABLE_VALUE}" MATCHES "^\"(.*)\"$") # Is surrounded by quotes set(CONF_VARIABLE_VALUE ${CMAKE_MATCH_1}) endif() set("${CONF_VARIABLE_NAME}" "${CONF_VARIABLE_VALUE}" PARENT_SCOPE) endforeach() endfunction() ######################################################## # 3. CMake-generic extensions ######################################################## # # These functions extend the CMake API in a way that is not particular # to Zephyr. Primarily they work around limitations in the CMake # language to allow cleaner build scripts. # 3.1. *_ifdef # # Functions for conditionally executing CMake functions with oneliners # e.g. # # if(CONFIG_FFT) # zephyr_library_source( # fft_32.c # fft_utils.c # ) # endif() # # Becomes # # zephyr_source_ifdef( # CONFIG_FFT # fft_32.c # fft_utils.c # ) # # More Generally # "_ifdef(CONDITION args)" # Becomes # """ # if(CONDITION) # (args) # endif() # """ # # ifdef functions are added on an as-need basis. See # https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html for # a list of available functions. function(add_subdirectory_ifdef feature_toggle dir) if(${${feature_toggle}}) add_subdirectory(${dir}) endif() endfunction() function(target_sources_ifdef feature_toggle target scope item) if(${${feature_toggle}}) target_sources(${target} ${scope} ${item} ${ARGN}) endif() endfunction() function(target_compile_definitions_ifdef feature_toggle target scope item) if(${${feature_toggle}}) target_compile_definitions(${target} ${scope} ${item} ${ARGN}) endif() endfunction() function(target_include_directories_ifdef feature_toggle target scope item) if(${${feature_toggle}}) target_include_directories(${target} ${scope} ${item} ${ARGN}) endif() endfunction() function(target_link_libraries_ifdef feature_toggle target item) if(${${feature_toggle}}) target_link_libraries(${target} ${item} ${ARGN}) endif() endfunction() function(add_compile_option_ifdef feature_toggle option) if(${${feature_toggle}}) add_compile_options(${option}) endif() endfunction() function(target_compile_option_ifdef feature_toggle target scope option) if(${feature_toggle}) target_compile_options(${target} ${scope} ${option}) endif() endfunction() function(target_cc_option_ifdef feature_toggle target scope option) if(${feature_toggle}) target_cc_option(${target} ${scope} ${option}) endif() endfunction() function(zephyr_library_sources_ifdef feature_toggle source) if(${${feature_toggle}}) zephyr_library_sources(${source} ${ARGN}) endif() endfunction() function(zephyr_sources_ifdef feature_toggle) if(${${feature_toggle}}) zephyr_sources(${ARGN}) endif() endfunction() function(zephyr_sources_ifndef feature_toggle) if(NOT ${feature_toggle}) zephyr_sources(${ARGN}) endif() endfunction() function(zephyr_cc_option_ifdef feature_toggle) if(${${feature_toggle}}) zephyr_cc_option(${ARGN}) endif() endfunction() function(zephyr_link_libraries_ifdef feature_toggle) if(${${feature_toggle}}) zephyr_link_libraries(${ARGN}) endif() endfunction() function(zephyr_compile_options_ifdef feature_toggle) if(${${feature_toggle}}) zephyr_compile_options(${ARGN}) endif() endfunction() function(zephyr_compile_definitions_ifdef feature_toggle) if(${${feature_toggle}}) zephyr_compile_definitions(${ARGN}) endif() endfunction() function(zephyr_include_directories_ifdef feature_toggle) if(${${feature_toggle}}) zephyr_include_directories(${ARGN}) endif() endfunction() function(zephyr_library_compile_definitions_ifdef feature_toggle item) if(${${feature_toggle}}) zephyr_library_compile_definitions(${item} ${ARGN}) endif() endfunction() function(zephyr_library_compile_options_ifdef feature_toggle item) if(${${feature_toggle}}) zephyr_library_compile_options(${item} ${ARGN}) endif() endfunction() function(zephyr_link_interface_ifdef feature_toggle interface) if(${${feature_toggle}}) target_link_libraries(${interface} INTERFACE zephyr_interface) endif() endfunction() function(zephyr_library_link_libraries_ifdef feature_toggle item) if(${${feature_toggle}}) zephyr_library_link_libraries(${item}) endif() endfunction() # 3.2. *_ifndef # See 3.1 *_ifdef function(set_ifndef variable value) if(NOT ${variable}) set(${variable} ${value} PARENT_SCOPE) endif() endfunction() function(target_cc_option_ifndef feature_toggle target scope option) if(NOT ${feature_toggle}) target_cc_option(${target} ${scope} ${option}) endif() endfunction() function(zephyr_cc_option_ifndef feature_toggle) if(NOT ${feature_toggle}) zephyr_cc_option(${ARGN}) endif() endfunction() function(zephyr_compile_options_ifndef feature_toggle) if(NOT ${feature_toggle}) zephyr_compile_options(${ARGN}) endif() endfunction() # 3.2. *_option Compiler-compatibility checks # # Utility functions for silently omitting compiler flags when the # compiler lacks support. *_cc_option was ported from KBuild, see # cc-option in # https://www.kernel.org/doc/Documentation/kbuild/makefiles.txt # function(target_cc_option target scope option) string(MAKE_C_IDENTIFIER check${option} check) check_c_compiler_flag(${option} ${check}) target_compile_option_ifdef(${check} ${target} ${scope} ${option}) endfunction() # Support an optional second option for when the first option is # not supported. function(target_cc_option_fallback target scope option1 option2) string(MAKE_C_IDENTIFIER check${option1} check) check_c_compiler_flag(${option1} ${check}) if(${check}) target_compile_options(${target} ${scope} ${option1}) else() target_compile_options(${target} ${scope} ${option2}) endif() endfunction() function(target_ld_options target scope) foreach(option ${ARGN}) string(MAKE_C_IDENTIFIER check${option} check) check_c_compiler_flag(${option} ${check}) target_link_libraries_ifdef(${check} ${target} ${scope} ${option}) endforeach() endfunction() # 3.4. Debugging CMake # Usage: # print(BOARD) # # will print: "BOARD: nrf52_pca10040" function(print arg) message(STATUS "${arg}: ${${arg}}") endfunction() # Usage: # assert(ZEPHYR_GCC_VARIANT "ZEPHYR_GCC_VARIANT not set.") # # will cause a FATAL_ERROR and print an error message if the first # expression is false macro(assert test comment) if(NOT ${test}) message(FATAL_ERROR "Assertion failed: ${comment}") endif() endmacro() # Usage: # assert_exists(CMAKE_READELF) # # will cause a FATAL_ERROR if there is no file or directory behind the # variable macro(assert_exists var) if(NOT EXISTS ${${var}}) message(FATAL_ERROR "No such file or directory: ${var}: '${${var}}'") endif() endmacro()