From ccfa1345332118c06bbf9c2dec9d8077c535a766 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Fri, 19 Oct 2018 11:51:51 +0800 Subject: [PATCH] build system: support for multiple targets --- Kconfig | 16 +++++ components/esp32/CMakeLists.txt | 2 + components/esp32/Kconfig | 6 ++ components/soc/CMakeLists.txt | 17 +++-- components/soc/component.mk | 3 +- components/soc/test/CMakeLists.txt | 12 ++-- components/soc/test/component.mk | 3 +- docs/en/api-guides/build-system-cmake.rst | 48 ++++++++++--- make/project.mk | 9 +++ tools/ci/test_build_system_cmake.sh | 35 +++++++++ tools/cmake/component_utils.cmake | 70 ++++++++++++++++++ tools/cmake/components.cmake | 6 ++ tools/cmake/idf_functions.cmake | 4 +- tools/cmake/kconfig.cmake | 4 ++ tools/cmake/project.cmake | 12 +++- tools/cmake/scripts/expand_requirements.cmake | 72 ++----------------- tools/cmake/targets.cmake | 57 +++++++++++++++ tools/cmake/toolchain-esp32.cmake | 2 + 18 files changed, 281 insertions(+), 97 deletions(-) create mode 100644 tools/cmake/component_utils.cmake create mode 100644 tools/cmake/targets.cmake diff --git a/Kconfig b/Kconfig index 98e5f2a9e9..2df3e971ba 100644 --- a/Kconfig +++ b/Kconfig @@ -8,6 +8,22 @@ config IDF_CMAKE bool option env="IDF_CMAKE" + +# A proxy to get environment variable $IDF_TARGET +config IDF_TARGET_ENV + string + option env="IDF_TARGET" + +# This option records the IDF target when sdkconfig is generated the first time. +# It is not updated if environment variable $IDF_TARGET changes later, and +# the build system is responsible for detecting the mismatch between +# CONFIG_IDF_TARGET and $IDF_TARGET. +config IDF_TARGET + string + default "IDF_TARGET_NOT_SET" if IDF_TARGET_ENV="" + default IDF_TARGET_ENV + + menu "SDK tool configuration" config TOOLPREFIX string "Compiler toolchain path/prefix" diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index 92b75a4cee..daa000c4f1 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -1,3 +1,5 @@ +require_idf_targets(esp32) + if(BOOTLOADER_BUILD) # For bootloader, all we need from esp32 is headers set(COMPONENT_ADD_INCLUDEDIRS include) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index b71528b3e1..b53a684098 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -1,5 +1,11 @@ menu "ESP32-specific" +# Hidden option to support checking for this specific target in C code and Kconfig files +config IDF_TARGET_ESP32 + bool + default "y" if IDF_TARGET="esp32" + default "n" + choice ESP32_DEFAULT_CPU_FREQ_MHZ prompt "CPU frequency" default ESP32_DEFAULT_CPU_FREQ_160 diff --git a/components/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index b46bde8504..0188244a2f 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -1,12 +1,15 @@ -set(SOC_NAME esp32) +set(SOC_NAME ${IDF_TARGET}) -include(${IDF_PATH}/components/soc/${SOC_NAME}/sources.cmake) +if(EXISTS "${COMPONENT_PATH}/${SOC_NAME}") + include(${COMPONENT_PATH}/${SOC_NAME}/sources.cmake) -spaces2list(SOC_SRCS) -add_prefix(COMPONENT_SRCS "${SOC_NAME}/" ${SOC_SRCS}) + spaces2list(SOC_SRCS) + add_prefix(COMPONENT_SRCS "${SOC_NAME}/" ${SOC_SRCS}) + set(COMPONENT_ADD_INCLUDEDIRS ${SOC_NAME}/include) +endif() + +list(APPEND COMPONENT_ADD_INCLUDEDIRS include) list(APPEND COMPONENT_SRCS "src/memory_layout_utils.c") -set(COMPONENT_ADD_INCLUDEDIRS ${SOC_NAME}/include include) - set(COMPONENT_REQUIRES) -register_component() \ No newline at end of file +register_component() diff --git a/components/soc/component.mk b/components/soc/component.mk index 6dfb407f1f..99fae13864 100644 --- a/components/soc/component.mk +++ b/components/soc/component.mk @@ -1,5 +1,4 @@ -# currently the only SoC supported; to be moved into Kconfig -SOC_NAME := esp32 +SOC_NAME := $(IDF_TARGET) COMPONENT_SRCDIRS := $(SOC_NAME) src/ diff --git a/components/soc/test/CMakeLists.txt b/components/soc/test/CMakeLists.txt index f4e89ade2f..b026d8f536 100644 --- a/components/soc/test/CMakeLists.txt +++ b/components/soc/test/CMakeLists.txt @@ -1,7 +1,9 @@ -set(SOC_NAME esp32) -set(COMPONENT_SRCDIRS "../${SOC_NAME}/test") -set(COMPONENT_ADD_INCLUDEDIRS "../${SOC_NAME}/test") +set(SOC_NAME ${IDF_TARGET}) +if(EXISTS "../${SOC_NAME}/test") + set(COMPONENT_SRCDIRS "../${SOC_NAME}/test") + set(COMPONENT_ADD_INCLUDEDIRS "../${SOC_NAME}/test") -set(COMPONENT_REQUIRES unity) + set(COMPONENT_REQUIRES unity) -register_component() \ No newline at end of file + register_component() +endif() diff --git a/components/soc/test/component.mk b/components/soc/test/component.mk index f8de6c86f2..3e6df4ba73 100644 --- a/components/soc/test/component.mk +++ b/components/soc/test/component.mk @@ -1,5 +1,4 @@ -# currently the only SoC supported; to be moved into Kconfig -SOC_NAME := esp32 +SOC_NAME := $(IDF_TARGET) COMPONENT_SRCDIRS := ../$(SOC_NAME)/test diff --git a/docs/en/api-guides/build-system-cmake.rst b/docs/en/api-guides/build-system-cmake.rst index b6888870a7..0d8b0a07c0 100644 --- a/docs/en/api-guides/build-system-cmake.rst +++ b/docs/en/api-guides/build-system-cmake.rst @@ -43,6 +43,8 @@ Concepts - "components" are modular pieces of standalone code which are compiled into static libraries (.a files) and linked into an app. Some are provided by ESP-IDF itself, others may be sourced from other places. +- "Target" is the hardware for which an application is built. At the moment, ESP-IDF supports only one target, ``esp32``. + Some things are not part of the project: - "ESP-IDF" is not part of the project. Instead it is standalone, and linked to the project via the ``IDF_PATH`` environment variable which holds the path of the ``esp-idf`` directory. This allows the IDF framework to be decoupled from your project. @@ -79,7 +81,6 @@ Type ``idf.py --help`` for a full list of commands. Here are a summary of the mo Building is incremental so if no source files or configuration has changed since the last build, nothing will be done. - ``idf.py clean`` will "clean" the project by deleting build output files from the build directory, forcing a "full rebuild" the next time the project is built. Cleaning doesn't delete CMake configuration output and some other files. - ``idf.py fullclean`` will delete the entire "build" directory contents. This includes all CMake configuration output. The next time the project is built, CMake will configure it from scratch. Note that this option recursively deletes *all* files in the build directory, so use with care. Project configuration is not deleted. -- ``idf.py reconfigure`` re-runs CMake_ even if it doesn't seem to need re-running. This isn't necessary during normal usage, but can be useful after adding/removing files from the source tree. - ``idf.py flash`` will automatically build the project if necessary, and then flash it to an ESP32. The ``-p`` and ``-b`` options can be used to set serial port name and flasher baud rate, respectively. - ``idf.py monitor`` will display serial output from the ESP32. The ``-p`` option can be used to set the serial port name. Type ``Ctrl-]`` to exit the monitor. See :doc:`/get-started/idf-monitor` for more details about using the monitor. @@ -94,6 +95,7 @@ Advanced Commands - There are matching commands ``idf.py app-flash``, etc. to flash only that single part of the project to the ESP32. - ``idf.py -p PORT erase_flash`` will use esptool.py to erase the ESP32's entire flash chip. - ``idf.py size`` prints some size information about the app. ``size-components`` and ``size-files`` are similar commands which print more detailed per-component or per-source-file information, respectively. +- ``idf.py reconfigure`` re-runs CMake_ even if it doesn't seem to need re-running. This isn't necessary during normal usage, but can be useful after adding/removing files from the source tree, or when modifying CMake cache variables. For example, ``idf.py -DNAME='VALUE' reconfigure`` can be used to set variable ``NAME`` in CMake cache to value ``VALUE``. The order of multiple ``idf.py`` commands on the same invocation is not important, they will automatically be executed in the correct order for everything to take effect (ie building before flashing, erasing before flashing, etc.). @@ -319,6 +321,7 @@ The following variables are set at the project level, but available for use in c - ``COMPONENTS``: Names of all components that are included in this build, formatted as a semicolon-delimited CMake list. - ``CONFIG_*``: Each value in the project configuration has a corresponding variable available in make. All names begin with ``CONFIG_``. :doc:`More information here `. - ``IDF_VER``: Git version of ESP-IDF (produced by ``git describe``) +- ``IDF_TARGET``: Name of the target for which the project is being built. If you modify any of these variables inside ``CMakeLists.txt`` then this will not prevent other components from building but it may make your component hard to build and/or debug. @@ -417,6 +420,8 @@ When writing a component - The values of ``COMPONENT_REQUIRES`` and ``COMPONENT_PRIV_REQUIRES`` should not depend on any configuration choices (``CONFIG_xxx`` macros). This is because requirements are expanded before configuration is loaded. Other component variables (like include paths or source files) can depend on configuration choices. - Not setting either or both ``REQUIRES`` variables is fine. If the component has no requirements except for the "common" components needed for RTOS, libc, etc (``COMPONENT_REQUIRES_COMMON``) then both variables can be empty or unset. +Components which support only some targets (values of ``IDF_TARGET``) may call ``require_idf_targets(NAMES...)`` CMake function to express these requirements. In this case the build system will generate an error if the component is included into the build, but does not support selected target. + When creating a project ----------------------- @@ -465,16 +470,17 @@ project function The custom ``project()`` function performs the following steps: +- Determines the target (set by ``IDF_TARGET`` environment variable) and saves the target in CMake cache. If the target set in the environment does not match the one in cache, exits with an error. - Evaluates component dependencies and builds the ``BUILD_COMPONENTS`` list of components to include in the build (see :ref:`above`). - Finds all components in the project (searching ``COMPONENT_DIRS`` and filtering by ``COMPONENTS`` if this is set). - Loads the project configuration from the ``sdkconfig`` file and generates a ``sdkconfig.cmake`` file and a ``sdkconfig.h`` header. These define configuration values in CMake and C/C++, respectively. If the project configuration changes, cmake will automatically be re-run to re-generate these files and re-configure the project. -- Sets the `CMAKE_TOOLCHAIN_FILE`_ variable to the ESP-IDF toolchain file with the Xtensa ESP32 toolchain. -- Declare the actual cmake-level project by calling the `CMake project function `_. -- Load the git version. This includes some magic which will automatically re-run CMake if a new revision is checked out in git. See `File Globbing & Incremental Builds`_. -- Include :ref:`project_include.cmake` files from any components which have them. -- Add each component to the build. Each component CMakeLists file calls ``register_component``, calls the CMake `add_library `_ function to add a library and then adds source files, compile options, etc. -- Add the final app executable to the build. -- Go back and add inter-component dependencies between components (ie adding the public header directories of each component to each other component). +- Sets the `CMAKE_TOOLCHAIN_FILE`_ variable to the correct toolchain file, depending on the target. +- Declares the actual cmake-level project by calling the `CMake project function `_. +- Loads the git version. This includes some magic which will automatically re-run CMake if a new revision is checked out in git. See `File Globbing & Incremental Builds`_. +- Includes :ref:`project_include.cmake` files from any components which have them. +- Adds each component to the build. Each component CMakeLists file calls ``register_component``, calls the CMake `add_library `_ function to add a library and then adds source files, compile options, etc. +- Adds the final app executable to the build. +- Goes back and adds inter-component dependencies between components (ie adding the public header directories of each component to each other component). Browse the :idf_file:`/tools/cmake/project.cmake` file and supporting functions in :idf_file:`/tools/cmake/idf_functions.cmake` for more details. @@ -604,6 +610,16 @@ This can also be used to select or stub out an implementation, as such: endif() +Conditions which depend on the target +------------------------------------- + +The current target is available to CMake files via ``IDF_TARGET`` variable. + +In addition to that, if target ``xyz`` is used (``IDF_TARGET=xyz``), then Kconfig variable ``CONFIG_IDF_TARGET_XYZ`` will be set. + +Note that component dependencies may depend on ``IDF_TARGET`` variable, but not on Kconfig variables. Also one can not use Kconfig variables in ``include`` statements in CMake files, but ``IDF_TARGET`` can be used in such context. + + Source Code Generation ---------------------- @@ -745,6 +761,13 @@ For example projects or other projects where you don't want to specify a full sd To override the name of this file, set the ``SDKCONFIG_DEFAULTS`` environment variable. +Target-dependent sdkconfig defaults +----------------------------------- + +In addition to ``sdkconfig.defaults`` file, build system will also load defaults from ``sdkconfig.defaults.TARGET_NAME`` file, where ``TARGET_NAME`` is the value of ``IDF_TARGET``. For example, for ``esp32`` target, default settings will be taken from ``sdkconfig.defaults`` first, and then from ``sdkconfig.defaults.esp32``. + +If ``SDKCONFIG_DEFAULTS`` is used to override the name of defaults file, the name of target-specific defaults file will be derived from ``SDKCONFIG_DEFAULTS`` value. + Flash arguments =============== @@ -776,6 +799,15 @@ The bootloader is a special "subproject" inside :idf:`/components/bootloader/sub The subproject is inserted as an external project from the top-level project, by the file :idf_file:`/components/bootloader/project_include.cmake`. The main build process runs CMake for the subproject, which includes discovering components (a subset of the main components) and generating a bootloader-specific config (derived from the main ``sdkconfig``). +Selecting the Target +==================== + +Currently ESP-IDF supports one target, ``esp32``. It is used by default by the build system. Developers working on adding multiple target support can change the target as follows:: + + rm sdkconfig + idf.py -DIDF_TARGET=new_target reconfigure + + Writing Pure CMake Components ============================= diff --git a/make/project.mk b/make/project.mk index 7b9bd9b1d8..be7521b589 100644 --- a/make/project.mk +++ b/make/project.mk @@ -91,6 +91,15 @@ ifndef IDF_PATH $(error IDF_PATH variable is not set to a valid directory.) endif +ifdef IDF_TARGET +ifneq ($(IDF_TARGET),esp32) +$(error GNU Make based build system only supports esp32 target, but IDF_TARGET is set to $(IDF_TARGET)) +endif +else +export IDF_TARGET := esp32 +endif + + ifneq ("$(IDF_PATH)","$(SANITISED_IDF_PATH)") # implies IDF_PATH was overriden on make command line. # Due to the way make manages variables, this is hard to account for diff --git a/tools/ci/test_build_system_cmake.sh b/tools/ci/test_build_system_cmake.sh index bb57375d1e..87fff17637 100755 --- a/tools/ci/test_build_system_cmake.sh +++ b/tools/ci/test_build_system_cmake.sh @@ -212,6 +212,41 @@ function run_tests() mv CMakeLists.bak CMakeLists.txt assert_built ${APP_BINS} ${BOOTLOADER_BINS} ${PARTITION_BIN} + # Next two tests will use this fake 'esp31b' target + export fake_target=esp31b + mkdir -p components/$fake_target + touch components/$fake_target/CMakeLists.txt + cp ${IDF_PATH}/tools/cmake/toolchain-esp32.cmake components/$fake_target/toolchain-$fake_target.cmake + sed -i.old '/cmake_minimum_required/ a\ + set(COMPONENTS esptool_py)' CMakeLists.txt + + print_status "Can override IDF_TARGET from environment" + clean_build_dir + rm sdkconfig + export IDF_TARGET=$fake_target + (cd build && cmake -G Ninja .. ) || failure "Failed to configure with IDF_TARGET set in environment" + grep "CONFIG_IDF_TARGET=\"${fake_target}\"" sdkconfig || failure "Project not configured for IDF_TARGET correctly" + grep "IDF_TARGET:STRING=${fake_target}" build/CMakeCache.txt || failure "IDF_TARGET not set in CMakeCache.txt" + unset IDF_TARGET + + print_status "Can set target using idf.py -D" + clean_build_dir + rm sdkconfig + idf.py -DIDF_TARGET=$fake_target reconfigure || failure "Failed to set target via idf.py" + grep "CONFIG_IDF_TARGET=\"${fake_target}\"" sdkconfig || failure "Project not configured correctly using idf.py -D" + grep "IDF_TARGET:STRING=${fake_target}" build/CMakeCache.txt || failure "IDF_TARGET not set in CMakeCache.txt using idf.py -D" + + # Clean up modifications for the fake target + mv CMakeLists.txt.old CMakeLists.txt + rm -rf components + + print_status "Can find toolchain file in component directory" + clean_build_dir + mv ${IDF_PATH}/tools/cmake/toolchain-esp32.cmake ${IDF_PATH}/components/esp32/ + idf.py build || failure "Failed to build with toolchain file in component directory" + mv ${IDF_PATH}/components/esp32/toolchain-esp32.cmake ${IDF_PATH}/tools/cmake/ + assert_built ${APP_BINS} ${BOOTLOADER_BINS} ${PARTITION_BIN} + print_status "All tests completed" if [ -n "${FAILURES}" ]; then echo "Some failures were detected:" diff --git a/tools/cmake/component_utils.cmake b/tools/cmake/component_utils.cmake new file mode 100644 index 0000000000..63377a2e37 --- /dev/null +++ b/tools/cmake/component_utils.cmake @@ -0,0 +1,70 @@ +# Given a component name (find_name) and a list of component paths (component_paths), +# return the path to the component in 'variable' +# +# Fatal error is printed if the component is not found. +function(find_component_path find_name components component_paths variable) + list(FIND components ${find_name} idx) + if(NOT idx EQUAL -1) + list(GET component_paths ${idx} path) + set("${variable}" "${path}" PARENT_SCOPE) + return() + else() + endif() + # TODO: find a way to print the dependency chain that lead to this not-found component + message(WARNING "Required component ${find_name} is not found in any of the provided COMPONENT_DIRS") +endfunction() + +# components_find_all: Search 'component_dirs' for components and return them +# as a list of names in 'component_names' and a list of full paths in +# 'component_paths' +# +# component_paths contains only unique component names. Directories +# earlier in the component_dirs list take precedence. +function(components_find_all component_dirs component_paths component_names test_component_names) + # component_dirs entries can be files or lists of files + set(paths "") + set(names "") + set(test_names "") + + # start by expanding the component_dirs list with all subdirectories + foreach(dir ${component_dirs}) + # Iterate any subdirectories for values + file(GLOB subdirs LIST_DIRECTORIES true "${dir}/*") + foreach(subdir ${subdirs}) + set(component_dirs "${component_dirs};${subdir}") + endforeach() + endforeach() + + # Look for a component in each component_dirs entry + foreach(dir ${component_dirs}) + debug("Looking for CMakeLists.txt in ${dir}") + file(GLOB component "${dir}/CMakeLists.txt") + if(component) + debug("CMakeLists.txt file ${component}") + get_filename_component(component "${component}" DIRECTORY) + get_filename_component(name "${component}" NAME) + if(NOT name IN_LIST names) + list(APPEND names "${name}") + list(APPEND paths "${component}") + + # Look for test component directory + file(GLOB test "${component}/test/CMakeLists.txt") + if(test) + list(APPEND test_names "${name}") + endif() + endif() + else() # no CMakeLists.txt file + # test for legacy component.mk and warn + file(GLOB legacy_component "${dir}/component.mk") + if(legacy_component) + get_filename_component(legacy_component "${legacy_component}" DIRECTORY) + message(WARNING "Component ${legacy_component} contains old-style component.mk but no CMakeLists.txt. " + "Component will be skipped.") + endif() + endif() + endforeach() + + set(${component_paths} ${paths} PARENT_SCOPE) + set(${component_names} ${names} PARENT_SCOPE) + set(${test_component_names} ${test_names} PARENT_SCOPE) +endfunction() diff --git a/tools/cmake/components.cmake b/tools/cmake/components.cmake index bafad5c42a..0a3570518e 100644 --- a/tools/cmake/components.cmake +++ b/tools/cmake/components.cmake @@ -133,6 +133,12 @@ function(add_component_dependencies target dep dep_type) endif() endfunction() +function(require_idf_targets) + if(NOT ${IDF_TARGET} IN_LIST ARGN) + message(FATAL_ERROR "Component ${COMPONENT_NAME} only supports targets: ${ARGN}") + endif() +endfunction() + function(components_finish_registration) # have the executable target depend on all components in the build diff --git a/tools/cmake/idf_functions.cmake b/tools/cmake/idf_functions.cmake index fd5fce0c7b..93cc4e7e0c 100644 --- a/tools/cmake/idf_functions.cmake +++ b/tools/cmake/idf_functions.cmake @@ -15,7 +15,7 @@ macro(idf_set_global_variables) # Commmon components, required by every component in the build # - set_default(COMPONENT_REQUIRES_COMMON "cxx esp32 newlib freertos heap log soc") + set_default(COMPONENT_REQUIRES_COMMON "cxx ${IDF_TARGET} newlib freertos heap log soc") # PROJECT_PATH has the path to the IDF project (top-level cmake directory) # @@ -76,7 +76,7 @@ function(idf_set_global_compiler_options) endif() # Default compiler configuration - add_compile_options(-ffunction-sections -fdata-sections -fstrict-volatile-bitfields -mlongcalls -nostdlib) + add_compile_options(-ffunction-sections -fdata-sections -fstrict-volatile-bitfields -nostdlib) # Default warnings configuration add_compile_options( diff --git a/tools/cmake/kconfig.cmake b/tools/cmake/kconfig.cmake index aad91c10dc..b5e5d484ee 100644 --- a/tools/cmake/kconfig.cmake +++ b/tools/cmake/kconfig.cmake @@ -99,6 +99,10 @@ function(kconfig_process_config) set(defaults_arg --defaults "${SDKCONFIG_DEFAULTS}") endif() + if(EXISTS "${SDKCONFIG_DEFAULTS}.${IDF_TARGET}") + list(APPEND defaults_arg --defaults "${SDKCONFIG_DEFAULTS}.${IDF_TARGET}") + endif() + # Set these in the parent scope, so that they can be written to project_description.json set(kconfigs "${kconfigs}") set(COMPONENT_KCONFIGS "${kconfigs}" PARENT_SCOPE) diff --git a/tools/cmake/project.cmake b/tools/cmake/project.cmake index 94fe0930d8..94ea567bb5 100644 --- a/tools/cmake/project.cmake +++ b/tools/cmake/project.cmake @@ -23,6 +23,7 @@ set(CMAKE_MODULE_PATH include(GetGitRevisionDescription) include(utilities) include(components) +include(targets) include(kconfig) include(git_submodules) include(idf_functions) @@ -53,6 +54,9 @@ endif() # top-level "project" call but customize it to do what we want in the IDF build. # macro(project name) + # Determine the build target + idf_set_target() + # Set global variables used by rest of the build idf_set_global_variables() @@ -71,6 +75,7 @@ macro(project name) -D "COMPONENT_DIRS=${COMPONENT_DIRS}" -D "BOOTLOADER_BUILD=${BOOTLOADER_BUILD}" -D "IDF_PATH=${IDF_PATH}" + -D "IDF_TARGET=${IDF_TARGET}" -D "DEBUG=${DEBUG}" -P "${IDF_PATH}/tools/cmake/scripts/expand_requirements.cmake" WORKING_DIRECTORY "${PROJECT_PATH}") @@ -102,10 +107,11 @@ macro(project name) # Include sdkconfig.cmake so rest of the build knows the configuration include(${SDKCONFIG_CMAKE}) + # Check that the targets set in cache, sdkconfig, and in environment all match + idf_check_config_target() + # Now the configuration is loaded, set the toolchain appropriately - # - # TODO: support more toolchains than just ESP32 - set(CMAKE_TOOLCHAIN_FILE $ENV{IDF_PATH}/tools/cmake/toolchain-esp32.cmake) + idf_set_toolchain() # Declare the actual cmake-level project _project(${name} ASM C CXX) diff --git a/tools/cmake/scripts/expand_requirements.cmake b/tools/cmake/scripts/expand_requirements.cmake index 56d78fd3bb..f92f62da12 100644 --- a/tools/cmake/scripts/expand_requirements.cmake +++ b/tools/cmake/scripts/expand_requirements.cmake @@ -31,6 +31,7 @@ # TODO: Error out if a component requirement is missing cmake_minimum_required(VERSION 3.5) include("${IDF_PATH}/tools/cmake/utilities.cmake") +include("${IDF_PATH}/tools/cmake/component_utils.cmake") if(NOT DEPENDENCIES_FILE) message(FATAL_ERROR "DEPENDENCIES_FILE must be set.") @@ -78,75 +79,10 @@ macro(register_config_only_component) register_component() endmacro() -# Given a component name (find_name) and a list of component paths (component_paths), -# return the path to the component in 'variable' -# -# Fatal error is printed if the component is not found. -function(find_component_path find_name components component_paths variable) - list(FIND components ${find_name} idx) - if(NOT idx EQUAL -1) - list(GET component_paths ${idx} path) - set("${variable}" "${path}" PARENT_SCOPE) - return() - else() +function(require_idf_targets) + if(NOT ${IDF_TARGET} IN_LIST ARGN) + message(FATAL_ERROR "Component ${COMPONENT_NAME} only supports targets: ${ARGN}") endif() - # TODO: find a way to print the dependency chain that lead to this not-found component - message(WARNING "Required component ${find_name} is not found in any of the provided COMPONENT_DIRS") -endfunction() - -# components_find_all: Search 'component_dirs' for components and return them -# as a list of names in 'component_names' and a list of full paths in -# 'component_paths' -# -# component_paths contains only unique component names. Directories -# earlier in the component_dirs list take precedence. -function(components_find_all component_dirs component_paths component_names test_component_names) - # component_dirs entries can be files or lists of files - set(paths "") - set(names "") - set(test_names "") - - # start by expanding the component_dirs list with all subdirectories - foreach(dir ${component_dirs}) - # Iterate any subdirectories for values - file(GLOB subdirs LIST_DIRECTORIES true "${dir}/*") - foreach(subdir ${subdirs}) - set(component_dirs "${component_dirs};${subdir}") - endforeach() - endforeach() - - # Look for a component in each component_dirs entry - foreach(dir ${component_dirs}) - debug("Looking for CMakeLists.txt in ${dir}") - file(GLOB component "${dir}/CMakeLists.txt") - if(component) - debug("CMakeLists.txt file ${component}") - get_filename_component(component "${component}" DIRECTORY) - get_filename_component(name "${component}" NAME) - if(NOT name IN_LIST names) - list(APPEND names "${name}") - list(APPEND paths "${component}") - - # Look for test component directory - file(GLOB test "${component}/test/CMakeLists.txt") - if(test) - list(APPEND test_names "${name}") - endif() - endif() - else() # no CMakeLists.txt file - # test for legacy component.mk and warn - file(GLOB legacy_component "${dir}/component.mk") - if(legacy_component) - get_filename_component(legacy_component "${legacy_component}" DIRECTORY) - message(WARNING "Component ${legacy_component} contains old-style component.mk but no CMakeLists.txt. " - "Component will be skipped.") - endif() - endif() - endforeach() - - set(${component_paths} ${paths} PARENT_SCOPE) - set(${component_names} ${names} PARENT_SCOPE) - set(${test_component_names} ${test_names} PARENT_SCOPE) endfunction() diff --git a/tools/cmake/targets.cmake b/tools/cmake/targets.cmake new file mode 100644 index 0000000000..95f7d5e109 --- /dev/null +++ b/tools/cmake/targets.cmake @@ -0,0 +1,57 @@ +include(component_utils) + +macro(idf_set_target) + # Input is IDF_TARGET environement variable + set(env_idf_target $ENV{IDF_TARGET}) + + if(NOT env_idf_target) + # IDF_TARGET not set in environment, see if it is set in cache + if(IDF_TARGET) + set(env_idf_target ${IDF_TARGET}) + else() + set(env_idf_target esp32) + message(STATUS "IDF_TARGET not set, using default target: ${env_idf_target}") + endif() + else() + # IDF_TARGET set both in environment and in cache, must be the same + if(NOT ${IDF_TARGET} STREQUAL ${env_idf_target}) + message(FATAL_ERROR "IDF_TARGET in CMake cache does not match " + "IDF_TARGET environment variable. To change the target, clear " + "the build directory and sdkconfig file, and build the project again") + endif() + endif() + + # IDF_TARGET will be used by Kconfig, make sure it is set + set(ENV{IDF_TARGET} ${env_idf_target}) + + # Finally, set IDF_TARGET in cache + set(IDF_TARGET ${env_idf_target} CACHE STRING "IDF Build Target") + + message(STATUS "Building for target ${IDF_TARGET}") +endmacro() + +macro(idf_check_config_target) + if(NOT ${IDF_TARGET} STREQUAL ${CONFIG_IDF_TARGET}) + message(FATAL_ERROR "CONFIG_IDF_TARGET in sdkconfig does not match " + "IDF_TARGET environement variable. To change the target, delete " + "sdkconfig file and build the project again.") + endif() +endmacro() + +macro(idf_set_toolchain) + # First try to load the toolchain file from the tools/cmake/ directory of IDF + set(toolchain_file_global $ENV{IDF_PATH}/tools/cmake/toolchain-${IDF_TARGET}.cmake) + if(EXISTS ${toolchain_file_global}) + set(CMAKE_TOOLCHAIN_FILE ${toolchain_file_global}) + else() + # Try to load the toolchain file from the directory of ${IDF_TARGET} component + find_component_path(${IDF_TARGET} "${BUILD_COMPONENTS}" "${BUILD_COMPONENT_PATHS}" target_component_path) + set(toolchain_file_component ${target_component_path}/toolchain-${IDF_TARGET}.cmake) + if(EXISTS ${toolchain_file_component}) + set(CMAKE_TOOLCHAIN_FILE ${toolchain_file_component}) + else() + message(FATAL_ERROR "Toolchain file toolchain-${IDF_TARGET}.cmake not found," + "checked ${toolchain_file_global} and ${toolchain_file_component}") + endif() + endif() +endmacro() diff --git a/tools/cmake/toolchain-esp32.cmake b/tools/cmake/toolchain-esp32.cmake index c23fa4cbc7..e8fbbef2bd 100644 --- a/tools/cmake/toolchain-esp32.cmake +++ b/tools/cmake/toolchain-esp32.cmake @@ -5,3 +5,5 @@ set(CMAKE_CXX_COMPILER xtensa-esp32-elf-g++) set(CMAKE_ASM_COMPILER xtensa-esp32-elf-gcc) set(CMAKE_EXE_LINKER_FLAGS "-nostdlib" CACHE STRING "Linker Base Flags") +set(CMAKE_C_FLAGS "-mlongcalls" CACHE STRING "C Compiler Base Flags") +set(CMAKE_CXX_FLAGS "-mlongcalls" CACHE STRING "C++ Compiler Base Flags")