zephyr/cmake/app/boilerplate.cmake

491 lines
18 KiB
CMake
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# This file must be included into the toplevel CMakeLists.txt file of
# Zephyr applications, e.g. zephyr/samples/hello_world/CMakeLists.txt
# must start with the line:
#
# include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
#
# It exists to reduce boilerplate code that Zephyr expects to be in
# application CMakeLists.txt code.
#
# Omitting it is permitted, but doing so incurs a maintenance cost as
# the application must manage upstream changes to this file.
# app is a CMake library containing all the application code and is
# modified by the entry point ${APPLICATION_SOURCE_DIR}/CMakeLists.txt
# that was specified when cmake was called.
# CMake version 3.13.1 is the real minimum supported version.
#
# Unfortunately CMake requires the toplevel CMakeLists.txt file to
# define the required version, not even invoking it from an included
# file, like boilerplate.cmake, is sufficient. It is however permitted
# to have multiple invocations of cmake_minimum_required.
#
# Under these restraints we use a second 'cmake_minimum_required'
# invocation in every toplevel CMakeLists.txt.
cmake_minimum_required(VERSION 3.13.1)
# CMP0002: "Logical target names must be globally unique"
cmake_policy(SET CMP0002 NEW)
if(NOT (${CMAKE_VERSION} VERSION_LESS "3.13.0"))
# Use the old CMake behaviour until 3.13.x is required and the build
# scripts have been ported to the new behaviour.
# CMP0079: "target_link_libraries() allows use with targets in other directories"
cmake_policy(SET CMP0079 OLD)
endif()
define_property(GLOBAL PROPERTY ZEPHYR_LIBS
BRIEF_DOCS "Global list of all Zephyr CMake libs that should be linked in"
FULL_DOCS "Global list of all Zephyr CMake libs that should be linked in.
zephyr_library() appends libs to this list.")
set_property(GLOBAL PROPERTY ZEPHYR_LIBS "")
define_property(GLOBAL PROPERTY ZEPHYR_INTERFACE_LIBS
BRIEF_DOCS "Global list of all Zephyr interface libs that should be linked in."
FULL_DOCS "Global list of all Zephyr interface libs that should be linked in.
zephyr_interface_library_named() appends libs to this list.")
set_property(GLOBAL PROPERTY ZEPHYR_INTERFACE_LIBS "")
define_property(GLOBAL PROPERTY GENERATED_KERNEL_OBJECT_FILES
BRIEF_DOCS "Object files that are generated after Zephyr has been linked once."
FULL_DOCS "\
Object files that are generated after Zephyr has been linked once.\
May include mmu tables, etc."
)
set_property(GLOBAL PROPERTY GENERATED_KERNEL_OBJECT_FILES "")
define_property(GLOBAL PROPERTY GENERATED_KERNEL_SOURCE_FILES
BRIEF_DOCS "Source files that are generated after Zephyr has been linked once."
FULL_DOCS "\
Source files that are generated after Zephyr has been linked once.\
May include isr_tables.c etc."
)
set_property(GLOBAL PROPERTY GENERATED_KERNEL_SOURCE_FILES "")
set(APPLICATION_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE PATH "Application Source Directory")
set(APPLICATION_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR} CACHE PATH "Application Binary Directory")
set(__build_dir ${CMAKE_CURRENT_BINARY_DIR}/zephyr)
set(PROJECT_BINARY_DIR ${__build_dir})
add_custom_target(code_data_relocation_target)
# CMake's 'project' concept has proven to not be very useful for Zephyr
# due in part to how Zephyr is organized and in part to it not fitting well
# with cross compilation.
# Zephyr therefore tries to rely as little as possible on project()
# and its associated variables, e.g. PROJECT_SOURCE_DIR.
# It is recommended to always use ZEPHYR_BASE instead of PROJECT_SOURCE_DIR
# when trying to reference ENV${ZEPHYR_BASE}.
set(PROJECT_SOURCE_DIR $ENV{ZEPHYR_BASE})
# Convert path to use the '/' separator
string(REPLACE "\\" "/" PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR})
set(ZEPHYR_BINARY_DIR ${PROJECT_BINARY_DIR})
set(ZEPHYR_BASE ${PROJECT_SOURCE_DIR})
set(AUTOCONF_H ${__build_dir}/include/generated/autoconf.h)
# Re-configure (Re-execute all CMakeLists.txt code) when autoconf.h changes
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${AUTOCONF_H})
#
# Import more CMake functions and macros
#
include(CheckCCompilerFlag)
include(CheckCXXCompilerFlag)
include(${ZEPHYR_BASE}/cmake/extensions.cmake)
include(${ZEPHYR_BASE}/cmake/version.cmake) # depends on hex.cmake
#
# Find tools
#
include(${ZEPHYR_BASE}/cmake/python.cmake)
include(${ZEPHYR_BASE}/cmake/git.cmake) # depends on version.cmake
include(${ZEPHYR_BASE}/cmake/ccache.cmake)
if(${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR})
message(FATAL_ERROR "Source directory equals build directory.\
In-source builds are not supported.\
Please specify a build directory, e.g. cmake -Bbuild -H.")
endif()
add_custom_target(
pristine
COMMAND ${CMAKE_COMMAND} -P ${ZEPHYR_BASE}/cmake/pristine.cmake
# Equivalent to rm -rf build/*
)
# The BOARD can be set by 3 sources. Through environment variables,
# through the cmake CLI, and through CMakeLists.txt.
#
# CLI has the highest precedence, then comes environment variables,
# and then finally CMakeLists.txt.
#
# A user can ignore all the precedence rules if he simply always uses
# the same source. E.g. always specifies -DBOARD= on the command line,
# always has an environment variable set, or always has a set(BOARD
# foo) line in his CMakeLists.txt and avoids mixing sources.
#
# The selected BOARD can be accessed through the variable 'BOARD'.
# Read out the cached board value if present
get_property(cached_board_value CACHE BOARD PROPERTY VALUE)
# There are actually 4 sources, the three user input sources, and the
# previously used value (CACHED_BOARD). The previously used value has
# precedence, and if we detect that the user is trying to change the
# value we give him a warning about needing to clean the build
# directory to be able to change boards.
set(board_cli_argument ${cached_board_value}) # Either new or old
if(board_cli_argument STREQUAL CACHED_BOARD)
# We already have a CACHED_BOARD so there is no new input on the CLI
unset(board_cli_argument)
endif()
set(board_app_cmake_lists ${BOARD})
if(cached_board_value STREQUAL BOARD)
# The app build scripts did not set a default, The BOARD we are
# reading is the cached value from the CLI
unset(board_app_cmake_lists)
endif()
if(CACHED_BOARD)
# Warn the user if it looks like he is trying to change the board
# without cleaning first
if(board_cli_argument)
if(NOT (CACHED_BOARD STREQUAL board_cli_argument))
message(WARNING "The build directory must be cleaned pristinely when changing boards")
# TODO: Support changing boards without requiring a clean build
endif()
endif()
set(BOARD ${CACHED_BOARD})
elseif(board_cli_argument)
set(BOARD ${board_cli_argument})
elseif(DEFINED ENV{BOARD})
set(BOARD $ENV{BOARD})
elseif(board_app_cmake_lists)
set(BOARD ${board_app_cmake_lists})
else()
message(FATAL_ERROR "BOARD is not being defined on the CMake command-line in the environment or by the app.")
endif()
assert(BOARD "BOARD not set")
message(STATUS "Selected BOARD ${BOARD}")
# Store the selected board in the cache
set(CACHED_BOARD ${BOARD} CACHE STRING "Selected board")
# The SHIELD can be set by 3 sources. Through environment variables,
# through the cmake CLI, and through CMakeLists.txt.
#
# CLI has the highest precedence, then comes environment variables,
# and then finally CMakeLists.txt.
#
# A user can ignore all the precedence rules if he simply always uses
# the same source. E.g. always specifies -DSHIELD= on the command line,
# always has an environment variable set, or always has a set(SHIELD
# foo) line in his CMakeLists.txt and avoids mixing sources.
#
# The selected SHIELD can be accessed through the variable 'SHIELD'.
# Read out the cached shield value if present
get_property(cached_shield_value CACHE SHIELD PROPERTY VALUE)
# There are actually 4 sources, the three user input sources, and the
# previously used value (CACHED_SHIELD). The previously used value has
# precedence, and if we detect that the user is trying to change the
# value we give him a warning about needing to clean the build
# directory to be able to change shields.
set(shield_cli_argument ${cached_shield_value}) # Either new or old
if(shield_cli_argument STREQUAL CACHED_SHIELD)
# We already have a CACHED_SHIELD so there is no new input on the CLI
unset(shield_cli_argument)
endif()
set(shield_app_cmake_lists ${SHIELD})
if(cached_shield_value STREQUAL SHIELD)
# The app build scripts did not set a default, The SHIELD we are
# reading is the cached value from the CLI
unset(shield_app_cmake_lists)
endif()
if(CACHED_SHIELD)
# Warn the user if it looks like he is trying to change the shield
# without cleaning first
if(shield_cli_argument)
if(NOT (CACHED_SHIELD STREQUAL shield_cli_argument))
message(WARNING "The build directory must be cleaned pristinely when changing shields")
# TODO: Support changing shields without requiring a clean build
endif()
endif()
set(SHIELD ${CACHED_SHIELD})
elseif(shield_cli_argument)
set(SHIELD ${shield_cli_argument})
elseif(DEFINED ENV{SHIELD})
set(SHIELD $ENV{SHIELD})
elseif(shield_app_cmake_lists)
set(SHIELD ${shield_app_cmake_lists})
endif()
# Store the selected shield in the cache
set(CACHED_SHIELD ${SHIELD} CACHE STRING "Selected shield")
# 'BOARD_ROOT' is a prioritized list of directories where boards may
# be found. It always includes ${ZEPHYR_BASE} at the lowest priority.
list(APPEND BOARD_ROOT ${ZEPHYR_BASE})
if(NOT SOC_ROOT)
set(SOC_DIR ${ZEPHYR_BASE}/soc)
else()
set(SOC_DIR ${SOC_ROOT}/soc)
endif()
# Use BOARD to search for a '_defconfig' file.
# e.g. zephyr/boards/arm/96b_carbon_nrf51/96b_carbon_nrf51_defconfig.
# When found, use that path to infer the ARCH we are building for.
foreach(root ${BOARD_ROOT})
# NB: find_path will return immediately if the output variable is
# already set
find_path(BOARD_DIR
NAMES ${BOARD}_defconfig
PATHS ${root}/boards/*/*
NO_DEFAULT_PATH
)
if(BOARD_DIR AND NOT (${root} STREQUAL ${ZEPHYR_BASE}))
set(USING_OUT_OF_TREE_BOARD 1)
endif()
set(shield_dir ${root}/boards/shields)
if(DEFINED SHIELD)
string(REPLACE " " ";" SHIELD_AS_LIST "${SHIELD}")
endif()
# Match the .overlay files in the shield directories to make sure we are
# finding shields, e.g. x_nucleo_iks01a1/x_nucleo_iks01a1.overlay
file(GLOB_RECURSE shields_refs_list
RELATIVE ${shield_dir}
${shield_dir}/*/*.overlay
)
# The above gives a list like
# x_nucleo_iks01a1/x_nucleo_iks01a1.overlay;x_nucleo_iks01a2/x_nucleo_iks01a2.overlay
# we construct a list of shield names by extracting file name and
# removing the extension.
foreach(shield_path ${shields_refs_list})
get_filename_component(shield ${shield_path} NAME_WE)
list(APPEND SHIELD_LIST ${shield})
endforeach()
if(DEFINED SHIELD)
foreach(s ${SHIELD_AS_LIST})
list(FIND SHIELD_LIST ${s} _idx)
if (NOT _idx EQUAL -1)
list(GET shields_refs_list ${_idx} s_path)
# if shield config flag is on, add shield overlay to the shield overlays
# list and dts_fixup file to the shield fixup file
list(APPEND
shield_dts_files
${shield_dir}/${s_path}
)
list(APPEND
shield_dts_fixups
${shield_dir}/${s}/dts_fixup.h
)
else()
list(APPEND NOT_FOUND_SHIELD_LIST ${s})
endif()
endforeach()
endif()
endforeach()
if(NOT BOARD_DIR)
message("No board named '${BOARD}' found")
print_usage()
unset(CACHED_BOARD CACHE)
message(FATAL_ERROR "Invalid usage")
endif()
if(DEFINED SHIELD AND DEFINED NOT_FOUND_SHIELD_LIST)
foreach (s ${NOT_FOUND_SHIELD_LIST})
message("No shield named '${s}' found")
endforeach()
print_usage()
unset(CACHED_SHIELD CACHE)
message(FATAL_ERROR "Invalid usage")
endif()
get_filename_component(BOARD_ARCH_DIR ${BOARD_DIR} DIRECTORY)
get_filename_component(BOARD_FAMILY ${BOARD_DIR} NAME)
get_filename_component(ARCH ${BOARD_ARCH_DIR} NAME)
if(CONF_FILE)
# CONF_FILE has either been specified on the cmake CLI or is already
# in the CMakeCache.txt. This has precedence over the environment
# variable CONF_FILE and the default prj.conf
elseif(DEFINED ENV{CONF_FILE})
set(CONF_FILE $ENV{CONF_FILE})
elseif(COMMAND set_conf_file)
set_conf_file()
elseif(EXISTS ${APPLICATION_SOURCE_DIR}/prj_${BOARD}.conf)
set(CONF_FILE ${APPLICATION_SOURCE_DIR}/prj_${BOARD}.conf)
elseif(EXISTS ${APPLICATION_SOURCE_DIR}/prj.conf)
set(CONF_FILE ${APPLICATION_SOURCE_DIR}/prj.conf)
endif()
set(CONF_FILE ${CONF_FILE} CACHE STRING "If desired, you can build the application using\
the configuration settings specified in an alternate .conf file using this parameter. \
These settings will override the settings in the applications .config file or its default .conf file.\
Multiple files may be listed, e.g. CONF_FILE=\"prj1.conf prj2.conf\"")
if(DTC_OVERLAY_FILE)
# DTC_OVERLAY_FILE has either been specified on the cmake CLI or is already
# in the CMakeCache.txt. This has precedence over the environment
# variable DTC_OVERLAY_FILE
elseif(DEFINED ENV{DTC_OVERLAY_FILE})
set(DTC_OVERLAY_FILE $ENV{DTC_OVERLAY_FILE})
elseif(EXISTS ${APPLICATION_SOURCE_DIR}/${BOARD}.overlay)
set(DTC_OVERLAY_FILE ${APPLICATION_SOURCE_DIR}/${BOARD}.overlay)
endif()
set(DTC_OVERLAY_FILE ${DTC_OVERLAY_FILE} CACHE STRING "If desired, you can \
build the application using the DT configuration settings specified in an \
alternate .overlay file using this parameter. These settings will override the \
settings in the board's .dts file. Multiple files may be listed, e.g. \
DTC_OVERLAY_FILE=\"dts1.overlay dts2.overlay\"")
# Prevent CMake from testing the toolchain
set(CMAKE_C_COMPILER_FORCED 1)
set(CMAKE_CXX_COMPILER_FORCED 1)
include(${ZEPHYR_BASE}/cmake/host-tools.cmake)
# DTS should be close to kconfig because CONFIG_ variables from
# kconfig and dts should be available at the same time.
#
# The DT system uses a C preprocessor for it's code generation needs.
# This creates an awkward chicken-and-egg problem, because we don't
# always know exactly which toolchain the user needs until we know
# more about the target, e.g. after DT and Kconfig.
#
# To resolve this we find "some" C toolchain, configure it generically
# with the minimal amount of configuration needed to have it
# preprocess DT sources, and then, after we have finished processing
# both DT and Kconfig we complete the target-specific configuration,
# and possibly change the toolchain.
include(${ZEPHYR_BASE}/cmake/generic_toolchain.cmake)
include(${ZEPHYR_BASE}/cmake/dts.cmake)
include(${ZEPHYR_BASE}/cmake/kconfig.cmake)
set(SOC_NAME ${CONFIG_SOC})
set(SOC_SERIES ${CONFIG_SOC_SERIES})
set(SOC_FAMILY ${CONFIG_SOC_FAMILY})
if("${SOC_SERIES}" STREQUAL "")
set(SOC_PATH ${SOC_NAME})
else()
set(SOC_PATH ${SOC_FAMILY}/${SOC_SERIES})
endif()
include(${ZEPHYR_BASE}/cmake/target_toolchain.cmake)
set(KERNEL_NAME ${CONFIG_KERNEL_BIN_NAME})
set(KERNEL_ELF_NAME ${KERNEL_NAME}.elf)
set(KERNEL_BIN_NAME ${KERNEL_NAME}.bin)
set(KERNEL_HEX_NAME ${KERNEL_NAME}.hex)
set(KERNEL_MAP_NAME ${KERNEL_NAME}.map)
set(KERNEL_LST_NAME ${KERNEL_NAME}.lst)
set(KERNEL_S19_NAME ${KERNEL_NAME}.s19)
set(KERNEL_EXE_NAME ${KERNEL_NAME}.exe)
set(KERNEL_STAT_NAME ${KERNEL_NAME}.stat)
set(KERNEL_STRIP_NAME ${KERNEL_NAME}.strip)
# Populate USER_CACHE_DIR with a directory that user applications may
# write cache files to.
if(NOT DEFINED USER_CACHE_DIR)
find_appropriate_cache_directory(USER_CACHE_DIR)
endif()
message(STATUS "Cache files will be written to: ${USER_CACHE_DIR}")
include(${BOARD_DIR}/board.cmake OPTIONAL)
# If we are using a suitable ethernet driver inside qemu, then these options
# must be set, otherwise a zephyr instance cannot receive any network packets.
# The Qemu supported ethernet driver should define CONFIG_ETH_NIC_MODEL
# string that tells what nic model Qemu should use.
if(CONFIG_QEMU_TARGET)
if(CONFIG_NET_QEMU_ETHERNET)
if(CONFIG_ETH_NIC_MODEL)
list(APPEND QEMU_FLAGS_${ARCH}
-nic tap,model=${CONFIG_ETH_NIC_MODEL},script=no,downscript=no,ifname=zeth
)
else()
message(FATAL_ERROR "
No Qemu ethernet driver configured!
Enable Qemu supported ethernet driver like e1000 at drivers/ethernet"
)
endif()
else()
list(APPEND QEMU_FLAGS_${ARCH}
-net none
)
endif()
endif()
# "app" is a CMake library containing all the application code and is
# modified by the entry point ${APPLICATION_SOURCE_DIR}/CMakeLists.txt
# that was specified when cmake was called.
zephyr_library_named(app)
set_property(TARGET app PROPERTY ARCHIVE_OUTPUT_DIRECTORY app)
add_subdirectory(${ZEPHYR_BASE} ${__build_dir})
# Link 'app' with the Zephyr interface libraries.
#
# NB: This must be done in boilerplate.cmake because 'app' can only be
# modified in the CMakeLists.txt file that created it. And it must be
# done after 'add_subdirectory(${ZEPHYR_BASE} ${__build_dir})'
# because interface libraries are defined while processing that
# subdirectory.
get_property(ZEPHYR_INTERFACE_LIBS_PROPERTY GLOBAL PROPERTY ZEPHYR_INTERFACE_LIBS)
foreach(boilerplate_lib ${ZEPHYR_INTERFACE_LIBS_PROPERTY})
# Linking 'app' with 'boilerplate_lib' causes 'app' to inherit the INTERFACE
# properties of 'boilerplate_lib'. The most common property is 'include
# directories', but it is also possible to have defines and compiler
# flags in the interface of a library.
#
string(TOUPPER ${boilerplate_lib} boilerplate_lib_upper_case) # Support lowercase lib names
target_link_libraries_ifdef(
CONFIG_APP_LINK_WITH_${boilerplate_lib_upper_case}
app
PUBLIC
${boilerplate_lib}
)
endforeach()
if("${CMAKE_EXTRA_GENERATOR}" STREQUAL "Eclipse CDT4")
# Call the amendment function before .project and .cproject generation
# C and CXX includes, defines in .cproject without __cplusplus
# with project includes and defines
include(${ZEPHYR_BASE}/cmake/ide/eclipse_cdt4_generator_amendment.cmake)
eclipse_cdt4_generator_amendment(1)
endif()