]> granicus.if.org Git - esp-idf/commitdiff
cmake: build system changes
authorRenz Christian Bagaporo <renz@espressif.com>
Fri, 26 Apr 2019 05:42:10 +0000 (13:42 +0800)
committerRenz Christian Bagaporo <renz@espressif.com>
Mon, 13 May 2019 11:57:39 +0000 (19:57 +0800)
20 files changed:
CMakeLists.txt
docs/zh_CN/api-guides/build-system-cmake.rst
examples/system/gcov/README.md
tools/cmake/build.cmake [new file with mode: 0644]
tools/cmake/component.cmake [new file with mode: 0644]
tools/cmake/component_utils.cmake [deleted file]
tools/cmake/components.cmake [deleted file]
tools/cmake/convert_to_cmake.py
tools/cmake/crosstool_version_check.cmake
tools/cmake/idf.cmake [new file with mode: 0644]
tools/cmake/idf_functions.cmake [deleted file]
tools/cmake/kconfig.cmake
tools/cmake/ldgen.cmake
tools/cmake/project.cmake
tools/cmake/project_description.json.in
tools/cmake/scripts/component_get_requirements.cmake [new file with mode: 0644]
tools/cmake/scripts/expand_requirements.cmake [deleted file]
tools/cmake/targets.cmake
tools/cmake/utilities.cmake
tools/kconfig_new/confgen.py

index 2c6ef7621fd2986fe94b8956252e2c0743cc4809..c98d3d8c5e74e6f2317f3aceff8a20b4596d8e2c 100644 (file)
 cmake_minimum_required(VERSION 3.5)
 project(esp-idf C CXX ASM)
 
-if(NOT IDF_PATH)
-    set(IDF_PATH ${CMAKE_CURRENT_LIST_DIR})
-endif()
-
-include(tools/cmake/idf_functions.cmake)
-
-#
-# Set variables that control the build configuration and the build itself
-#
-idf_set_variables()
-
-kconfig_set_variables()
-
-#
-# Generate a component dependencies file, enumerating components to be included in the build
-# as well as their dependencies.
-#
-execute_process(COMMAND "${CMAKE_COMMAND}"
-    -D "COMPONENTS=${IDF_COMPONENTS}"
-    -D "COMPONENT_REQUIRES_COMMON=${IDF_COMPONENT_REQUIRES_COMMON}"
-    -D "EXCLUDE_COMPONENTS=${IDF_EXCLUDE_COMPONENTS}"
-    -D "TEST_COMPONENTS=${IDF_TEST_COMPONENTS}"
-    -D "TEST_EXCLUDE_COMPONENTS=${IDF_TEST_EXCLUDE_COMPONENTS}"
-    -D "BUILD_TESTS=${IDF_BUILD_TESTS}"
-    -D "DEPENDENCIES_FILE=${CMAKE_BINARY_DIR}/component_depends.cmake"
-    -D "COMPONENT_DIRS=${IDF_COMPONENT_DIRS}"
-    -D "BOOTLOADER_BUILD=${BOOTLOADER_BUILD}"
-    -D "IDF_TARGET=${IDF_TARGET}"
-    -D "IDF_PATH=${IDF_PATH}"
-    -D "DEBUG=${DEBUG}"
-    -P "${IDF_PATH}/tools/cmake/scripts/expand_requirements.cmake"
-    WORKING_DIRECTORY "${PROJECT_PATH}"
-    RESULT_VARIABLE expand_requirements_result)
-
-if(expand_requirements_result)
-    message(FATAL_ERROR "Failed to expand component requirements")
-endif()
+# Include the sdkconfig cmake file, since the following operations require
+# knowledge of config values.
+idf_build_get_property(sdkconfig_cmake SDKCONFIG_CMAKE)
+include(${sdkconfig_cmake})
+
+# Make each build property available as a read-only variable
+idf_build_get_property(build_properties __BUILD_PROPERTIES)
+foreach(build_property ${build_properties})
+    idf_build_get_property(val ${build_property})
+    set(${build_property} "${val}")
+endforeach()
 
-include("${CMAKE_BINARY_DIR}/component_depends.cmake")
-
-#
-# We now have the following component-related variables:
-#
-# IDF_COMPONENTS is the list of initial components set by the user
-# (or empty to include all components in the build).
-# BUILD_COMPONENTS is the list of components to include in the build.
-# BUILD_COMPONENT_PATHS is the paths to all of these components, obtained from the component dependencies file.
-#
-# Print the list of found components and test components
-#
-string(REPLACE ";" " " BUILD_COMPONENTS_SPACES "${BUILD_COMPONENTS}")
-message(STATUS "Component names: ${BUILD_COMPONENTS_SPACES}")
-unset(BUILD_COMPONENTS_SPACES)
-message(STATUS "Component paths: ${BUILD_COMPONENT_PATHS}")
-
-# Print list of test components
-if(TESTS_ALL EQUAL 1 OR TEST_COMPONENTS)
-    string(REPLACE ";" " " BUILD_TEST_COMPONENTS_SPACES "${BUILD_TEST_COMPONENTS}")
-    message(STATUS "Test component names: ${BUILD_TEST_COMPONENTS_SPACES}")
-    unset(BUILD_TEST_COMPONENTS_SPACES)
-    message(STATUS "Test component paths: ${BUILD_TEST_COMPONENT_PATHS}")
-endif()
+# Check that the CMake target value matches the Kconfig target value.
+__target_check()
 
-# Generate project configuration
-kconfig_process_config()
+unset(compile_options)
+unset(c_compile_options)
+unset(cxx_compile_options)
+unset(compile_definitions)
 
-# Include sdkconfig.cmake so rest of the build knows the configuration
-include(${SDKCONFIG_CMAKE})
+# Add the following build specifications here, since these seem to be dependent
+# on config values on the root Kconfig.
 
-# Verify the environment is configured correctly
-idf_verify_environment()
+# Temporary trick to support both gcc5 and gcc8 builds
+if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 5.2.0)
+    set(GCC_NOT_5_2_0 0 CACHE STRING "GCC is 5.2.0 version")
+else()
+    set(GCC_NOT_5_2_0 1 CACHE STRING "GCC is not 5.2.0 version")
+endif()
 
-# Check git revision (may trigger reruns of cmake)
-##  sets IDF_VER to IDF git revision
-idf_get_git_revision()
+list(APPEND compile_definitions "-DGCC_NOT_5_2_0=${GCC_NOT_5_2_0}")
 
-# Check that the targets set in cache, sdkconfig, and in environment all match
-idf_check_config_target()
+if(CONFIG_OPTIMIZATION_LEVEL_RELEASE)
+    list(APPEND compile_options "-Os")
+else()
+    list(APPEND compile_options "-Og")
+endif()
 
-## get PROJECT_VER
-if(NOT BOOTLOADER_BUILD)
-    app_get_revision("${CMAKE_SOURCE_DIR}")
+if(CONFIG_CXX_EXCEPTIONS)
+    list(APPEND cxx_compile_options "-fexceptions")
+else()
+    list(APPEND cxx_compile_options "-fno-exceptions")
 endif()
 
-# Add some idf-wide definitions
-idf_set_global_compile_options()
+if(CONFIG_DISABLE_GCC8_WARNINGS)
+    list(APPEND compile_options "-Wno-parentheses"
+                                "-Wno-sizeof-pointer-memaccess"
+                                "-Wno-clobbered")
+
+    # doesn't use GCC_NOT_5_2_0 because idf_set_global_variables was not called before
+    if(GCC_NOT_5_2_0)
+        list(APPEND compile_options "-Wno-format-overflow"
+                                    "-Wno-stringop-truncation"
+                                    "-Wno-misleading-indentation"
+                                    "-Wno-cast-function-type"
+                                    "-Wno-implicit-fallthrough"
+                                    "-Wno-unused-const-variable"
+                                    "-Wno-switch-unreachable"
+                                    "-Wno-format-truncation"
+                                    "-Wno-memset-elt-size"
+                                    "-Wno-int-in-bool-context")
+    endif()
+endif()
 
-# generate compile_commands.json (needs to come after project)
-set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
+if(CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED)
+    list(APPEND compile_definitions "NDEBUG")
+endif()
 
-#
-# Setup variables for linker script generation
-#
-ldgen_set_variables()
+if(CONFIG_STACK_CHECK_NORM)
+    list(APPEND compile_options "-fstack-protector")
+elseif(CONFIG_STACK_CHECK_STRONG)
+    list(APPEND compile_options "-fstack-protector-strong")
+elseif(CONFIG_STACK_CHECK_ALL)
+    list(APPEND compile_options "-fstack-protector-all")
+endif()
 
-# Include any top-level project_include.cmake files from components
-foreach(component ${BUILD_COMPONENT_PATHS})
-    set(COMPONENT_PATH "${component}")
-    include_if_exists("${component}/project_include.cmake")
-    unset(COMPONENT_PATH)
+# All targets built under this scope is with the ESP-IDF build system
+set(ESP_PLATFORM 1)
+list(APPEND compile_definitions "-DESP_PLATFORM")
+
+idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND)
+idf_build_set_property(C_COMPILE_OPTIONS "${c_compile_options}" APPEND)
+idf_build_set_property(CXX_COMPILE_OPTIONS "${cxx_compile_options}" APPEND)
+idf_build_set_property(COMPILE_DEFINITIONS "${compile_definitions}" APPEND)
+
+idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS)
+
+# Include each component's project_include.cmake
+foreach(component_target ${build_component_targets})
+    __component_get_property(dir ${component_target} COMPONENT_DIR)
+    __component_get_property(_name ${component_target} COMPONENT_NAME)
+    set(COMPONENT_NAME ${_name})
+    set(COMPONENT_DIR ${dir})
+    set(COMPONENT_PATH ${dir})  # this is deprecated, users are encouraged to use COMPONENT_DIR;
+                                # retained for compatibility
+    if(EXISTS ${COMPONENT_DIR}/project_include.cmake)
+        include(${COMPONENT_DIR}/project_include.cmake)
+    endif()
 endforeach()
 
-#
-# Add each component to the build as a library
-#
-foreach(COMPONENT_PATH ${BUILD_COMPONENT_PATHS})
-    get_filename_component(COMPONENT_NAME ${COMPONENT_PATH} NAME)
-
-    list(FIND BUILD_TEST_COMPONENT_PATHS ${COMPONENT_PATH} idx)
-
-    if(NOT idx EQUAL -1)
-        list(GET BUILD_TEST_COMPONENTS ${idx} test_component)
-        set(COMPONENT_NAME ${test_component})
+# Add each component as a subdirectory, processing each component's CMakeLists.txt
+foreach(component_target ${build_component_targets})
+    __component_get_property(dir ${component_target} COMPONENT_DIR)
+    __component_get_property(_name ${component_target} COMPONENT_NAME)
+    __component_get_property(prefix ${component_target} __PREFIX)
+    __component_get_property(alias ${component_target} COMPONENT_ALIAS)
+    set(COMPONENT_NAME ${_name})
+    set(COMPONENT_DIR ${dir})
+    set(COMPONENT_ALIAS ${alias})
+    set(COMPONENT_PATH ${dir}) # same deprecation situation here
+    idf_build_get_property(build_prefix __PREFIX)
+    set(__idf_component_context 1)
+    if(NOT prefix STREQUAL build_prefix)
+        add_subdirectory(${dir} ${prefix}_${_name} EXCLUDE_FROM_ALL)
+    else()
+        add_subdirectory(${dir} ${_name} EXCLUDE_FROM_ALL)
     endif()
-
-    component_get_target(COMPONENT_TARGET ${COMPONENT_NAME})
-
-    add_subdirectory(${COMPONENT_PATH} ${COMPONENT_NAME})
+    set(__idf_component_context 0)
 endforeach()
-unset(COMPONENT_NAME)
-unset(COMPONENT_PATH)
-
-# each component should see the include directories of its requirements
-#
-# (we can't do this until all components are registered and targets exist in cmake, as we have
-# a circular requirements graph...)
-foreach(component ${BUILD_COMPONENTS})
-    component_get_target(component_target ${component})
-    if(TARGET ${component_target})
-        get_component_requirements(${component} deps priv_deps)
-
-        list(APPEND priv_deps ${IDF_COMPONENT_REQUIRES_COMMON})
-
-        foreach(dep ${deps})
-            component_get_target(dep_target ${dep})
-            add_component_dependencies(${component_target} ${dep_target} PUBLIC)
-        endforeach()
 
-        foreach(dep ${priv_deps})
-            component_get_target(dep_target ${dep})
-            add_component_dependencies(${component_target} ${dep_target} PRIVATE)
+# Establish dependencies between components
+idf_build_get_property(build_components BUILD_COMPONENTS)
+foreach(build_component ${build_components})
+    __component_get_target(component_target ${build_component})
+    __component_get_property(component_lib ${component_target} COMPONENT_LIB)
+    __component_get_property(reqs ${component_target} __REQUIRES)
+    foreach(req ${reqs})
+        __component_get_property(req_lib ${req} COMPONENT_LIB)
+        if(TARGET ${req_lib})
+            set_property(TARGET ${component_lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${req_lib})
+        endif()
+    endforeach()
+
+    get_property(type TARGET ${component_lib} PROPERTY TYPE)
+    if(type STREQUAL STATIC_LIBRARY)
+        __component_get_property(reqs ${component_target} __REQUIRES)
+        __component_get_property(priv_reqs ${component_target} __PRIV_REQUIRES)
+        foreach(req ${reqs} ${priv_reqs})
+            __component_get_property(req_lib ${req} COMPONENT_LIB)
+            if(TARGET ${req_lib})
+                set_property(TARGET ${component_lib} APPEND PROPERTY LINK_LIBRARIES ${req_lib})
+            endif()
         endforeach()
     endif()
 endforeach()
 
-if(IDF_BUILD_ARTIFACTS)
-    # Write project description JSON file
-    make_json_list("${BUILD_COMPONENTS}" build_components_json)
-    make_json_list("${BUILD_COMPONENT_PATHS}" build_component_paths_json)
-    configure_file("${IDF_PATH}/tools/cmake/project_description.json.in"
-        "${IDF_BUILD_ARTIFACTS_DIR}/project_description.json")
-    unset(build_components_json)
-    unset(build_component_paths_json)
-endif()
-
-set(BUILD_COMPONENTS ${BUILD_COMPONENTS} PARENT_SCOPE)
 
-ldgen_add_dependencies()
index ff4bd44f34d0c0572db352c01282704044a0fc93..2cf27bda982b34de74ea274d8cae89c1a14f9631 100644 (file)
@@ -357,9 +357,9 @@ ESP-IDF 在搜索所有待构建的组件时,会按照 ``COMPONENT_DIRS`` 指
 
 .. highlight:: cmake
 
-在编译特定组件的源文件时,可以使用 ``component_compile_options`` 命令来传递编译器选项::
+在编译特定组件的源文件时,可以使用 ``target_compile_options(${COMPONENT_TARGET} PRIVATE `` 命令来传递编译器选项::
 
-    component_compile_options(-Wno-unused-variable)
+    target_compile_options(${COMPONENT_TARGET} PRIVATE -Wno-unused-variable)
 
 这条命令封装了 CMake 的 `target_compile_options`_ 命令。
 
index 941ae68c4819d7b364bd8abf8ee499cd8d94510c..8faf9f31e328dae6fb1c57d74b92a39fcdc40011 100644 (file)
@@ -25,7 +25,7 @@ It will enable coverage info for all source files of your component. If you need
 `gcov_example.o: CFLAGS += --coverage`
 Replace `gcov_example.o` with path to your file.
 
-  For CMake-based build system, use `component_compile_options(--coverage)` or: `  set_source_files_properties(gcov_example.c PROPERTIES COMPILE_FLAGS --coverage`
+  For CMake-based build system, use `target_compile_options(${COMPONENT_TARGET} PRIVATE --coverage)` or: `  set_source_files_properties(gcov_example.c PROPERTIES COMPILE_FLAGS --coverage`
 
 
 ### Hard-coded Dump Call
diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake
new file mode 100644 (file)
index 0000000..f85ef1d
--- /dev/null
@@ -0,0 +1,434 @@
+
+# idf_build_get_property
+#
+# @brief Retrieve the value of the specified property related to ESP-IDF build.
+#
+# @param[out] var the variable to store the value in
+# @param[in] property the property to get the value of
+#
+# @param[in, optional] GENERATOR_EXPRESSION (option) retrieve the generator expression for the property
+#                   instead of actual value
+function(idf_build_get_property var property)
+    cmake_parse_arguments(_ "GENERATOR_EXPRESSION" "" "" ${ARGN})
+    if(__GENERATOR_EXPRESSION)
+        set(val "$<TARGET_PROPERTY:__idf_build_target,${property}>")
+    else()
+        get_property(val TARGET __idf_build_target PROPERTY ${property})
+    endif()
+    set(${var} ${val} PARENT_SCOPE)
+endfunction()
+
+# idf_build_set_property
+#
+# @brief Set the value of the specified property related to ESP-IDF build. The property is
+#        also added to the internal list of build properties if it isn't there already.
+#
+# @param[in] property the property to set the value of
+# @param[out] value value of the property
+#
+# @param[in, optional] APPEND (option) append the value to the current value of the
+#                     property instead of replacing it
+function(idf_build_set_property property value)
+    cmake_parse_arguments(_ "APPEND" "" "" ${ARGN})
+
+    if(__APPEND)
+        set_property(TARGET __idf_build_target APPEND PROPERTY ${property} ${value})
+    else()
+        set_property(TARGET __idf_build_target PROPERTY ${property} ${value})
+    endif()
+
+    # Keep track of set build properties so that they can be exported to a file that
+    # will be included in early expansion script.
+    idf_build_get_property(build_properties __BUILD_PROPERTIES)
+    if(NOT property IN_LIST build_properties)
+        idf_build_set_property(__BUILD_PROPERTIES "${property}" APPEND)
+    endif()
+endfunction()
+
+# idf_build_unset_property
+#
+# @brief Unset the value of the specified property related to ESP-IDF build. Equivalent
+#        to setting the property to an empty string; though it also removes the property
+#        from the internal list of build properties.
+#
+# @param[in] property the property to unset the value of
+function(idf_build_unset_property property)
+    idf_build_set_property(${property} "") # set to an empty value
+    idf_build_get_property(build_properties __BUILD_PROPERTIES) # remove from tracked properties
+    list(REMOVE_ITEM build_properties ${property})
+    idf_build_set_property(__BUILD_PROPERTIES "${build_properties}")
+endfunction()
+
+#
+# Retrieve the IDF_PATH repository's version, either using a version
+# file or Git revision. Sets the IDF_VER build property.
+#
+function(__build_get_idf_git_revision)
+    idf_build_get_property(idf_path IDF_PATH)
+    git_describe(idf_ver_git "${idf_path}")
+    if(EXISTS "${idf_path}/version.txt")
+        file(STRINGS "${idf_path}/version.txt" idf_ver_t)
+        set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${idf_path}/version.txt")
+    else()
+        set(idf_ver_t ${idf_ver_git})
+    endif()
+    # cut IDF_VER to required 32 characters.
+    string(SUBSTRING "${idf_ver_t}" 0 31 idf_ver)
+    idf_build_set_property(COMPILE_DEFINITIONS "-DIDF_VER=\"${idf_ver}\"" APPEND)
+    git_submodule_check("${idf_path}")
+    idf_build_set_property(IDF_VER ${idf_ver})
+endfunction()
+
+#
+# Sets initial list of build specifications (compile options, definitions, etc.) common across
+# all library targets built under the ESP-IDF build system. These build specifications are added
+# privately using the directory-level CMake commands (add_compile_options, include_directories, etc.)
+# during component registration.
+#
+function(__build_set_default_build_specifications)
+    unset(compile_definitions)
+    unset(compile_options)
+    unset(c_compile_options)
+    unset(cxx_compile_options)
+
+    list(APPEND compile_definitions "-DHAVE_CONFIG_H")
+
+    list(APPEND compile_options     "-ffunction-sections"
+                                    "-fdata-sections"
+                                    "-fstrict-volatile-bitfields"
+                                    "-nostdlib"
+                                    # warning-related flags
+                                    "-Wall"
+                                    "-Werror=all"
+                                    "-Wno-error=unused-function"
+                                    "-Wno-error=unused-but-set-variable"
+                                    "-Wno-error=unused-variable"
+                                    "-Wno-error=deprecated-declarations"
+                                    "-Wextra"
+                                    "-Wno-unused-parameter"
+                                    "-Wno-sign-compare"
+                                    # always generate debug symbols (even in release mode, these don't
+                                    # go into the final binary so have no impact on size
+                                    "-ggdb")
+
+    list(APPEND c_compile_options   "-std=gnu99"
+                                    "-Wno-old-style-declaration")
+
+    list(APPEND cxx_compile_options "-std=gnu++11"
+                                    "-fno-rtti")
+
+    idf_build_set_property(COMPILE_DEFINITIONS "${compile_definitions}" APPEND)
+    idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND)
+    idf_build_set_property(C_COMPILE_OPTIONS "${c_compile_options}" APPEND)
+    idf_build_set_property(CXX_COMPILE_OPTIONS "${cxx_compile_options}" APPEND)
+endfunction()
+
+#
+# Initialize the build. This gets called upon inclusion of idf.cmake to set internal
+# properties used for the processing phase of the build.
+#
+function(__build_init idf_path)
+    # Create the build target, to which the ESP-IDF build properties, dependencies are attached to
+    add_custom_target(__idf_build_target)
+
+    set_default(python "python")
+
+    idf_build_set_property(PYTHON ${python})
+    idf_build_set_property(IDF_PATH ${idf_path})
+
+    idf_build_set_property(__PREFIX idf)
+    idf_build_set_property(__CHECK_PYTHON 1)
+
+    __build_set_default_build_specifications()
+
+    # Add internal components to the build
+    idf_build_get_property(idf_path IDF_PATH)
+    idf_build_get_property(prefix __PREFIX)
+    file(GLOB component_dirs ${idf_path}/components/*)
+    foreach(component_dir ${component_dirs})
+        get_filename_component(component_dir ${component_dir} ABSOLUTE)
+        __component_add(${component_dir} ${prefix})
+    endforeach()
+
+    # Set components required by all other components in the build
+    set(requires_common cxx newlib freertos heap log soc esp_rom esp_common xtensa)
+    idf_build_set_property(__COMPONENT_REQUIRES_COMMON "${requires_common}")
+
+    __build_get_idf_git_revision()
+    __kconfig_init()
+endfunction()
+
+# idf_build_component
+#
+# @brief Specify component directory for the build system to process.
+#        Relative paths are converted to absolute paths with respect to current directory.
+#        Any component that needs to be processed has to be specified using this
+#        command before calling idf_build_process.
+#
+# @param[in] component_dir directory of the component to process
+function(idf_build_component component_dir)
+    idf_build_get_property(prefix __PREFIX)
+    __component_add(${component_dir} ${prefix} 0)
+endfunction()
+
+#
+# Resolve the requirement component to the component target created for that component.
+#
+function(__build_resolve_and_add_req var component_target req type)
+    __component_get_target(_component_target ${req})
+    if(NOT _component_target)
+        message(FATAL_ERROR "Failed to resolve component '${req}'.")
+    endif()
+    __component_set_property(${component_target} ${type} ${_component_target} APPEND)
+    set(${var} ${_component_target} PARENT_SCOPE)
+endfunction()
+
+#
+# Build a list of components (in the form of component targets) to be added to the build
+# based on public and private requirements. This list is saved in an internal property,
+# __BUILD_COMPONENT_TARGETS.
+#
+function(__build_expand_requirements component_target)
+    # Since there are circular dependencies, make sure that we do not infinitely
+    # expand requirements for each component.
+    idf_build_get_property(component_targets_seen __COMPONENT_TARGETS_SEEN)
+    if(component_target IN_LIST component_targets_seen)
+        return()
+    endif()
+
+    idf_build_set_property(__COMPONENT_TARGETS_SEEN ${component_target} APPEND)
+
+    get_property(reqs TARGET ${component_target} PROPERTY REQUIRES)
+    get_property(priv_reqs TARGET ${component_target} PROPERTY PRIV_REQUIRES)
+
+    foreach(req ${reqs})
+        __build_resolve_and_add_req(_component_target ${component_target} ${req} __REQUIRES)
+        __build_expand_requirements(${_component_target})
+    endforeach()
+
+    foreach(req ${priv_reqs})
+        __build_resolve_and_add_req(_component_target ${component_target} ${req} __PRIV_REQUIRES)
+        __build_expand_requirements(${_component_target})
+    endforeach()
+
+    idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS)
+    if(NOT component_target IN_LIST build_component_targets)
+        idf_build_set_property(__BUILD_COMPONENT_TARGETS ${component_target} APPEND)
+    endif()
+endfunction()
+
+#
+# Write a CMake file containing set build properties, owing to the fact that an internal
+# list of properties is maintained in idf_build_set_property() call. This is used to convert
+# those set properties to variables in the scope the output file is included in.
+#
+function(__build_write_properties output_file)
+    idf_build_get_property(build_properties __BUILD_PROPERTIES)
+    foreach(property ${build_properties})
+        idf_build_get_property(val ${property})
+        set(build_properties_text "${build_properties_text}\nset(${property} ${val})")
+    endforeach()
+    file(WRITE ${output_file} "${build_properties_text}")
+endfunction()
+
+#
+# Check if the Python interpreter used for the build has all the required modules.
+#
+function(__build_check_python)
+    idf_build_get_property(check __CHECK_PYTHON)
+    if(check)
+        idf_build_get_property(python PYTHON)
+        idf_build_get_property(idf_path IDF_PATH)
+        message(STATUS "Checking Python dependencies...")
+        execute_process(COMMAND "${python}" "${idf_path}/tools/check_python_dependencies.py"
+            RESULT_VARIABLE result)
+        if(NOT result EQUAL 0)
+            message(FATAL_ERROR "Some Python dependencies must be installed. Check above message for details.")
+        endif()
+    endif()
+endfunction()
+
+#
+# Utility macro for setting default property value if argument is not specified
+# for idf_build_process().
+#
+macro(__build_set_default var default)
+    set(_var __${var})
+    if(${_var})
+        idf_build_set_property(${var} "${${_var}}")
+    else()
+        idf_build_set_property(${var} "${default}")
+    endif()
+    unset(_var)
+endmacro()
+
+# idf_build_process
+#
+# @brief Main processing step for ESP-IDF build: config generation, adding components to the build,
+#        dependency resolution, etc.
+#
+# @param[in] target ESP-IDF target
+#
+# @param[in, optional] PROJECT_DIR (single value) directory of the main project the buildsystem
+#                      is processed for; defaults to CMAKE_SOURCE_DIR
+# @param[in, optional] PROJECT_VER (single value) version string of the main project; defaults
+#                      to 0.0.0
+# @param[in, optional] PROJECT_NAME (single value) main project name, defaults to CMAKE_PROJECT_NAME
+# @param[in, optional] SDKCONFIG (single value) sdkconfig output path, defaults to PROJECT_DIR/sdkconfig
+#                       if PROJECT_DIR is set and CMAKE_SOURCE_DIR/sdkconfig if not
+# @param[in, optional] SDKCONFIG_DEFAULTS (single value) config defaults file to use for the build; defaults
+#                       to none (Kconfig defaults or previously generated config are used)
+# @param[in, optional] BUILD_DIR (single value) directory for build artifacts; defautls to CMAKE_BINARY_DIR
+# @param[in, optional] COMPONENTS (multivalue) starting components for trimming build
+function(idf_build_process target)
+    set(options)
+    set(single_value PROJECT_DIR PROJECT_VER PROJECT_NAME BUILD_DIR SDKCONFIG SDKCONFIG_DEFAULTS)
+    set(multi_value COMPONENTS)
+    cmake_parse_arguments(_ "${options}" "${single_value}" "${multi_value}" ${ARGN})
+
+    idf_build_set_property(BOOTLOADER_BUILD "${BOOTLOADER_BUILD}")
+
+    # Check build target is specified. Since this target corresponds to a component
+    # name, the target component is automatically added to the list of common component
+    # requirements.
+    if(NOT target OR target STREQUAL "")
+        message(FATAL_ERROR "Build target not specified.")
+    endif()
+
+    idf_build_set_property(IDF_TARGET ${target})
+
+    __build_set_default(PROJECT_DIR ${CMAKE_SOURCE_DIR})
+    __build_set_default(PROJECT_NAME ${CMAKE_PROJECT_NAME})
+    __build_set_default(PROJECT_VER "0.0.0")
+    __build_set_default(BUILD_DIR ${CMAKE_BINARY_DIR})
+
+    idf_build_get_property(project_dir PROJECT_DIR)
+    __build_set_default(SDKCONFIG "${project_dir}/sdkconfig")
+
+    __build_set_default(SDKCONFIG_DEFAULTS "")
+
+    # Check for required Python modules
+    __build_check_python()
+
+    # Generate config values in different formats
+    idf_build_get_property(sdkconfig SDKCONFIG)
+    idf_build_get_property(sdkconfig_defaults SDKCONFIG_DEFAULTS)
+    __kconfig_generate_config("${sdkconfig}" "${sdkconfig_defaults}")
+
+    # Write the partial build properties to a temporary file.
+    # The path to this generated file is set to a short-lived build
+    # property BUILD_PROPERTIES_FILE.
+    idf_build_get_property(build_dir BUILD_DIR)
+    set(build_properties_file ${build_dir}/build_properties.temp.cmake)
+    idf_build_set_property(BUILD_PROPERTIES_FILE ${build_properties_file})
+    __build_write_properties(${build_properties_file})
+
+    # Perform early expansion of component CMakeLists.txt in CMake scripting mode.
+    # It is here we retrieve the public and private requirements of each component.
+    # It is also here we add the common component requirements to each component's
+    # own requirements.
+    idf_build_get_property(component_targets __COMPONENT_TARGETS)
+    idf_build_set_property(__COMPONENT_REQUIRES_COMMON ${target} APPEND)
+    idf_build_get_property(common_reqs __COMPONENT_REQUIRES_COMMON)
+    foreach(component_target ${component_targets})
+        get_property(component_dir TARGET ${component_target} PROPERTY COMPONENT_DIR)
+        __component_get_requirements(error reqs priv_reqs ${component_dir})
+        if(error)
+            message(FATAL_ERROR "${error}")
+        endif()
+
+        list(APPEND reqs "${common_reqs}")
+
+        # Remove duplicates and the component itself from its requirements
+        __component_get_property(alias ${component_target} COMPONENT_ALIAS)
+        __component_get_property(_name ${component_target} COMPONENT_NAME)
+
+        # Prevent component from linking to itself.
+        if(reqs)
+            list(REMOVE_DUPLICATES reqs)
+            list(REMOVE_ITEM reqs ${alias} ${_name})
+        endif()
+
+        if(priv_reqs)
+            list(REMOVE_DUPLICATES priv_reqs)
+            list(REMOVE_ITEM priv_reqs ${alias} ${_name})
+        endif()
+
+        __component_set_property(${component_target} REQUIRES "${reqs}")
+        __component_set_property(${component_target} PRIV_REQUIRES "${priv_reqs}")
+    endforeach()
+    idf_build_unset_property(BUILD_PROPERTIES_FILE)
+    file(REMOVE ${build_properties_file})
+
+    # Finally, do component expansion. In this case it simply means getting a final list
+    # of build component targets given the requirements set by each component.
+    if(__COMPONENTS)
+        unset(component_targets)
+        foreach(component ${__COMPONENTS})
+            __component_get_target(component_target ${component})
+            if(NOT component_target)
+                message(FATAL_ERROR "Failed to resolve component '${component}'.")
+            endif()
+            list(APPEND component_targets ${component_target})
+        endforeach()
+    endif()
+
+    foreach(component_target ${component_targets})
+        __build_expand_requirements(${component_target})
+    endforeach()
+    unset(__COMPONENT_TARGETS_SEEN)
+
+    # Get a list of common component requirements in component targets form (previously
+    # we just have a list of component names)
+    foreach(common_req ${common_reqs})
+        __component_get_target(component_target ${common_req})
+        __component_get_property(lib ${component_target} COMPONENT_LIB)
+        idf_build_set_property(___COMPONENT_REQUIRES_COMMON ${lib} APPEND)
+    endforeach()
+
+    # Perform component processing (inclusion of project_include.cmake, adding component
+    # subdirectories, creating library targets, linking libraries, etc.)
+    idf_build_get_property(idf_path IDF_PATH)
+    add_subdirectory(${idf_path} ${build_dir}/esp-idf)
+endfunction()
+
+# idf_build_executable
+#
+# @brief Specify the executable the build system can attach dependencies to (for generating
+# files used for linking, targets which should execute before creating the specified executable,
+# generating additional binary files, generating files related to flashing, etc.)
+function(idf_build_executable elf)
+    # Propagate link dependencies from component library targets to the executable
+    idf_build_get_property(build_components BUILD_COMPONENTS)
+    foreach(build_component ${build_components})
+        get_target_property(type ${build_component} TYPE)
+        if(type STREQUAL "INTERFACE_LIBRARY")
+            get_target_property(iface_link_depends ${build_component} INTERFACE_LINK_DEPENDS)
+        else()
+            get_target_property(link_depends ${build_component} LINK_DEPENDS)
+            get_target_property(iface_link_depends ${build_component} INTERFACE_LINK_DEPENDS)
+        endif()
+        if(iface_link_depends)
+            list(APPEND _link_depends ${iface_link_depends})
+        endif()
+        if(link_depends)
+            list(APPEND _link_depends ${link_depends})
+        endif()
+    endforeach()
+
+    idf_build_get_property(link_depends LINK_DEPENDS)
+    if(link_depends)
+        list(APPEND _link_depends ${link_depends})
+    endif()
+
+    set_property(TARGET ${elf} APPEND PROPERTY LINK_DEPENDS "${_link_depends}")
+
+    # Set the EXECUTABLE_NAME and EXECUTABLE properties since there are generator expression
+    # from components that depend on it
+    get_filename_component(elf_name ${elf} NAME_WE)
+    idf_build_set_property(EXECUTABLE_NAME ${elf_name})
+    idf_build_set_property(EXECUTABLE ${elf})
+
+    # Add dependency of the build target to the executable
+    add_dependencies(${elf} __idf_build_target)
+endfunction()
\ No newline at end of file
diff --git a/tools/cmake/component.cmake b/tools/cmake/component.cmake
new file mode 100644 (file)
index 0000000..ccb7bc0
--- /dev/null
@@ -0,0 +1,460 @@
+#
+# Internal function for retrieving component properties from a component target.
+#
+function(__component_get_property var component_target property)
+    get_property(val TARGET ${component_target} PROPERTY ${property})
+    set(${var} "${val}" PARENT_SCOPE)
+endfunction()
+
+#
+# Internal function for setting component properties on a component target. As with build properties,
+# set properties are also keeped track of.
+#
+function(__component_set_property component_target property val)
+    cmake_parse_arguments(_ "APPEND" "" "" ${ARGN})
+
+    if(__APPEND)
+        set_property(TARGET ${component_target} APPEND PROPERTY ${property} "${val}")
+    else()
+        set_property(TARGET ${component_target} PROPERTY ${property} "${val}")
+    endif()
+
+    # Keep track of set component properties
+    __component_get_property(properties ${component_target} __COMPONENT_PROPERTIES)
+    if(NOT property IN_LIST properties)
+        __component_set_property(${component_target} __COMPONENT_PROPERTIES ${property} APPEND)
+    endif()
+endfunction()
+
+#
+# Given a component name or alias, get the corresponding component target.
+#
+function(__component_get_target var name_or_alias)
+    # Look at previously resolved names or aliases
+    idf_build_get_property(component_names_resolved __COMPONENT_NAMES_RESOLVED)
+    list(FIND component_names_resolved ${name_or_alias} result)
+    if(NOT result EQUAL -1)
+        # If it has been resolved before, return that value. The index is the same
+        # as in __COMPONENT_NAMES_RESOLVED as these are parallel lists.
+        idf_build_get_property(component_targets_resolved __COMPONENT_TARGETS_RESOLVED)
+        list(GET component_targets_resolved ${result} target)
+        set(${var} ${target} PARENT_SCOPE)
+        return()
+    endif()
+
+    idf_build_get_property(component_targets __COMPONENT_TARGETS)
+
+    # Assume first that the paramters is an alias.
+    string(REPLACE "::" "_" name_or_alias "${name_or_alias}")
+    set(component_target ___${name_or_alias})
+
+    if(component_target IN_LIST component_targets)
+        set(${var} ${component_target} PARENT_SCOPE)
+        set(target ${component_target})
+    else() # assumption is wrong, try to look for it manually
+        unset(target)
+        foreach(component_target ${component_targets})
+            __component_get_property(_component_name ${component_target} COMPONENT_NAME)
+            if(name_or_alias STREQUAL _component_name)
+                # There should only be one component of the same name
+                if(NOT target)
+                    set(target ${component_target})
+                else()
+                    message(FATAL_ERROR "Multiple components with name '${name_or_alias}' found.")
+                    return()
+                endif()
+            endif()
+        endforeach()
+        set(${var} ${target} PARENT_SCOPE)
+    endif()
+
+    # Save the resolved name or alias
+    if(target)
+        idf_build_set_property(__COMPONENT_NAMES_RESOLVED ${name_or_alias} APPEND)
+        idf_build_set_property(__COMPONENT_TARGETS_RESOLVED ${target} APPEND)
+    endif()
+endfunction()
+
+#
+# Called during component registration, sets basic properties of the current component.
+#
+macro(__component_set_properties)
+    __component_get_property(type ${component_target} COMPONENT_TYPE)
+
+    # Fill in the rest of component property
+    __component_set_property(${component_target} SRCS "${sources}")
+    __component_set_property(${component_target} INCLUDE_DIRS "${__INCLUDE_DIRS}")
+
+    if(type STREQUAL LIBRARY)
+        __component_set_property(${component_target} PRIV_INCLUDE_DIRS "${__PRIV_INCLUDE_DIRS}")
+    endif()
+
+    __component_set_property(${component_target} LDFRAGMENTS "${__LDFRAGMENTS}")
+    __component_set_property(${component_target} EMBED_FILES "${__EMBED_FILES}")
+    __component_set_property(${component_target} EMBED_TXTFILES "${__EMBED_TXTFILES}")
+    __component_set_property(${component_target} REQUIRED_IDF_TARGETS "${__REQUIRED_IDF_TARGETS}")
+endmacro()
+
+#
+# Add a component to process in the build. The components are keeped tracked of in property
+# __COMPONENT_TARGETS in component target form.
+#
+function(__component_add component_dir prefix)
+    # For each component, two entities are created: a component target and a component library. The
+    # component library is created during component registration (the actual static/interface library).
+    # On the other hand, component targets are created early in the build
+    # (during adding component as this function suggests).
+    # This is so that we still have a target to attach properties to up until the component registration.
+    # Plus, interface libraries have limitations on the types of properties that can be set on them,
+    # so later in the build, these component targets actually contain the properties meant for the
+    # corresponding component library.
+    idf_build_get_property(component_targets __COMPONENT_TARGETS)
+    get_filename_component(abs_dir ${component_dir} ABSOLUTE)
+    get_filename_component(base_dir ${abs_dir} NAME)
+
+    set(component_name ${base_dir})
+    # The component target has three underscores as a prefix. The corresponding component library
+    # only has two.
+    set(component_target ___${prefix}_${component_name})
+
+    # If a component of the same name has not been added before If it has been added
+    # before just override the properties. As a side effect, components added later
+    # 'override' components added earlier.
+    if(NOT component_target IN_LIST component_targets)
+        if(NOT TARGET ${component_target})
+            add_custom_target(${component_target} EXCLUDE_FROM_ALL)
+        endif()
+        idf_build_set_property(__COMPONENT_TARGETS ${component_target} APPEND)
+    endif()
+
+    set(component_lib __${prefix}_${component_name})
+    set(component_dir ${abs_dir})
+    set(component_alias ${prefix}::${component_name}) # The 'alias' of the component library,
+                                                    # used to refer to the component outside
+                                                    # the build system. Users can use this name
+                                                    # to resolve ambiguity with component names
+                                                    # and to link IDF components to external targets.
+
+    # Set the basic properties of the component
+    __component_set_property(${component_target} COMPONENT_LIB ${component_lib})
+    __component_set_property(${component_target} COMPONENT_NAME ${component_name})
+    __component_set_property(${component_target} COMPONENT_DIR ${component_dir})
+    __component_set_property(${component_target} COMPONENT_ALIAS ${component_alias})
+    __component_set_property(${component_target} __PREFIX ${prefix})
+
+    # Set Kconfig related properties on the component
+    __kconfig_component_init(${component_target})
+endfunction()
+
+#
+# Given a component directory, get the requirements by expanding it early. The expansion is performed
+# using a separate CMake script (the expansion is performed in a separate instance of CMake in scripting mode).
+#
+function(__component_get_requirements error requires_var priv_requires_var component_dir)
+    idf_build_get_property(idf_path IDF_PATH)
+    idf_build_get_property(build_properties_file BUILD_PROPERTIES_FILE)
+    idf_build_get_property(idf_target IDF_TARGET)
+
+    # This function assumes that the directory has been checked to contain a component, thus
+    # no check is performed here.
+    execute_process(COMMAND "${CMAKE_COMMAND}"
+        -D "IDF_PATH=${idf_path}"
+        -D "IDF_TARGET=${idf_target}"
+        -D "COMPONENT_DIR=${component_dir}"
+        -D "BUILD_PROPERTIES_FILE=${build_properties_file}"
+        -D "CMAKE_BUILD_EARLY_EXPANSION=1"
+        -P "${idf_path}/tools/cmake/scripts/component_get_requirements.cmake"
+        RESULT_VARIABLE result
+        ERROR_VARIABLE error
+        )
+
+    if(NOT result EQUAL 0)
+        set(error "${error}" PARENT_SCOPE)
+        return()
+    endif()
+
+    string(REGEX REPLACE ";" "\\\\;" _output "${error}")
+    string(REGEX REPLACE "\n" ";" _output "${_output}")
+    list(REVERSE _output)
+
+    if(_output)
+        list(GET _output 1 _output)
+
+        string(REGEX MATCH "\(.*\):::\(.*\)" _output "${_output}")
+
+        string(REPLACE ":" ";" requires "${CMAKE_MATCH_1}")
+        string(REPLACE ":" ";" priv_requires "${CMAKE_MATCH_2}")
+    endif()
+
+    set(${requires_var} ${requires} PARENT_SCOPE)
+    set(${priv_requires_var} ${priv_requires} PARENT_SCOPE)
+endfunction()
+
+# __component_add_sources, __component_check_target
+#
+# Utility macros for component registration. Adds source files and checks target requirements
+# respectively.
+macro(__component_add_sources sources)
+    set(sources "")
+    if(__SRCS)
+        if(__SRC_DIRS)
+            message(WARNING "SRCS and SRC_DIRS are both specified; ignoring SRC_DIRS.")
+        endif()
+        foreach(src ${__SRCS})
+            get_filename_component(src "${src}" ABSOLUTE BASE_DIR ${COMPONENT_DIR})
+            list(APPEND sources ${src})
+        endforeach()
+    else()
+        if(__SRC_DIRS)
+            foreach(dir ${__SRC_DIRS})
+                get_filename_component(abs_dir ${dir} ABSOLUTE BASE_DIR ${COMPONENT_DIR})
+
+                if(NOT IS_DIRECTORY ${abs_dir})
+                    message(FATAL_ERROR "SRC_DIRS entry '${dir}' does not exist.")
+                endif()
+
+                file(GLOB dir_sources "${abs_dir}/*.c" "${abs_dir}/*.cpp" "${abs_dir}/*.S")
+
+                if(dir_sources)
+                    foreach(src ${dir_sources})
+                        get_filename_component(src "${src}" ABSOLUTE BASE_DIR ${COMPONENT_DIR})
+                        list(APPEND sources "${src}")
+                    endforeach()
+                else()
+                    message(WARNING "No source files found for SRC_DIRS entry '${dir}'.")
+                endif()
+            endforeach()
+        endif()
+
+        if(__EXCLUDE_SRCS)
+            foreach(src ${__EXCLUDE_SRCS})
+                get_filename_component(src "${src}" ABSOLUTE)
+                list(REMOVE_ITEM source "${src}")
+            endforeach()
+        endif()
+    endif()
+
+    list(REMOVE_DUPLICATES sources)
+endmacro()
+
+macro(__component_check_target)
+    if(__REQUIRED_IDF_TARGETS)
+        idf_build_get_property(idf_target IDF_TARGET)
+        if(NOT idf_target IN_LIST __REQUIRED_IDF_TARGETS)
+            message(FATAL_ERROR "Component ${COMPONENT_NAME} only supports targets: ${__REQUIRED_IDF_TARGETS}")
+        endif()
+    endif()
+endmacro()
+
+# idf_component_get_property
+#
+# @brief Retrieve the value of the specified component property
+#
+# @param[out] var the variable to store the value of the property in
+# @param[in] component the component name or alias to get the value of the property of
+# @param[in] property the property to get the value of
+#
+# @param[in, optional] GENERATOR_EXPRESSION (option) retrieve the generator expression for the property
+#                   instead of actual value
+function(idf_component_get_property var component property)
+    cmake_parse_arguments(_ "GENERATOR_EXPRESSION" "" "" ${ARGN})
+    __component_get_target(component_target ${component})
+    if(__GENERATOR_EXPRESSION)
+        set(val "$<TARGET_PROPERTY:${component_target},${property}>")
+    else()
+        __component_get_property(val ${component_target} ${property})
+    endif()
+    set(${var} "${val}" PARENT_SCOPE)
+endfunction()
+
+# idf_component_set_property
+#
+# @brief Set the value of the specified component property related. The property is
+#        also added to the internal list of component properties if it isn't there already.
+#
+# @param[in] component component name or alias of the component to set the property of
+# @param[in] property the property to set the value of
+# @param[out] value value of the property to set to
+#
+# @param[in, optional] APPEND (option) append the value to the current value of the
+#                     property instead of replacing it
+function(idf_component_set_property component property val)
+    cmake_parse_arguments(_ "APPEND" "" "" ${ARGN})
+    __component_get_target(component_target ${component})
+
+    if(__APPEND)
+        __component_set_property(${component_target} ${property} "${val}" APPEND)
+    else()
+        __component_set_property(${component_target} ${property} "${val}")
+    endif()
+endfunction()
+
+# idf_component_register
+#
+# @brief Register a component to the build, creating component library targets etc.
+#
+# @param[in, optional] SRCS (multivalue) list of source files for the component
+# @param[in, optional] SRC_DIRS (multivalue) list of source directories to look for source files
+#                       in (.c, .cpp. .S); ignored when SRCS is specified.
+# @param[in, optional] EXCLUDE_SRCS (multivalue) used to exclude source files for the specified
+#                       SRC_DIRS
+# @param[in, optional] INCLUDE_DIRS (multivalue) public include directories for the created component library
+# @param[in, optional] PRIV_INCLUDE_DIRS (multivalue) private include directories for the created component library
+# @param[in, optional] LDFRAGMENTS (multivalue) linker script fragments for the component
+# @param[in, optional] REQUIRES (multivalue) publicly required components in terms of usage requirements
+# @param[in, optional] PRIV_REQUIRES (multivalue) privately required components in terms of usage requirements
+#                      or components only needed for functions/values defined in its project_include.cmake
+# @param[in, optional] REQUIRED_IDF_TARGETS (multivalue) the list of IDF build targets that the component only supports
+# @param[in, optional] EMBED_FILES (multivalue) list of binary files to embed with the component
+# @param[in, optional] EMBED_TXTFILES (multivalue) list of text files to embed with the component
+function(idf_component_register)
+    set(options)
+    set(single_value)
+    set(multi_value SRCS SRC_DIRS EXCLUDE_SRCS
+                    INCLUDE_DIRS PRIV_INCLUDE_DIRS LDFRAGMENTS REQUIRES
+                    PRIV_REQUIRES REQUIRED_IDF_TARGETS EMBED_FILES EMBED_TXTFILES)
+    cmake_parse_arguments(_ "${options}" "${single_value}" "${multi_value}" ${ARGN})
+
+    if(NOT __idf_component_context)
+        message(FATAL_ERROR "Called idf_component_register from a non-component directory.")
+    endif()
+
+    __component_check_target()
+    __component_add_sources(sources)
+
+    # Create the final target for the component. This target is the target that is
+    # visible outside the build system.
+    __component_get_target(component_target ${COMPONENT_ALIAS})
+    __component_get_property(component_lib ${component_target} COMPONENT_LIB)
+
+    # Use generator expression so that users can append/override flags even after call to
+    # idf_build_process
+    idf_build_get_property(include_directories INCLUDE_DIRECTORIES GENERATOR_EXPRESSION)
+    idf_build_get_property(compile_options COMPILE_OPTIONS GENERATOR_EXPRESSION)
+    idf_build_get_property(c_compile_options C_COMPILE_OPTIONS GENERATOR_EXPRESSION)
+    idf_build_get_property(cxx_compile_options CXX_COMPILE_OPTIONS GENERATOR_EXPRESSION)
+    idf_build_get_property(common_reqs ___COMPONENT_REQUIRES_COMMON)
+
+    include_directories("${include_directories}")
+    add_compile_options("${compile_options}")
+    add_c_compile_options("${c_compile_options}")
+    add_cxx_compile_options("${cxx_compile_options}")
+
+    # Unfortunately add_definitions() does not support generator expressions. A new command
+    # add_compile_definition() does but is only available on CMake 3.12 or newer. This uses
+    # add_compile_options(), which can add any option as the workaround.
+    #
+    # TODO: Use add_compile_definitions() once minimum supported version is 3.12 or newer.
+    idf_build_get_property(compile_definitions COMPILE_DEFINITIONS GENERATOR_EXPRESSION)
+    add_compile_options("${compile_definitions}")
+
+    list(REMOVE_ITEM common_reqs ${component_lib})
+    link_libraries(${common_reqs})
+
+    idf_build_get_property(sdkconfig_h SDKCONFIG_HEADER)
+    get_filename_component(sdkconfig_h ${sdkconfig_h} DIRECTORY)
+
+    # The contents of 'sources' is from the __component_add_sources call
+    if(sources OR __EMBED_FILES OR __EMBED_TXTFILES)
+        add_library(${component_lib} STATIC ${sources})
+        __component_set_property(${component_target} COMPONENT_TYPE LIBRARY)
+        target_include_directories(${component_lib} PUBLIC ${__INCLUDE_DIRS})
+        target_include_directories(${component_lib} PRIVATE ${__PRIV_INCLUDE_DIRS})
+        target_include_directories(${component_lib} PUBLIC ${sdkconfig_h})
+        set_target_properties(${component_lib} PROPERTIES OUTPUT_NAME ${COMPONENT_NAME})
+        __ldgen_add_component(${component_lib})
+    else()
+        add_library(${component_lib} INTERFACE)
+        __component_set_property(${component_target} COMPONENT_TYPE CONFIG_ONLY)
+        target_include_directories(${component_lib} INTERFACE ${__INCLUDE_DIRS})
+        target_include_directories(${component_lib} INTERFACE ${sdkconfig_h})
+    endif()
+
+    # Alias the static/interface library created for linking to external targets.
+    # The alias is the <prefix>::<component name> name.
+    __component_get_property(component_alias ${component_target} COMPONENT_ALIAS)
+    add_library(${component_alias} ALIAS ${component_lib})
+
+    # Perform other component processing, such as embedding binaries and processing linker
+    # script fragments
+    foreach(file ${__EMBED_FILES})
+        target_add_binary_data(${component_lib} "${file}" "BINARY")
+    endforeach()
+
+    foreach(file ${__EMBED_TXTFILES})
+        target_add_binary_data(${component_lib} "${file}" "TEXT")
+    endforeach()
+
+    if(__LDFRAGMENTS)
+        __ldgen_add_fragment_files("${__LDFRAGMENTS}")
+    endif()
+
+    # Add the component to built components
+    idf_build_set_property(__BUILD_COMPONENTS ${component_lib} APPEND)
+    idf_build_set_property(BUILD_COMPONENTS ${component_alias} APPEND)
+
+    # Make the COMPONENT_LIB variable available in the component CMakeLists.txt
+    set(COMPONENT_LIB ${component_lib} PARENT_SCOPE)
+    # COMPONENT_TARGET is deprecated but is made available with same function
+    # as COMPONENT_LIB for compatibility.
+    set(COMPONENT_TARGET ${component_lib} PARENT_SCOPE)
+endfunction()
+
+#
+# Deprecated functions
+#
+
+# register_component
+#
+# Compatibility function for registering 3.xx style components.
+macro(register_component)
+    spaces2list(COMPONENT_SRCS)
+    spaces2list(COMPONENT_SRCDIRS)
+    spaces2list(COMPONENT_ADD_INCLUDEDIRS)
+    spaces2list(COMPONENT_PRIV_INCLUDEDIRS)
+    spaces2list(COMPONENT_REQUIRES)
+    spaces2list(COMPONENT_PRIV_REQUIRES)
+    spaces2list(COMPONENT_ADD_LDFRAGMENTS)
+    spaces2list(COMPONENT_EMBED_FILES)
+    spaces2list(COMPONENT_EMBED_TXTFILES)
+    spaces2list(COMPONENT_SRCEXCLUDE)
+    idf_component_register(SRCS "${COMPONENT_SRCS}"
+                        SRC_DIRS "${COMPONENT_SRCDIRS}"
+                        INCLUDE_DIRS "${COMPONENT_ADD_INCLUDEDIRS}"
+                        PRIV_INCLUDE_DIRS "${COMPONENT_PRIV_INCLUDEDIRS}"
+                        REQUIRES "${COMPONENT_REQUIRES}"
+                        PRIV_REQUIRES "${COMPONENT_PRIV_REQUIRES}"
+                        LDFRAGMENTS "${COMPONENT_ADD_LDFRAGMENTS}"
+                        EMBED_FILES "${COMPONENT_EMBED_FILES}"
+                        EMBED_TXTFILES "${COMPONENT_EMBED_TXTFILES}"
+                        EXCLUDE_SRCS "${COMPONENT_SRCEXCLUDE}")
+endmacro()
+
+# require_idf_targets
+#
+# Compatibility function for requiring IDF build targets for 3.xx style components.
+function(require_idf_targets)
+    set(__REQUIRED_IDF_TARGETS "${ARGN}")
+    __component_check_target()
+endfunction()
+
+# register_config_only_component
+#
+# Compatibility function for registering 3.xx style config components.
+macro(register_config_only_component)
+    register_component()
+endmacro()
+
+# component_compile_options
+#
+# Wrapper around target_compile_options that passes the component name
+function(component_compile_options)
+    target_compile_options(${COMPONENT_LIB} PRIVATE ${ARGV})
+endfunction()
+
+# component_compile_definitions
+#
+# Wrapper around target_compile_definitions that passes the component name
+function(component_compile_definitions)
+    target_compile_definitions(${COMPONENT_LIB} PRIVATE ${ARGV})
+endfunction()
\ No newline at end of file
diff --git a/tools/cmake/component_utils.cmake b/tools/cmake/component_utils.cmake
deleted file mode 100644 (file)
index 4100664..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-function(debug message)
-    if(DEBUG)
-        message(STATUS "${message}")
-    endif()
-endfunction()
-
-# 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
deleted file mode 100644 (file)
index d2be1ff..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-# Given a list of components in 'component_paths', filter only paths to the components
-# mentioned in 'components' and return as a list in 'result_paths'
-function(components_get_paths component_paths components result_paths)
-    set(result "")
-    foreach(path ${component_paths})
-        get_filename_component(name "${path}" NAME)
-        if("${name}" IN_LIST components)
-            list(APPEND result "${name}")
-        endif()
-    endforeach()
-    set("${result_path}" "${result}" PARENT_SCOPE)
-endfunction()
-
-# Add a component to the build, using the COMPONENT variables defined
-# in the parent
-#
-function(register_component)
-    get_filename_component(component_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
-
-    spaces2list(COMPONENT_SRCS)
-    spaces2list(COMPONENT_SRCDIRS)
-    spaces2list(COMPONENT_ADD_INCLUDEDIRS)
-    spaces2list(COMPONENT_SRCEXCLUDE)
-
-    if(COMPONENT_SRCDIRS)
-        # Warn user if both COMPONENT_SRCDIRS and COMPONENT_SRCS are set
-        if(COMPONENT_SRCS)
-            message(WARNING "COMPONENT_SRCDIRS and COMPONENT_SRCS are both set, COMPONENT_SRCS will be ignored")
-        endif()
-
-        set(COMPONENT_SRCS "")
-
-        foreach(dir ${COMPONENT_SRCDIRS})
-            get_filename_component(abs_dir ${dir} ABSOLUTE BASE_DIR ${component_dir})
-            if(NOT IS_DIRECTORY ${abs_dir})
-                message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: COMPONENT_SRCDIRS entry '${dir}' does not exist")
-            endif()
-
-            file(GLOB matches "${abs_dir}/*.c" "${abs_dir}/*.cpp" "${abs_dir}/*.S")
-            if(matches)
-                list(SORT matches)
-                set(COMPONENT_SRCS "${COMPONENT_SRCS};${matches}")
-            else()
-                message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: COMPONENT_SRCDIRS entry '${dir}' has no source files")
-            endif()
-        endforeach()
-    endif()
-
-    # Remove COMPONENT_SRCEXCLUDE matches
-    foreach(exclude ${COMPONENT_SRCEXCLUDE})
-        get_filename_component(exclude "${exclude}" ABSOLUTE ${component_dir})
-        foreach(src ${COMPONENT_SRCS})
-            get_filename_component(abs_src "${src}" ABSOLUTE ${component_dir})
-            if("${exclude}" STREQUAL "${abs_src}")  # compare as canonical paths
-                list(REMOVE_ITEM COMPONENT_SRCS "${src}")
-            endif()
-        endforeach()
-    endforeach()
-
-    # add as a PUBLIC library (if there are source files) or INTERFACE (if header only)
-    if(COMPONENT_SRCS OR embed_binaries)
-        add_library(${COMPONENT_TARGET} STATIC ${COMPONENT_SRCS})
-        set(include_type PUBLIC)
-
-        set_property(TARGET ${COMPONENT_TARGET} PROPERTY OUTPUT_NAME ${COMPONENT_NAME})
-
-        ldgen_component_add(${COMPONENT_TARGET})
-    else()
-        add_library(${COMPONENT_TARGET} INTERFACE) # header-only component
-        set(include_type INTERFACE)
-    endif()
-
-    # binaries to embed directly in library
-    spaces2list(COMPONENT_EMBED_FILES)
-    spaces2list(COMPONENT_EMBED_TXTFILES)
-    foreach(embed_data ${COMPONENT_EMBED_FILES} ${COMPONENT_EMBED_TXTFILES})
-        if(embed_data IN_LIST COMPONENT_EMBED_TXTFILES)
-            set(embed_type "TEXT")
-        else()
-            set(embed_type "BINARY")
-        endif()
-        target_add_binary_data("${COMPONENT_TARGET}" "${embed_data}" "${embed_type}")
-    endforeach()
-
-    # add component public includes
-    foreach(include_dir ${COMPONENT_ADD_INCLUDEDIRS})
-        get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir})
-        if(NOT IS_DIRECTORY ${abs_dir})
-            message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: "
-                "COMPONENT_ADD_INCLUDEDIRS entry '${include_dir}' not found")
-        endif()
-        target_include_directories(${COMPONENT_TARGET} ${include_type} ${abs_dir})
-    endforeach()
-
-    # add component private includes
-    foreach(include_dir ${COMPONENT_PRIV_INCLUDEDIRS})
-        if(${include_type} STREQUAL INTERFACE)
-            message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE} "
-                "sets no component source files but sets COMPONENT_PRIV_INCLUDEDIRS")
-        endif()
-
-        get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir})
-        if(NOT IS_DIRECTORY ${abs_dir})
-            message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: "
-                "COMPONENT_PRIV_INCLUDEDIRS entry '${include_dir}' does not exist")
-        endif()
-        target_include_directories(${COMPONENT_TARGET} PRIVATE ${abs_dir})
-    endforeach()
-
-    if(${COMPONENT_NAME} IN_LIST BUILD_TEST_COMPONENTS)
-        target_link_libraries(${COMPONENT_TARGET} "-L${CMAKE_CURRENT_BINARY_DIR}")
-        target_link_libraries(${COMPONENT_TARGET} "-Wl,--whole-archive -l${COMPONENT_NAME} -Wl,--no-whole-archive")
-    endif()
-
-    if(COMPONENT_SRCS OR embed_binaries)
-        target_include_directories(${COMPONENT_TARGET} PUBLIC ${IDF_INCLUDE_DIRECTORIES})
-        target_compile_options(${COMPONENT_TARGET} PUBLIC ${IDF_COMPILE_OPTIONS})
-        target_compile_options(${COMPONENT_TARGET} PUBLIC $<$<COMPILE_LANGUAGE:C>:${IDF_C_COMPILE_OPTIONS}>)
-        target_compile_options(${COMPONENT_TARGET} PUBLIC $<$<COMPILE_LANGUAGE:CXX>:${IDF_CXX_COMPILE_OPTIONS}>)
-        target_compile_definitions(${COMPONENT_TARGET} PUBLIC ${IDF_COMPILE_DEFINITIONS})
-    endif()
-
-    if(COMPONENT_ADD_LDFRAGMENTS)
-        spaces2list(COMPONENT_ADD_LDFRAGMENTS)
-        ldgen_add_fragment_files(${COMPONENT_TARGET} "${COMPONENT_ADD_LDFRAGMENTS}")
-    endif()
-endfunction()
-
-function(register_config_only_component)
-    get_filename_component(component_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
-    get_filename_component(component ${component_dir} NAME)
-
-    # No-op for now...
-endfunction()
-
-function(add_component_dependencies target dep dep_type)
-    get_target_property(target_type ${target} TYPE)
-    get_target_property(target_imported ${target} IMPORTED)
-
-    if(${target_type} STREQUAL STATIC_LIBRARY OR ${target_type} STREQUAL EXECUTABLE)
-        if(TARGET ${dep})
-            # Add all compile options exported by dep into target
-            target_include_directories(${target} ${dep_type}
-                $<TARGET_PROPERTY:${dep},INTERFACE_INCLUDE_DIRECTORIES>)
-            target_compile_definitions(${target} ${dep_type}
-                $<TARGET_PROPERTY:${dep},INTERFACE_COMPILE_DEFINITIONS>)
-            target_compile_options(${target} ${dep_type}
-                $<TARGET_PROPERTY:${dep},INTERFACE_COMPILE_OPTIONS>)
-        endif()
-    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()
-
-# component_compile_options
-#
-# Wrapper around target_compile_options that passes the component name
-function(component_compile_options)
-    target_compile_options(${COMPONENT_TARGET} PRIVATE ${ARGV})
-endfunction()
-
-# component_compile_definitions
-#
-# Wrapper around target_compile_definitions that passes the component name
-function(component_compile_definitions)
-    target_compile_definitions(${COMPONENT_TARGET} PRIVATE ${ARGV})
-endfunction()
-
-# component_get_target
-#
-# Get the library target created for the given component
-function(component_get_target var component)
-    get_property(prefix GLOBAL PROPERTY __IDF_COMPONENTS_PREFIX)
-    set(${var} ${prefix}_${component} PARENT_SCOPE)
-endfunction()
index d2d46d462c61dbc050455e50e5eec85de87ab794..80d9296d62c647925de21fc90f4b6831f3b3097c 100755 (executable)
@@ -173,7 +173,7 @@ def convert_component(project_path, component_path):
         else:
             f.write("register_config_only_component()\n")
         if cflags is not None:
-            f.write("component_compile_options(%s)\n" % cflags)
+            f.write("target_compile_options(${COMPONENT_LIB} PRIVATE %s)\n" % cflags)
 
     print("Converted %s" % cmakelists_path)
 
index 1180fc029a41bf41f4d9e73b74dcf95b3de6c3de..2ea681da1be0f0c4b82da2e84fe233398500f4e4 100644 (file)
@@ -31,7 +31,8 @@ function(crosstool_version_check expected_ctng_version)
 endfunction()
 
 function(get_expected_ctng_version _toolchain_ver _gcc_ver)
-    file(STRINGS ${IDF_PATH}/tools/toolchain_versions.mk config_contents)
+    idf_build_get_property(idf_path IDF_PATH)
+    file(STRINGS ${idf_path}/tools/toolchain_versions.mk config_contents)
     foreach(name_and_value ${config_contents})
         # Strip spaces
         string(REPLACE " " "" name_and_value ${name_and_value})
diff --git a/tools/cmake/idf.cmake b/tools/cmake/idf.cmake
new file mode 100644 (file)
index 0000000..9d45fd9
--- /dev/null
@@ -0,0 +1,45 @@
+get_property(__idf_env_set GLOBAL PROPERTY __IDF_ENV_SET)
+if(NOT __idf_env_set)
+    # Infer an IDF_PATH relative to the tools/cmake directory
+    get_filename_component(_idf_path "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
+    file(TO_CMAKE_PATH "${_idf_path}" _idf_path)
+
+    # Get the path set in environment
+    set(idf_path $ENV{IDF_PATH})
+    file(TO_CMAKE_PATH "${idf_path}" idf_path)
+
+    # Environment IDF_PATH should match the inferred IDF_PATH. If not, warn the user.
+    if(idf_path)
+        if(NOT idf_path STREQUAL _idf_path)
+            message(WARNING "IDF_PATH environment variable is different from inferred IDF_PATH.
+                            Check if your project's top-level CMakeLists.txt includes the right
+                            CMake files. Environment IDF_PATH will be used for the build.")
+        endif()
+    else()
+        message(WARNING "IDF_PATH environment variable not found. Setting IDF_PATH to '${_idf_path}'.")
+        set(idf_path ${_idf_path})
+        set(ENV{IDF_PATH} ${_idf_path})
+    endif()
+
+    # Include other CMake modules required
+    set(CMAKE_MODULE_PATH
+        "${idf_path}/tools/cmake"
+        "${idf_path}/tools/cmake/third_party"
+        ${CMAKE_MODULE_PATH})
+    include(build)
+
+    set(IDF_PATH ${idf_path})
+
+    include(GetGitRevisionDescription)
+    include(git_submodules)
+    include(crosstool_version_check)
+    include(kconfig)
+    include(component)
+    include(utilities)
+    include(targets)
+    include(ldgen)
+
+    __build_init("${idf_path}")
+
+    set_property(GLOBAL PROPERTY __IDF_ENV_SET 1)
+endif()
\ No newline at end of file
diff --git a/tools/cmake/idf_functions.cmake b/tools/cmake/idf_functions.cmake
deleted file mode 100644 (file)
index 697ef45..0000000
+++ /dev/null
@@ -1,326 +0,0 @@
-#
-# Load cmake modules
-#
-
-get_property(__idf_environment_set GLOBAL PROPERTY __IDF_ENVIRONMENT_SET)
-
-if(NOT __idf_environment_set)
-
-    # Set IDF_PATH, as nothing else will work without this.
-    set(IDF_PATH "$ENV{IDF_PATH}")
-    if(NOT IDF_PATH)
-        # Documentation says you should set IDF_PATH in your environment, but we
-        # can infer it relative to tools/cmake directory if it's not set.
-        get_filename_component(IDF_PATH "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE)
-    endif()
-    file(TO_CMAKE_PATH "${IDF_PATH}" IDF_PATH)
-    set(ENV{IDF_PATH} ${IDF_PATH})
-
-    set(CMAKE_MODULE_PATH
-        "${IDF_PATH}/tools/cmake"
-        "${IDF_PATH}/tools/cmake/third_party"
-        ${CMAKE_MODULE_PATH})
-    include(utilities)
-    include(components)
-    include(kconfig)
-    include(targets)
-    include(git_submodules)
-    include(GetGitRevisionDescription)
-    include(crosstool_version_check)
-    include(ldgen)
-
-    set_default(PYTHON "python")
-
-    if(NOT PYTHON_DEPS_CHECKED AND NOT BOOTLOADER_BUILD)
-        message(STATUS "Checking Python dependencies...")
-        execute_process(COMMAND "${PYTHON}" "${IDF_PATH}/tools/check_python_dependencies.py"
-            RESULT_VARIABLE result)
-        if(NOT result EQUAL 0)
-            message(FATAL_ERROR "Some Python dependencies must be installed. Check above message for details.")
-        endif()
-    endif()
-
-    idf_set_target()
-
-    set_property(GLOBAL APPEND PROPERTY __IDF_COMPONENTS_PREFIX "idf_component")
-    set_property(GLOBAL PROPERTY __IDF_ENVIRONMENT_SET 1)
-endif()
-
-macro(idf_set_variables)
-    set_default(IDF_BUILD_ARTIFACTS OFF)
-
-    if(IDF_BUILD_ARTIFACTS)
-        if(NOT IDF_BUILD_ARTIFACTS_DIR OR NOT IDF_PROJECT_EXECUTABLE)
-            message(FATAL_ERROR "IDF_BUILD_ARTIFACTS and IDF_PROJECT_EXECUTABLE needs to be specified \
-                    if IDF_BUILD_ARTIFACTS is ON.")
-        endif()
-    endif()
-
-    set_default(IDF_COMPONENT_DIRS "${IDF_EXTRA_COMPONENT_DIRS} ${IDF_PATH}/components")
-    set_default(IDF_COMPONENTS "")
-    set_default(IDF_COMPONENT_REQUIRES_COMMON "cxx ${IDF_TARGET} newlib freertos heap log \
-                                                esp_rom esp_common xtensa")
-
-    list(FIND IDF_COMPONENT_REQUIRES_COMMON "${IDF_TARGET}" result)
-    if(result EQUAL -1)
-        list(APPEND IDF_COMPONENT_REQUIRES_COMMON "${IDF_TARGET}")
-    endif()
-
-    if(CONFIG_LEGACY_INCLUDE_COMMON_HEADERS)
-        list(APPEND IDF_COMPONENT_REQUIRES_COMMON "soc")
-    endif()
-
-    set(IDF_PROJECT_PATH "${CMAKE_SOURCE_DIR}")
-
-    set(ESP_PLATFORM 1 CACHE BOOL INTERNAL)
-
-    spaces2list(IDF_COMPONENT_DIRS)
-    spaces2list(IDF_COMPONENTS)
-    spaces2list(IDF_COMPONENT_REQUIRES_COMMON)
-endmacro()
-
-# Add all the IDF global compiler & preprocessor options
-# (applied to all components). Some are config-dependent
-#
-# If you only want to set options for a particular component,
-# don't call or edit this function. TODO DESCRIBE WHAT TO DO INSTEAD
-#
-function(idf_set_global_compile_options)
-    # Temporary trick to support both gcc5 and gcc8 builds
-    if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 5.2.0)
-        set(GCC_NOT_5_2_0 0 CACHE STRING "GCC is 5.2.0 version")
-    else()
-        set(GCC_NOT_5_2_0 1 CACHE STRING "GCC is not 5.2.0 version")
-    endif()
-    list(APPEND compile_definitions "GCC_NOT_5_2_0=${GCC_NOT_5_2_0}")
-
-    list(APPEND compile_definitions "ESP_PLATFORM" "HAVE_CONFIG_H")
-
-    spaces2list(CMAKE_C_FLAGS)
-    spaces2list(CMAKE_CXX_FLAGS)
-    list(APPEND compile_options "${CMAKE_C_FLAGS}")
-    list(APPEND c_compile_options "${CMAKE_C_FLAGS}")
-    list(APPEND cxx_compile_options "${CMAKE_CXX_FLAGS}")
-
-    if(CONFIG_OPTIMIZATION_LEVEL_RELEASE)
-        list(APPEND compile_options "-Os")
-    else()
-        list(APPEND compile_options "-Og")
-    endif()
-
-    list(APPEND c_compile_options "-std=gnu99")
-    list(APPEND cxx_compile_options "-std=gnu++11" "-fno-rtti")
-
-    # IDF uses some GNU extension from libc
-    list(APPEND compile_definitions "_GNU_SOURCE")
-
-    if(CONFIG_CXX_EXCEPTIONS)
-        list(APPEND cxx_compile_options "-fexceptions")
-    else()
-        list(APPEND cxx_compile_options "-fno-exceptions")
-    endif()
-
-    # Default compiler configuration
-    list(APPEND compile_options "-ffunction-sections"
-                                "-fdata-sections"
-                                "-fstrict-volatile-bitfields"
-                                "-nostdlib")
-
-    list(APPEND compile_options "-Wall"
-                                "-Werror=all"
-                                "-Wno-error=unused-function"
-                                "-Wno-error=unused-but-set-variable"
-                                "-Wno-error=unused-variable"
-                                "-Wno-error=deprecated-declarations"
-                                "-Wextra"
-                                "-Wno-unused-parameter"
-                                "-Wno-sign-compare")
-
-    list(APPEND c_compile_options "-Wno-old-style-declaration")
-
-    if(CONFIG_DISABLE_GCC8_WARNINGS)
-        list(APPEND compile_options
-            "-Wno-parentheses"
-            "-Wno-sizeof-pointer-memaccess"
-            "-Wno-clobbered"
-        )
-
-        # doesn't use GCC_NOT_5_2_0 because idf_set_global_variables was not called before
-        if(NOT CMAKE_C_COMPILER_VERSION VERSION_EQUAL 5.2.0)
-            list(APPEND compile_options
-                "-Wno-format-overflow"
-                "-Wno-stringop-truncation"
-                "-Wno-misleading-indentation"
-                "-Wno-cast-function-type"
-                "-Wno-implicit-fallthrough"
-                "-Wno-unused-const-variable"
-                "-Wno-switch-unreachable"
-                "-Wno-format-truncation"
-                "-Wno-memset-elt-size"
-                "-Wno-int-in-bool-context"
-            )
-        endif()
-    endif()
-
-    # Stack protection
-    if(NOT BOOTLOADER_BUILD)
-        if(CONFIG_STACK_CHECK_NORM)
-            list(APPEND compile_options "-fstack-protector")
-        elseif(CONFIG_STACK_CHECK_STRONG)
-            list(APPEND compile_options "-fstack-protector-strong")
-        elseif(CONFIG_STACK_CHECK_ALL)
-            list(APPEND compile_options "-fstack-protector-all")
-        endif()
-    endif()
-
-    if(CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED)
-        list(APPEND compile_definitions "NDEBUG")
-    endif()
-
-    # Always generate debug symbols (even in Release mode, these don't
-    # go into the final binary so have no impact on size)
-    list(APPEND compile_options "-ggdb")
-
-    # Use EXTRA_CFLAGS, EXTRA_CXXFLAGS and EXTRA_CPPFLAGS to add more priority options to the compiler
-    # EXTRA_CPPFLAGS is used for both C and C++
-    # Unlike environments' CFLAGS/CXXFLAGS/CPPFLAGS which work for both host and target build,
-    # these works only for target build
-    set(EXTRA_CFLAGS "$ENV{EXTRA_CFLAGS}")
-    set(EXTRA_CXXFLAGS "$ENV{EXTRA_CXXFLAGS}")
-    set(EXTRA_CPPFLAGS "$ENV{EXTRA_CPPFLAGS}")
-    spaces2list(EXTRA_CFLAGS)
-    spaces2list(EXTRA_CXXFLAGS)
-    spaces2list(EXTRA_CPPFLAGS)
-    list(APPEND c_compile_options ${EXTRA_CFLAGS})
-    list(APPEND cxx_compile_options ${EXTRA_CXXFLAGS})
-    list(APPEND compile_options ${EXTRA_CPPFLAGS})
-
-    set_default(IDF_COMPILE_DEFINITIONS "${compile_definitions}")
-    set_default(IDF_COMPILE_OPTIONS "${compile_options}")
-    set_default(IDF_C_COMPILE_OPTIONS "${c_compile_options}")
-    set_default(IDF_CXX_COMPILE_OPTIONS "${cxx_compile_options}")
-    set_default(IDF_INCLUDE_DIRECTORIES "${CONFIG_DIR}")
-
-    set(IDF_COMPILE_DEFINITIONS ${IDF_COMPILE_DEFINITIONS} PARENT_SCOPE)
-    set(IDF_COMPILE_OPTIONS ${IDF_COMPILE_OPTIONS} PARENT_SCOPE)
-    set(IDF_C_COMPILE_OPTIONS ${IDF_C_COMPILE_OPTIONS} PARENT_SCOPE)
-    set(IDF_CXX_COMPILE_OPTIONS ${IDF_CXX_COMPILE_OPTIONS} PARENT_SCOPE)
-    set(IDF_INCLUDE_DIRECTORIES ${CONFIG_DIR} PARENT_SCOPE)
-endfunction()
-
-# Verify the IDF environment is configured correctly (environment, toolchain, etc)
-function(idf_verify_environment)
-    if(NOT CMAKE_PROJECT_NAME)
-        message(FATAL_ERROR "Internal error, IDF project.cmake should have set this variable already")
-    endif()
-
-    # Check toolchain is configured properly in cmake
-    if(NOT ( ${CMAKE_SYSTEM_NAME} STREQUAL "Generic" AND ${CMAKE_C_COMPILER} MATCHES xtensa))
-        message(FATAL_ERROR "Internal error, toolchain has not been set correctly by project "
-            "(or an invalid CMakeCache.txt file has been generated somehow)")
-    endif()
-
-    #
-    # Warn if the toolchain version doesn't match
-    #
-    # TODO: make these platform-specific for diff toolchains
-    get_expected_ctng_version(expected_toolchain expected_gcc)
-    gcc_version_check("${expected_gcc}")
-    crosstool_version_check("${expected_toolchain}")
-endfunction()
-
-# idf_get_git_revision
-#
-# Set global IDF_VER to the git revision of ESP-IDF.
-#
-# Running git_describe() here automatically triggers rebuilds
-# if the ESP-IDF git version changes
-function(idf_get_git_revision)
-    git_describe(IDF_VER_GIT "${IDF_PATH}")
-    if(EXISTS "${IDF_PATH}/version.txt")
-        file(STRINGS "${IDF_PATH}/version.txt" IDF_VER_T)
-        set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${IDF_PATH}/version.txt")
-    else()
-        set(IDF_VER_T ${IDF_VER_GIT})
-    endif()
-    # cut IDF_VER to required 32 characters.
-    string(SUBSTRING "${IDF_VER_T}" 0 31 IDF_VER)
-    message(STATUS "IDF_VER: ${IDF_VER}")
-    add_definitions(-DIDF_VER=\"${IDF_VER}\")
-    git_submodule_check("${IDF_PATH}")
-    set(IDF_VER ${IDF_VER} PARENT_SCOPE)
-endfunction()
-
-# app_get_revision
-#
-# Set global PROJECT_VER
-#
-# If PROJECT_VER variable set in project CMakeLists.txt file, its value will be used.
-# Else, if the _project_path/version.txt exists, its contents will be used as PROJECT_VER.
-# Else, if the project is located inside a Git repository, the output of git describe will be used.
-# Otherwise, PROJECT_VER will be "1".
-function(app_get_revision _project_path)
-    if(NOT DEFINED PROJECT_VER)
-        if(EXISTS "${_project_path}/version.txt")
-            file(STRINGS "${_project_path}/version.txt" PROJECT_VER)
-            set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_project_path}/version.txt")
-        else()
-            git_describe(PROJECT_VER_GIT "${_project_path}")
-            if(PROJECT_VER_GIT)
-                set(PROJECT_VER ${PROJECT_VER_GIT})
-            else()
-                message(STATUS "Project is not inside a git repository, \
-                        will not use 'git describe' to determine PROJECT_VER.")
-                set(PROJECT_VER "1")
-            endif()
-        endif()
-    endif()
-    message(STATUS "Project version: ${PROJECT_VER}")
-    set(PROJECT_VER ${PROJECT_VER} PARENT_SCOPE)
-endfunction()
-
-# idf_link_components
-#
-# Link library components to the target
-function(idf_link_components target components)
-    foreach(component ${components})
-        component_get_target(component_target ${component})
-
-        # Add each component library's link-time dependencies (which are otherwise ignored) to the executable
-        # LINK_DEPENDS in order to trigger a re-link when needed (on Ninja/Makefile generators at least).
-        # (maybe this should probably be something CMake does, but it doesn't do it...)
-        if(TARGET ${component_target})
-            get_target_property(type ${component_target} TYPE)
-            get_target_property(imported ${component_target} IMPORTED)
-            if(NOT imported)
-                if(${type} STREQUAL STATIC_LIBRARY OR ${type} STREQUAL EXECUTABLE)
-                    get_target_property(link_depends "${component_target}" LINK_DEPENDS)
-                    if(link_depends)
-                        set_property(TARGET ${target} APPEND PROPERTY LINK_DEPENDS "${link_depends}")
-                    endif()
-                endif()
-            endif()
-
-            if(${type} MATCHES .+_LIBRARY)
-                list(APPEND libraries ${component_target})
-            endif()
-        endif()
-    endforeach()
-
-    if(libraries)
-        # gc-sections is necessary for linking some IDF binary libraries
-        # (and without it, IDF apps are much larger than they should be)
-        target_link_libraries(${target} "-Wl,--gc-sections")
-        target_link_libraries(${target} "-Wl,--start-group")
-        target_link_libraries(${target} ${libraries})
-        message(STATUS "Component libraries: ${IDF_COMPONENT_LIBRARIES}")
-    endif()
-endfunction()
-
-# idf_import_components
-#
-# Adds ESP-IDF as a subdirectory to the current project and imports the components
-function(idf_import_components var idf_path build_path)
-    add_subdirectory(${idf_path} ${build_path})
-    set(${var} ${BUILD_COMPONENTS} PARENT_SCOPE)
-endfunction()
index 3c8dfb2ccf952530f59105a4b3242b4e714a5575..813363a931efb2413c058d534df00953c7936810 100644 (file)
 include(ExternalProject)
 
-macro(kconfig_set_variables)
-    set_default(IDF_SDKCONFIG_DEFAULTS "")
-
-    set_default(CONFIG_DIR ${IDF_BUILD_ARTIFACTS_DIR}/config)
-    set_default(SDKCONFIG ${IDF_PROJECT_PATH}/sdkconfig)
-    set(SDKCONFIG_HEADER ${CONFIG_DIR}/sdkconfig.h)
-    set(SDKCONFIG_CMAKE ${CONFIG_DIR}/sdkconfig.cmake)
-    set(SDKCONFIG_JSON ${CONFIG_DIR}/sdkconfig.json)
-    set(KCONFIG_JSON_MENUS ${CONFIG_DIR}/kconfig_menus.json)
-
-    set(ROOT_KCONFIG ${IDF_PATH}/Kconfig)
-endmacro()
-
-if(CMAKE_HOST_WIN32)
-    # Prefer a prebuilt mconf-idf on Windows
-    if(DEFINED ENV{MSYSTEM})
-        find_program(WINPTY winpty)
-    else()
-        unset(WINPTY CACHE)  # in case previous CMake run was in a tty and this one is not
-    endif()
-    find_program(MCONF mconf-idf)
+function(__kconfig_init)
+    idf_build_get_property(idf_path IDF_PATH)
+    if(CMAKE_HOST_WIN32)
+        # Prefer a prebuilt mconf-idf on Windows
+        if(DEFINED ENV{MSYSTEM})
+            find_program(WINPTY winpty)
+        else()
+            unset(WINPTY CACHE)  # in case previous CMake run was in a tty and this one is not
+        endif()
+        find_program(MCONF mconf-idf)
+
+        # Fall back to the old binary which was called 'mconf' not 'mconf-idf'
+        if(NOT MCONF)
+            find_program(MCONF mconf)
+            if(MCONF)
+                message(WARNING "Falling back to mconf binary '${MCONF}' not mconf-idf. "
+                    "This is probably because an old version of IDF mconf is installed and this is fine. "
+                    "However if there are config problems please check the Getting Started guide for your platform.")
+            endif()
+        endif()
 
-    # Fall back to the old binary which was called 'mconf' not 'mconf-idf'
-    if(NOT MCONF)
-        find_program(MCONF mconf)
-        if(MCONF)
-            message(WARNING "Falling back to mconf binary '${MCONF}' not mconf-idf. "
-                "This is probably because an old version of IDF mconf is installed and this is fine. "
-                "However if there are config problems please check the Getting Started guide for your platform.")
+        if(NOT MCONF)
+            find_program(NATIVE_GCC gcc)
+            if(NOT NATIVE_GCC)
+                message(FATAL_ERROR
+                    "Windows requires a prebuilt mconf-idf for your platform "
+                    "on the PATH, or an MSYS2 version of gcc on the PATH to build mconf-idf. "
+                    "Consult the setup docs for ESP-IDF on Windows.")
+            endif()
+        elseif(WINPTY)
+            set(MCONF "${WINPTY}" "${MCONF}")
         endif()
     endif()
 
     if(NOT MCONF)
-        find_program(NATIVE_GCC gcc)
-        if(NOT NATIVE_GCC)
-            message(FATAL_ERROR
-                "Windows requires a prebuilt mconf-idf for your platform "
-                "on the PATH, or an MSYS2 version of gcc on the PATH to build mconf-idf. "
-                "Consult the setup docs for ESP-IDF on Windows.")
-        endif()
-    elseif(WINPTY)
-        set(MCONF "${WINPTY}" "${MCONF}")
+        # Use the existing Makefile to build mconf (out of tree) when needed
+        #
+        set(MCONF ${CMAKE_BINARY_DIR}/kconfig_bin/mconf-idf)
+
+        externalproject_add(mconf-idf
+            SOURCE_DIR ${idf_path}/tools/kconfig
+            CONFIGURE_COMMAND ""
+            BINARY_DIR "kconfig_bin"
+            BUILD_COMMAND make -f ${idf_path}/tools/kconfig/Makefile mconf-idf
+            BUILD_BYPRODUCTS ${MCONF}
+            INSTALL_COMMAND ""
+            EXCLUDE_FROM_ALL 1
+            )
+
+        file(GLOB mconf_srcfiles ${idf_path}/tools/kconfig/*.c)
+        externalproject_add_stepdependencies(mconf-idf build
+            ${mconf_srcfiles}
+            ${idf_path}/tools/kconfig/Makefile
+            ${CMAKE_CURRENT_LIST_FILE})
+        unset(mconf_srcfiles)
+
+        set(menuconfig_depends DEPENDS mconf-idf)
     endif()
-endif()
-
-if(NOT MCONF)
-    # Use the existing Makefile to build mconf (out of tree) when needed
-    #
-    set(MCONF ${CMAKE_BINARY_DIR}/kconfig_bin/mconf-idf)
-
-    externalproject_add(mconf-idf
-        SOURCE_DIR ${IDF_PATH}/tools/kconfig
-        CONFIGURE_COMMAND ""
-        BINARY_DIR "kconfig_bin"
-        BUILD_COMMAND make -f ${IDF_PATH}/tools/kconfig/Makefile mconf-idf
-        BUILD_BYPRODUCTS ${MCONF}
-        INSTALL_COMMAND ""
-        EXCLUDE_FROM_ALL 1
-        )
-
-    file(GLOB mconf_srcfiles ${IDF_PATH}/tools/kconfig/*.c)
-    externalproject_add_stepdependencies(mconf-idf build
-        ${mconf_srcfiles}
-        ${IDF_PATH}/tools/kconfig/Makefile
-        ${CMAKE_CURRENT_LIST_FILE})
-    unset(mconf_srcfiles)
 
-    set(menuconfig_depends DEPENDS mconf-idf)
+    idf_build_set_property(__MCONF ${MCONF})
+    idf_build_set_property(__MENUCONFIG_DEPENDS "${menuconfig_depends}")
 
-endif()
-
-# Find all Kconfig files for all components
-function(kconfig_process_config)
-    file(MAKE_DIRECTORY "${CONFIG_DIR}")
-    set(kconfigs)
-    set(kconfigs_projbuild)
-
-    # Components are usually sorted (somewhat) topologically via their dependencies. This extends to the component
-    # paths list. Obtain an alphabetical list in order to present menus also in the same order.
-    set(components ${BUILD_COMPONENTS})
-    list(SORT components)
+    idf_build_get_property(idf_path IDF_PATH)
+    idf_build_set_property(__ROOT_KCONFIG ${idf_path}/Kconfig)
+    idf_build_set_property(__OUTPUT_SDKCONFIG 1)
+endfunction()
 
-    foreach(component ${components})
-        list(FIND BUILD_COMPONENTS ${component} idx)
-        list(GET BUILD_COMPONENT_PATHS ${idx} component_path)
-        list(APPEND component_paths ${component_path})
-    endforeach()
+#
+# Initialize Kconfig-related properties for components.
+# This function assumes that all basic properties of the components have been
+# set prior to calling it.
+#
+function(__kconfig_component_init component_target)
+    __component_get_property(component_dir ${component_target} COMPONENT_DIR)
+    file(GLOB kconfig "${component_dir}/Kconfig")
+    __component_set_property(${component_target} KCONFIG "${kconfig}")
+    file(GLOB kconfig "${component_dir}/Kconfig.projbuild")
+    __component_set_property(${component_target} KCONFIG_PROJBUILD "${kconfig}")
+endfunction()
 
-    # Find Kconfig and Kconfig.projbuild for each component as applicable
-    # if any of these change, cmake should rerun
-    foreach(dir ${component_paths})
-        file(GLOB kconfig "${dir}/Kconfig")
+#
+# Generate the config files and create config related targets and configure
+# dependencies.
+#
+function(__kconfig_generate_config sdkconfig sdkconfig_defaults)
+    # List all Kconfig and Kconfig.projbuild in known components
+    idf_build_get_property(component_targets __COMPONENT_TARGETS)
+    foreach(component_target ${component_targets})
+        __component_get_property(kconfig ${component_target} KCONFIG)
+        __component_get_property(kconfig_projbuild ${component_target} KCONFIG_PROJBUILD)
         if(kconfig)
-            set(kconfigs "${kconfigs} ${kconfig}")
-            set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${kconfig})
+            list(APPEND kconfigs ${kconfig})
         endif()
-        file(GLOB kconfig ${dir}/Kconfig.projbuild)
-        if(kconfig)
-            set(kconfigs_projbuild "${kconfigs_projbuild} ${kconfig}")
-            set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${kconfig})
+        if(kconfig_projbuild)
+            list(APPEND kconfig_projbuilds ${kconfig_projbuild})
         endif()
     endforeach()
 
-    if(IDF_SDKCONFIG_DEFAULTS)
-        set(defaults_arg --defaults "${IDF_SDKCONFIG_DEFAULTS}")
+    # Store the list version of kconfigs and kconfig_projbuilds
+    idf_build_set_property(KCONFIGS "${kconfigs}")
+    idf_build_set_property(KCONFIG_PROJBUILDS "${kconfig_projbuilds}")
+
+    idf_build_get_property(idf_target IDF_TARGET)
+
+    if(sdkconfig_defaults)
+        set(defaults_arg --defaults "${sdkconfig_defaults}")
     endif()
 
-    if(EXISTS "${IDF_SDKCONFIG_DEFAULTS}.${IDF_TARGET}")
-        list(APPEND defaults_arg --defaults "${IDF_SDKCONFIG_DEFAULTS}.${IDF_TARGET}")
+    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)
-    set(COMPONENT_KCONFIGS_PROJBUILD "${kconfigs_projbuild}" PARENT_SCOPE)
+    idf_build_get_property(idf_path IDF_PATH)
+    idf_build_get_property(root_kconfig __ROOT_KCONFIG)
+    idf_build_get_property(python PYTHON)
+
+    string(REPLACE ";" " " kconfigs "${kconfigs}")
+    string(REPLACE ";" " " kconfig_projbuilds "${kconfig_projbuilds}")
 
     set(confgen_basecommand
-        ${PYTHON} ${IDF_PATH}/tools/kconfig_new/confgen.py
-        --kconfig ${ROOT_KCONFIG}
-        --config ${SDKCONFIG}
+        ${python} ${idf_path}/tools/kconfig_new/confgen.py
+        --kconfig ${root_kconfig}
+        --config ${sdkconfig}
         ${defaults_arg}
         --env "COMPONENT_KCONFIGS=${kconfigs}"
-        --env "COMPONENT_KCONFIGS_PROJBUILD=${kconfigs_projbuild}"
-        --env "IDF_CMAKE=y")
+        --env "COMPONENT_KCONFIGS_PROJBUILD=${kconfig_projbuilds}"
+        --env "IDF_CMAKE=y"
+        --env "IDF_TARGET=${idf_target}")
+
+    idf_build_get_property(build_dir BUILD_DIR)
+    set(config_dir ${build_dir}/config)
+    file(MAKE_DIRECTORY "${config_dir}")
+
+    # Generate the config outputs
+    set(sdkconfig_cmake ${config_dir}/sdkconfig.cmake)
+    set(sdkconfig_header ${config_dir}/sdkconfig.h)
+    set(sdkconfig_json ${config_dir}/sdkconfig.json)
+    set(sdkconfig_json_menus ${config_dir}/${kconfig_menus}.json)
+
+    idf_build_get_property(output_sdkconfig __OUTPUT_SDKCONFIG)
+    if(output_sdkconfig)
+        execute_process(
+            COMMAND ${confgen_basecommand}
+            --output header ${sdkconfig_header}
+            --output cmake ${sdkconfig_cmake}
+            --output json ${sdkconfig_json}
+            --output json_menus ${sdkconfig_json_menus}
+            --output config ${sdkconfig}
+            RESULT_VARIABLE config_result)
+    else()
+        execute_process(
+            COMMAND ${confgen_basecommand}
+            --output header ${sdkconfig_header}
+            --output cmake ${sdkconfig_cmake}
+            --output json ${sdkconfig_json}
+            --output json_menus ${sdkconfig_json_menus}
+            RESULT_VARIABLE config_result)
+    endif()
+
+    if(config_result)
+        message(FATAL_ERROR "Failed to run confgen.py (${confgen_basecommand}). Error ${config_result}")
+    endif()
+
+    # Add the generated config header to build specifications.
+    idf_build_set_property(INCLUDE_DIRECTORIES ${config_dir} APPEND)
+
+    # When sdkconfig file changes in the future, trigger a cmake run
+    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${sdkconfig}")
+
+    # Ditto if either of the generated files are missing/modified (this is a bit irritating as it means
+    # you can't edit these manually without them being regenerated, but I don't know of a better way...)
+    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${sdkconfig_header}")
+    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${sdkconfig_cmake}")
+
+    # Or if the config generation tool changes
+    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${idf_path}/tools/kconfig_new/confgen.py")
+    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${idf_path}/tools/kconfig_new/kconfiglib.py")
+
+    set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY
+                ADDITIONAL_MAKE_CLEAN_FILES "${sdkconfig_header}" "${sdkconfig_cmake}")
+
+    idf_build_set_property(SDKCONFIG_HEADER ${sdkconfig_header})
+    idf_build_set_property(SDKCONFIG_JSON ${sdkconfig_json})
+    idf_build_set_property(SDKCONFIG_CMAKE ${sdkconfig_cmake})
+    idf_build_set_property(SDKCONFIG_JSON_MENUS ${sdkconfig_json_menus})
+
+    idf_build_get_property(menuconfig_depends __MENUCONFIG_DEPENDS)
 
     # Generate the menuconfig target (uses C-based mconf-idf tool, either prebuilt or via mconf-idf target above)
     add_custom_target(menuconfig
         ${menuconfig_depends}
         # create any missing config file, with defaults if necessary
-        COMMAND ${confgen_basecommand} --env "IDF_TARGET=${IDF_TARGET}" --output config ${SDKCONFIG}
+        COMMAND ${confgen_basecommand} --env "IDF_TARGET=${idf_target}" --output config ${sdkconfig}
         COMMAND ${CMAKE_COMMAND} -E env
         "COMPONENT_KCONFIGS=${kconfigs}"
-        "COMPONENT_KCONFIGS_PROJBUILD=${kconfigs_projbuild}"
+        "COMPONENT_KCONFIGS_PROJBUILD=${kconfig_projbuilds}"
         "IDF_CMAKE=y"
-        "KCONFIG_CONFIG=${SDKCONFIG}"
-        "IDF_TARGET=${IDF_TARGET}"
-        ${MCONF} ${ROOT_KCONFIG}
+        "KCONFIG_CONFIG=${sdkconfig}"
+        ${MCONF} ${root_kconfig}
         VERBATIM
         USES_TERMINAL
         # additional run of confgen esures that the deprecated options will be inserted into sdkconfig (for backward
         # compatibility)
-        COMMAND ${confgen_basecommand} --env "IDF_TARGET=${IDF_TARGET}" --output config ${SDKCONFIG}
+        COMMAND ${confgen_basecommand} --env "IDF_TARGET=${idf_target}" --output config ${sdkconfig}
         )
 
     # Custom target to run confserver.py from the build tool
@@ -152,47 +214,4 @@ function(kconfig_process_config)
         ${PYTHON} ${IDF_PATH}/tools/kconfig_new/confserver.py --kconfig ${IDF_PATH}/Kconfig --config ${SDKCONFIG}
         VERBATIM
         USES_TERMINAL)
-
-    # Generate configuration output via confgen.py
-    # makes sdkconfig.h and skdconfig.cmake
-    #
-    # This happens during the cmake run not during the build
-    if(NOT BOOTLOADER_BUILD)
-        execute_process(
-            COMMAND ${confgen_basecommand}
-            --output header ${SDKCONFIG_HEADER}
-            --output cmake ${SDKCONFIG_CMAKE}
-            --output json ${SDKCONFIG_JSON}
-            --output json_menus ${KCONFIG_JSON_MENUS}
-            --output config ${SDKCONFIG} # only generate config at the top-level project
-            RESULT_VARIABLE config_result)
-    else()
-        execute_process(
-            COMMAND ${confgen_basecommand}
-            --output header ${SDKCONFIG_HEADER}
-            --output cmake ${SDKCONFIG_CMAKE}
-            --output json ${SDKCONFIG_JSON}
-            --output json_menus ${KCONFIG_JSON_MENUS}
-            RESULT_VARIABLE config_result)
-    endif()
-    if(config_result)
-        message(FATAL_ERROR "Failed to run confgen.py (${confgen_basecommand}). Error ${config_result}")
-    endif()
-
-    # When sdkconfig file changes in the future, trigger a cmake run
-    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${SDKCONFIG}")
-
-    # Ditto if either of the generated files are missing/modified (this is a bit irritating as it means
-    # you can't edit these manually without them being regenerated, but I don't know of a better way...)
-    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${SDKCONFIG_HEADER}")
-    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${SDKCONFIG_CMAKE}")
-
-    # Or if the config generation tool changes
-    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${IDF_PATH}/tools/kconfig_new/confgen.py")
-    set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${IDF_PATH}/tools/kconfig_new/kconfiglib.py")
-
-    set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY
-        ADDITIONAL_MAKE_CLEAN_FILES
-        "${SDKCONFIG_HEADER}" "${SDKCONFIG_CMAKE}")
-
 endfunction()
index 9688b601adcefdb04b66b2549c76d04b3b3fd437..9161ecd8f184cf1340235d5be8942241004fd45b 100644 (file)
@@ -1,81 +1,84 @@
 # Utilities for supporting linker script generation in the build system
 
-# ldgen_set_variables
-#
-# Create the custom target to attach the fragment files and template files
-# for the build to.
-function(ldgen_set_variables)
-    add_custom_target(ldgen)
-endfunction()
-
-# ldgen_add_fragment_files
+# __ldgen_add_fragment_files
 #
 # Add one or more linker fragment files, and append it to the list of fragment
 # files found so far.
-function(ldgen_add_fragment_files component fragment_files)
+function(__ldgen_add_fragment_files fragment_files)
     spaces2list(fragment_files)
 
     foreach(fragment_file ${fragment_files})
-        get_filename_component(_fragment_file ${fragment_file} ABSOLUTE)
-        list(APPEND _fragment_files ${_fragment_file})
+        get_filename_component(abs_path ${fragment_file} ABSOLUTE)
+        list(APPEND _fragment_files ${abs_path})
     endforeach()
 
-    set_property(TARGET ldgen APPEND PROPERTY FRAGMENT_FILES ${_fragment_files})
+    idf_build_set_property(__LDGEN_FRAGMENT_FILES "${_fragment_files}" APPEND)
 endfunction()
 
-# ldgen_component_add
+# __ldgen_add_component
 #
-# Add component to known libraries for linker script generation
-function(ldgen_component_add component_lib)
-    set_property(TARGET ldgen APPEND PROPERTY OUTPUT_LIBRARIES "$<TARGET_FILE:${component_lib}>")
-    set_property(TARGET ldgen APPEND PROPERTY LIBRARIES ${component_lib})
+# Generate sections info for specified target to be used in linker script generation
+function(__ldgen_add_component component_lib)
+    idf_build_set_property(__LDGEN_LIBRARIES "$<TARGET_FILE:${component_lib}>" APPEND)
+    idf_build_set_property(__LDGEN_DEPENDS ${component_lib} APPEND)
 endfunction()
 
-# ldgen_process_template
+# __ldgen_process_template
 #
 # Passes a linker script template to the linker script generation tool for
 # processing
-function(ldgen_process_template template output)
-    get_property(output_libraries TARGET ldgen PROPERTY OUTPUT_LIBRARIES)
-    file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/ldgen_libraries.in CONTENT "$<JOIN:${output_libraries},\n>")
-    file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/ldgen_libraries INPUT ${CMAKE_BINARY_DIR}/ldgen_libraries.in)
+function(__ldgen_process_template output_var template)
+    idf_build_get_property(idf_target IDF_TARGET)
+    idf_build_get_property(idf_path IDF_PATH)
 
-    get_filename_component(filename "${template}" NAME_WE)
+    idf_build_get_property(build_dir BUILD_DIR)
+    idf_build_get_property(ldgen_libraries __LDGEN_LIBRARIES GENERATOR_EXPRESSION)
+    file(GENERATE OUTPUT ${build_dir}/ldgen_libraries.in CONTENT $<JOIN:${ldgen_libraries},\n>)
+    file(GENERATE OUTPUT ${build_dir}/ldgen_libraries INPUT ${build_dir}/ldgen_libraries.in)
+
+    get_filename_component(filename "${template}" NAME)
+
+    set(output ${CMAKE_CURRENT_BINARY_DIR}/${filename}.ld)
+    set(${output_var} ${output} PARENT_SCOPE)
 
     set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
         APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES
-        "${CMAKE_BINARY_DIR}/ldgen_libraries.in"
-        "${CMAKE_BINARY_DIR}/ldgen_libraries")
+        "${build_dir}/ldgen_libraries.in"
+        "${build_dir}/ldgen_libraries")
+
+    idf_build_get_property(ldgen_fragment_files __LDGEN_FRAGMENT_FILES GENERATOR_EXPRESSION)
+    idf_build_get_property(ldgen_depends __LDGEN_DEPENDS GENERATOR_EXPRESSION)
+    # Create command to invoke the linker script generator tool.
+    idf_build_get_property(sdkconfig SDKCONFIG)
+    idf_build_get_property(root_kconfig __ROOT_KCONFIG)
+    idf_build_get_property(kconfigs KCONFIGS)
+    idf_build_get_property(kconfig_projbuilds KCONFIG_PROJBUILDS)
+
+    idf_build_get_property(python PYTHON)
+
+    string(REPLACE ";" " " kconfigs "${kconfigs}")
+    string(REPLACE ";" " " kconfig_projbuilds "${kconfig_projbuilds}")
 
     add_custom_command(
         OUTPUT ${output}
-        COMMAND ${PYTHON} ${IDF_PATH}/tools/ldgen/ldgen.py
-        --config    ${SDKCONFIG}
-        --fragments "$<JOIN:$<TARGET_PROPERTY:ldgen,FRAGMENT_FILES>,\t>"
+        COMMAND ${python} ${idf_path}/tools/ldgen/ldgen.py
+        --config    ${sdkconfig}
+        --fragments "$<JOIN:${ldgen_fragment_files},\t>"
         --input     ${template}
         --output    ${output}
-        --kconfig   ${ROOT_KCONFIG}
-        --env       "COMPONENT_KCONFIGS=${COMPONENT_KCONFIGS}"
-        --env       "COMPONENT_KCONFIGS_PROJBUILD=${COMPONENT_KCONFIGS_PROJBUILD}"
+        --kconfig   ${root_kconfig}
+        --env       "COMPONENT_KCONFIGS=${kconfigs}"
+        --env       "COMPONENT_KCONFIGS_PROJBUILD=${kconfig_projbuilds}"
         --env       "IDF_CMAKE=y"
-        --env       "IDF_PATH=${IDF_PATH}"
-        --env       "IDF_TARGET=${IDF_TARGET}"
-        --libraries-file ${CMAKE_BINARY_DIR}/ldgen_libraries
+        --env       "IDF_PATH=${idf_path}"
+        --env       "IDF_TARGET=${idf_target}"
+        --libraries-file ${build_dir}/ldgen_libraries
         --objdump   ${CMAKE_OBJDUMP}
-        DEPENDS     ${template} $<TARGET_PROPERTY:ldgen,FRAGMENT_FILES>
-                    $<TARGET_PROPERTY:ldgen,LIBRARIES> ${SDKCONFIG}
+        DEPENDS     ${template} ${ldgen_fragment_files} ${ldgen_depends} ${SDKCONFIG}
     )
 
-    get_filename_component(output_name ${output} NAME)
-    add_custom_target(ldgen_${output_name}_script DEPENDS ${output})
-    add_dependencies(ldgen ldgen_${output_name}_script)
-endfunction()
-
-# ldgen_add_dependencies
-#
-# Add dependency of project executable to ldgen custom target.
-function(ldgen_add_dependencies)
-    if(IDF_PROJECT_EXECUTABLE)
-        add_dependencies(${IDF_PROJECT_EXECUTABLE} ldgen)
-    endif()
+    get_filename_component(_name ${output} NAME)
+    add_custom_target(__ldgen_output_${_name} DEPENDS ${output})
+    add_dependencies(__idf_build_target __ldgen_output_${_name})
+    idf_build_set_property(LINK_DEPENDS ${output} APPEND)
 endfunction()
\ No newline at end of file
index 1bf088c3f864bea2cf1cd06b5133a522131999f6..437e715a480d9c4c7a6acb8a87761064ade79dd3 100644 (file)
 # Designed to be included from an IDF app's CMakeLists.txt file
-#
 cmake_minimum_required(VERSION 3.5)
 
-include(${CMAKE_CURRENT_LIST_DIR}/idf_functions.cmake)
+# The mere inclusion of this CMake file sets up some interal build properties.
+# These properties can be modified in between this inclusion the the idf_build_process
+# call.
+include(${CMAKE_CURRENT_LIST_DIR}/idf.cmake)
 
-# Set the path of idf.py.
 set(IDFTOOL ${PYTHON} "${IDF_PATH}/tools/idf.py")
+# Internally, the Python interpreter is already set to 'python'. Re-set here
+# to be absolutely sure.
+set_default(PYTHON "python")
+idf_build_set_property(PYTHON ${PYTHON})
 
-# Trick to temporarily redefine project(). When functions are overriden in CMake, the originals can still be accessed
-# using an underscore prefixed function of the same name. The following lines make sure that __project  calls
-# the original project(). See https://cmake.org/pipermail/cmake/2015-October/061751.html.
-function(project)
-endfunction()
+# On processing, checking Python required modules can be turned off if it was
+# already checked externally.
+if(PYTHON_DEPS_CHECKED)
+    idf_build_set_property(__CHECK_PYTHON 0)
+endif()
 
-function(_project)
+# Initialize build target for this build using the environment variable or
+# value passed externally.
+__target_init()
+
+#
+# Get the project version from either a version file or the Git revision. This is passed
+# to the idf_build_process call. Dependencies are also set here for when the version file
+# changes (if it is used).
+#
+function(__project_get_revision var)
+    set(_project_path "${CMAKE_CURRENT_LIST_DIR}")
+    if(NOT DEFINED PROJECT_VER)
+        if(EXISTS "${_project_path}/version.txt")
+            file(STRINGS "${_project_path}/version.txt" PROJECT_VER)
+            set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_project_path}/version.txt")
+        else()
+            git_describe(PROJECT_VER_GIT "${_project_path}")
+            if(PROJECT_VER_GIT)
+                set(PROJECT_VER ${PROJECT_VER_GIT})
+            else()
+                message(STATUS "Project is not inside a git repository, \
+                        will not use 'git describe' to determine PROJECT_VER.")
+                set(PROJECT_VER "1")
+            endif()
+        endif()
+    endif()
+    message(STATUS "Project version: ${PROJECT_VER}")
+    set(${var} "${PROJECT_VER}" PARENT_SCOPE)
 endfunction()
 
-macro(project name)
+#
+# Output the built components to the user. Generates files for invoking idf_monitor.py
+# that doubles as an overview of some of the more important build properties.
+#
+function(__project_info test_components)
+    idf_build_get_property(prefix __PREFIX)
+    idf_build_get_property(_build_components BUILD_COMPONENTS)
+    idf_build_get_property(build_dir BUILD_DIR)
+    idf_build_get_property(idf_path IDF_PATH)
+
+    list(SORT _build_components)
+
+    unset(build_components)
+    unset(build_component_paths)
+
+    foreach(build_component ${_build_components})
+        __component_get_target(component_target "${build_component}")
+        __component_get_property(_name ${component_target} COMPONENT_NAME)
+        __component_get_property(_prefix ${component_target} __PREFIX)
+        __component_get_property(_alias ${component_target} COMPONENT_ALIAS)
+        __component_get_property(_dir ${component_target} COMPONENT_DIR)
+
+        if(_alias IN_LIST test_components)
+            list(APPEND test_component_paths ${_dir})
+        else()
+            if(_prefix STREQUAL prefix)
+                set(component ${_name})
+            else()
+                set(component ${_alias})
+            endif()
+            list(APPEND build_components ${component})
+            list(APPEND build_component_paths ${_dir})
+        endif()
+    endforeach()
+
+    set(PROJECT_NAME ${CMAKE_PROJECT_NAME})
+    idf_build_get_property(PROJECT_PATH PROJECT_DIR)
+    idf_build_get_property(BUILD_DIR BUILD_DIR)
+    idf_build_get_property(SDKCONFIG SDKCONFIG)
+    idf_build_get_property(SDKCONFIG_DEFAULTS SDKCONFIG_DEFAULTS)
+    idf_build_get_property(PROJECT_EXECUTABLE EXECUTABLE)
+    set(PROJECT_BIN ${CMAKE_PROJECT_NAME}.bin)
+    idf_build_get_property(IDF_VER IDF_VER)
+
+    idf_build_get_property(sdkconfig_cmake SDKCONFIG_CMAKE)
+    include(${sdkconfig_cmake})
+    idf_build_get_property(COMPONENT_KCONFIGS KCONFIGS)
+    idf_build_get_property(COMPONENT_KCONFIGS_PROJBUILD KCONFIG_PROJBUILDS)
+
+    # Write project description JSON file
+    idf_build_get_property(build_dir BUILD_DIR)
+    make_json_list("${build_components};${test_components}" build_components_json)
+    make_json_list("${build_component_paths};${test_component_paths}" build_component_paths_json)
+    configure_file("${idf_path}/tools/cmake/project_description.json.in"
+        "${build_dir}/project_description.json")
+
+    # We now have the following component-related variables:
+    #
+    # build_components is the list of components to include in the build.
+    # build_component_paths is the paths to all of these components, obtained from the component dependencies file.
+    #
+    # Print the list of found components and test components
+    string(REPLACE ";" " " build_components "${build_components}")
+    string(REPLACE ";" " " build_component_paths "${build_component_paths}")
+    message(STATUS "Components: ${build_components}")
+    message(STATUS "Component paths: ${build_component_paths}")
+
+    if(test_components)
+        string(REPLACE ";" " " test_components "${test_components}")
+        string(REPLACE ";" " " test_component_paths "${test_component_paths}")
+        message(STATUS "Test components: ${test_components}")
+        message(STATUS "Test component paths: ${test_component_paths}")
+    endif()
+endfunction()
 
-    # Bridge existing documented variable names with library namespaced
-    # variables in order for old projects to work.
+function(__project_init components_var test_components_var)
+    # Use EXTRA_CFLAGS, EXTRA_CXXFLAGS and EXTRA_CPPFLAGS to add more priority options to the compiler
+    # EXTRA_CPPFLAGS is used for both C and C++
+    # Unlike environments' CFLAGS/CXXFLAGS/CPPFLAGS which work for both host and target build,
+    # these works only for target build
+    set(extra_cflags "$ENV{EXTRA_CFLAGS}")
+    set(extra_cxxflags "$ENV{EXTRA_CXXFLAGS}")
+    set(extra_cppflags "$ENV{EXTRA_CPPFLAGS}")
+
+    spaces2list(extra_cflags)
+    spaces2list(extra_cxxflags)
+    spaces2list(extra_cppflags)
+
+    idf_build_set_property(C_COMPILE_OPTIONS "${extra_cflags}" APPEND)
+    idf_build_set_property(CXX_COMPILE_OPTIONS "${extra_cxxflags}" APPEND)
+    idf_build_set_property(COMPILE_OPTIONS "${extra_cppflags}" APPEND)
+
+    function(__project_component_dir component_dir)
+        get_filename_component(component_dir "${component_dir}" ABSOLUTE)
+        if(EXISTS ${component_dir}/CMakeLists.txt)
+            idf_build_component(${component_dir})
+        else()
+            file(GLOB component_dirs ${component_dir}/*)
+            foreach(component_dir ${component_dirs})
+                if(EXISTS ${component_dir}/CMakeLists.txt)
+                    idf_build_component(${component_dir})
+                endif()
+            endforeach()
+        endif()
+    endfunction()
 
+    # Add component directories to the build, given the component filters, exclusions
+    # extra directories, etc. passed from the root CMakeLists.txt.
     if(COMPONENT_DIRS)
+        # User wants to fully override where components are pulled from.
         spaces2list(COMPONENT_DIRS)
-
+        idf_build_set_property(__COMPONENT_TARGETS "")
         foreach(component_dir ${COMPONENT_DIRS})
-            get_filename_component(component_dir ${component_dir} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
-            list(APPEND IDF_COMPONENT_DIRS "${component_dir}")
+            __project_component_dir(${component_dir})
         endforeach()
-    endif()
+    else()
+        # Look for components in the usual places: CMAKE_CURRENT_LIST_DIR/main,
+        # CMAKE_CURRENT_LIST_DIR/components, and the extra component dirs
+        if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/main")
+            __project_component_dir("${CMAKE_CURRENT_LIST_DIR}/main")
+        endif()
 
-    if(EXTRA_COMPONENT_DIRS)
-        spaces2list(EXTRA_COMPONENT_DIRS)
+        __project_component_dir("${CMAKE_CURRENT_LIST_DIR}/components")
 
+        spaces2list(EXTRA_COMPONENT_DIRS)
         foreach(component_dir ${EXTRA_COMPONENT_DIRS})
-            get_filename_component(component_dir ${component_dir} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR})
-            list(APPEND IDF_EXTRA_COMPONENT_DIRS "${component_dir}")
+            __project_component_dir("${component_dir}")
         endforeach()
     endif()
 
-    list(APPEND IDF_EXTRA_COMPONENT_DIRS "${CMAKE_SOURCE_DIR}/components")
-
-    if(NOT MAIN_SRCS)
-        list(APPEND IDF_EXTRA_COMPONENT_DIRS "${CMAKE_SOURCE_DIR}/main")
-    endif()
-
-    if(COMPONENTS)
-        set(IDF_COMPONENTS "${COMPONENTS}")
-    endif()
-
-    if(COMPONENT_REQUIRES_COMMON)
-        set(IDF_COMPONENT_REQUIRES_COMMON "${COMPONENT_REQUIRES_COMMON}")
-    endif()
-
-    if(EXCLUDE_COMPONENTS)
-        set(IDF_EXCLUDE_COMPONENTS "${COMPONENT_EXCLUDES}")
-    endif()
-
-    if(TESTS_ALL EQUAL 1 OR TEST_COMPONENTS)
-        set(IDF_BUILD_TESTS 1)
+    spaces2list(COMPONENTS)
+    spaces2list(EXCLUDE_COMPONENTS)
+    idf_build_get_property(component_targets __COMPONENT_TARGETS)
+    foreach(component_target ${component_targets})
+        __component_get_property(component_name ${component_target} COMPONENT_NAME)
+        set(include 1)
+        if(COMPONENTS AND NOT component_name IN_LIST COMPONENTS)
+            set(include 0)
+        endif()
+        if(EXCLUDE_COMPONENTS AND component_name IN_LIST EXCLUDE_COMPONENTS)
+            set(include 0)
+        endif()
+        if(include)
+            list(APPEND components ${component_name})
+        endif()
+    endforeach()
+
+    if(TESTS_ALL OR BUILD_TESTS OR TEST_COMPONENTS OR TEST_EXCLUDE_COMPONENTS)
+        spaces2list(TEST_COMPONENTS)
+        spaces2list(TEST_EXCLUDE_COMPONENTS)
+        idf_build_get_property(component_targets __COMPONENT_TARGETS)
+        foreach(component_target ${component_targets})
+            __component_get_property(component_dir ${component_target} COMPONENT_DIR)
+            __component_get_property(component_name ${component_target} COMPONENT_NAME)
+            if(component_name IN_LIST components)
+                set(include 1)
+                if(TEST_COMPONENTS AND NOT component_name IN_LIST TEST_COMPONENTS)
+                    set(include 0)
+                endif()
+                if(TEST_EXCLUDE_COMPONENTS AND component_name IN_LIST TEST_EXCLUDE_COMPONENTS)
+                    set(include 0)
+                endif()
+                if(include AND EXISTS ${component_dir}/test)
+                    __component_add(${component_dir}/test ${component_name})
+                    list(APPEND test_components ${component_name}::test)
+                endif()
+            endif()
+        endforeach()
     endif()
 
-    if(TEST_COMPONENTS)
-        set(IDF_TEST_COMPONENTS "${TEST_COMPONENTS}")
-    endif()
+    set(${components_var} "${components}" PARENT_SCOPE)
+    set(${test_components_var} "${test_components}" PARENT_SCOPE)
+endfunction()
 
-    if(TEST_EXCLUDE_COMPONENTS)
-        set(IDF_TEST_EXCLUDE_COMPONENTS "${TEST_EXCLUDE_COMPONENTS}")
-    endif()
+# Trick to temporarily redefine project(). When functions are overriden in CMake, the originals can still be accessed
+# using an underscore prefixed function of the same name. The following lines make sure that __project  calls
+# the original project(). See https://cmake.org/pipermail/cmake/2015-October/061751.html.
+function(project)
+endfunction()
 
-    if(NOT SDKCONFIG_DEFAULTS)
-        if(EXISTS ${CMAKE_SOURCE_DIR}/sdkconfig.defaults)
-            set(IDF_SDKCONFIG_DEFAULTS ${CMAKE_SOURCE_DIR}/sdkconfig.defaults)
-        endif()
-    else()
-        set(IDF_SDKCONFIG_DEFAULTS ${SDKCONFIG_DEFAULTS})
-    endif()
+function(_project)
+endfunction()
 
-    # Set build variables
-    idf_set_variables()
+macro(project project_name)
+    # Initialize project, preparing COMPONENTS argument for idf_build_process()
+    # call later using external COMPONENT_DIRS, COMPONENTS_DIRS, EXTRA_COMPONENTS_DIR,
+    # EXTRA_COMPONENTS_DIRS, COMPONENTS, EXLUDE_COMPONENTS, TEST_COMPONENTS,
+    # TEST_EXLUDE_COMPONENTS, TESTS_ALL, BUILD_TESTS
+    __project_init(components test_components)
 
-    # Now the configuration is loaded, set the toolchain appropriately
-    idf_set_toolchain()
+    __target_set_toolchain()
 
     # Enable ccache if it's on the path
     if(NOT CCACHE_DISABLE)
@@ -92,59 +249,153 @@ macro(project name)
         endif()
     endif()
 
-    __project(${name} C CXX ASM)
+    # The actual call to project()
+    __project(${project_name} C CXX ASM)
+
+    # Generate compile_commands.json (needs to come after project call).
+    set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
+
+    # Since components can import third-party libraries, the original definition of project() should be restored
+    # before the call to add components to the build.
+    function(project)
+        set(project_ARGV ARGV)
+        __project(${${project_ARGV}})
+    endfunction()
+
+    # Prepare the following arguments for the idf_build_process() call using external
+    # user values:
+    #
+    # SDKCONFIG_DEFAULTS is from external SDKCONFIG_DEFAULTS
+    # SDKCONFIG is from external SDKCONFIG
+    # BUILD_DIR is set to project binary dir
+    #
+    # PROJECT_NAME is taken from the passed name from project() call
+    # PROJECT_DIR is set to the current directory
+    # PROJECT_VER is from the version text or git revision of the current repo
+    if(SDKCONFIG_DEFAULTS)
+        get_filename_component(sdkconfig_defaults "${SDKCONFIG_DEFAULTS}" ABSOLUTE)
+        if(NOT EXISTS "${sdkconfig_defaults}")
+            message(FATAL_ERROR "SDKCONFIG_DEFAULTS '${sdkconfig_defaults}' does not exist.")
+        endif()
+    else()
+        if(EXISTS "${CMAKE_SOURCE_DIR}/sdkconfig.defaults")
+            set(sdkconfig_defaults "${CMAKE_SOURCE_DIR}/sdkconfig.defaults")
+        else()
+            set(sdkconfig_defaults "")
+        endif()
+    endif()
 
-    set(IDF_BUILD_ARTIFACTS ON)
-    set(IDF_PROJECT_EXECUTABLE ${CMAKE_PROJECT_NAME}.elf)
-    set(IDF_BUILD_ARTIFACTS_DIR ${CMAKE_BINARY_DIR})
+    if(SDKCONFIG)
+        get_filename_component(sdkconfig "${SDKCONFIG}" ABSOLUTE)
+        if(NOT EXISTS "${sdkconfig}")
+            message(FATAL_ERROR "SDKCONFIG '${sdkconfig}' does not exist.")
+        endif()
+        set(sdkconfig ${SDKCONFIG})
+    else()
+        set(sdkconfig "${CMAKE_CURRENT_LIST_DIR}/sdkconfig")
+    endif()
 
-    if(MAIN_SRCS)
-        spaces2list(MAIN_SRCS)
-        add_executable(${IDF_PROJECT_EXECUTABLE} ${MAIN_SRCS})
+    if(BUILD_DIR)
+        get_filename_component(build_dir "${BUILD_DIR}" ABSOLUTE)
+        if(NOT EXISTS "${build_dir}")
+            message(FATAL_ERROR "BUILD_DIR '${build_dir}' does not exist.")
+        endif()
     else()
-        # Create a dummy file to work around CMake requirement of having a source
-        # file while adding an executable
-        add_executable(${IDF_PROJECT_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/dummy_main_src.c")
-        add_custom_command(OUTPUT dummy_main_src.c
-            COMMAND ${CMAKE_COMMAND} -E touch dummy_main_src.c
-            WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-            VERBATIM)
+        set(build_dir ${CMAKE_BINARY_DIR})
+    endif()
 
-        add_custom_target(dummy_main_src DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/dummy_main_src.c)
+    __project_get_revision(project_ver)
+
+    message(STATUS "Building ESP-IDF components for target ${IDF_TARGET}")
+
+    idf_build_process(${IDF_TARGET}
+                    SDKCONFIG_DEFAULTS "${sdkconfig_defaults}"
+                    SDKCONFIG ${sdkconfig}
+                    BUILD_DIR ${build_dir}
+                    PROJECT_NAME ${CMAKE_PROJECT_NAME}
+                    PROJECT_DIR ${CMAKE_CURRENT_LIST_DIR}
+                    PROJECT_VER "${project_ver}"
+                    COMPONENTS "${components};${test_components}")
+
+    # Special treatment for 'main' component for standard projects (not part of core build system).
+    # Have it depend on every other component in the build. This is
+    # a convenience behavior for the standard project; thus is done outside of the core build system
+    # so that it treats components equally.
+    #
+    # This behavior should only be when user did not set REQUIRES/PRIV_REQUIRES manually.
+    idf_build_get_property(build_components BUILD_COMPONENTS)
+    if(idf::main IN_LIST build_components)
+        __component_get_target(main_target idf::main)
+        __component_get_property(reqs ${main_target} REQUIRES)
+        __component_get_property(priv_reqs ${main_target} PRIV_REQUIRES)
+        idf_build_get_property(common_reqs __COMPONENT_REQUIRES_COMMON)
+        if(reqs STREQUAL common_reqs AND NOT priv_reqs) #if user has not set any requirements
+            list(REMOVE_ITEM build_components idf::main)
+            __component_get_property(lib ${main_target} COMPONENT_LIB)
+            set_property(TARGET ${lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${build_components}")
+            get_property(type TARGET ${lib} PROPERTY TYPE)
+            if(type STREQUAL STATIC_LIBRARY)
+                set_property(TARGET ${lib} APPEND PROPERTY LINK_LIBRARIES "${build_components}")
+            endif()
+        endif()
+    endif()
 
-        add_dependencies(${IDF_PROJECT_EXECUTABLE} dummy_main_src)
+    set(project_elf ${CMAKE_PROJECT_NAME}.elf)
+
+    # Create a dummy file to work around CMake requirement of having a source
+    # file while adding an executable
+    set(project_elf_src ${CMAKE_BINARY_DIR}/project_elf_src.c)
+    add_custom_command(OUTPUT ${project_elf_src}
+        COMMAND ${CMAKE_COMMAND} -E touch ${project_elf_src}
+        VERBATIM)
+    add_custom_target(_project_elf_src DEPENDS "${project_elf_src}")
+    add_executable(${project_elf} "${project_elf_src}")
+    add_dependencies(${project_elf} _project_elf_src)
+
+    target_link_libraries(${project_elf} "-Wl,--gc-sections")
+    target_link_libraries(${project_elf} "-Wl,--start-group")
+
+    if(test_components)
+        target_link_libraries(${project_elf} "-Wl,--whole-archive")
+        foreach(test_component ${test_components})
+            if(TARGET ${test_component})
+                target_link_libraries(${project_elf} ${test_component})
+            endif()
+        endforeach()
+        target_link_libraries(${project_elf} "-Wl,--no-whole-archive")
     endif()
 
-    set(mapfile "${CMAKE_PROJECT_NAME}.map")
+    idf_build_get_property(build_components BUILD_COMPONENTS)
+    if(test_components)
+        list(REMOVE_ITEM build_components ${test_components})
+    endif()
+    target_link_libraries(${project_elf} ${build_components})
 
-    target_link_libraries(${IDF_PROJECT_EXECUTABLE} "-Wl,--cref -Wl,--Map=${mapfile}")
+    set(mapfile "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.map")
+    target_link_libraries(${project_elf} "-Wl,--cref -Wl,--Map=${mapfile}")
 
     set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY
         ADDITIONAL_MAKE_CLEAN_FILES
-        "${CMAKE_CURRENT_BINARY_DIR}/${mapfile}")
+        "${mapfile}" "${project_elf_src}")
+
+    idf_build_get_property(idf_path IDF_PATH)
+    idf_build_get_property(python PYTHON)
 
     # Add size targets, depend on map file, run idf_size.py
     add_custom_target(size
-        DEPENDS ${exe_target}
-        COMMAND ${PYTHON} ${IDF_PATH}/tools/idf_size.py ${mapfile}
+        DEPENDS ${project_elf}
+        COMMAND ${python} ${idf_path}/tools/idf_size.py ${mapfile}
         )
     add_custom_target(size-files
-        DEPENDS ${exe_target}
-        COMMAND ${PYTHON} ${IDF_PATH}/tools/idf_size.py --files ${mapfile}
+        DEPENDS ${project_elf}
+        COMMAND ${python} ${idf_path}/tools/idf_size.py --files ${mapfile}
         )
     add_custom_target(size-components
-        DEPENDS ${exe_target}
-        COMMAND ${PYTHON} ${IDF_PATH}/tools/idf_size.py --archives ${mapfile}
+        DEPENDS ${project_elf}
+        COMMAND ${python} ${idf_path}/tools/idf_size.py --archives ${mapfile}
         )
 
-    # Since components can import third-party libraries, the original definition of project() should be restored
-    # before the call to add components to the build.
-    function(project)
-        set(project_ARGV ARGV)
-        __project(${${project_ARGV}})
-    endfunction()
+    idf_build_executable(${project_elf})
 
-    # Finally, add the rest of the components to the build.
-    idf_import_components(components $ENV{IDF_PATH} esp-idf)
-    idf_link_components(${IDF_PROJECT_EXECUTABLE} "${components}")
+    __project_info("${test_components}")
 endmacro()
index 55c3fb97d0759b1a6a29ae4370d0bfe93ae40010..3d02e1632bd53c14246b6b6baed117a4d1bc66c1 100644 (file)
@@ -1,11 +1,11 @@
 {
-    "project_name":       "${IDF_PROJECT_NAME}",
-    "project_path":       "${IDF_PROJECT_PATH}",
-    "build_dir":          "${IDF_BUILD_ARTIFACTS_DIR}",
+    "project_name":       "${PROJECT_NAME}",
+    "project_path":       "${PROJECT_PATH}",
+    "build_dir":          "${BUILD_DIR}",
     "config_file":        "${SDKCONFIG}",
-    "config_defaults":    "${IDF_SDKCONFIG_DEFAULTS}",
-    "app_elf":            "${IDF_PROJECT_EXECUTABLE}",
-    "app_bin":            "${IDF_PROJECT_BIN}",
+    "config_defaults":    "${SDKCONFIG_DEFAULTS}",
+    "app_elf":            "${PROJECT_EXECUTABLE}",
+    "app_bin":            "${PROJECT_BIN}",
     "git_revision":       "${IDF_VER}",
     "phy_data_partition": "${CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION}",
     "monitor_baud" : "${CONFIG_MONITOR_BAUD}",
diff --git a/tools/cmake/scripts/component_get_requirements.cmake b/tools/cmake/scripts/component_get_requirements.cmake
new file mode 100644 (file)
index 0000000..8223f70
--- /dev/null
@@ -0,0 +1,49 @@
+include(${IDF_PATH}/tools/cmake/utilities.cmake)
+
+include("${BUILD_PROPERTIES_FILE}")
+include("${SDKCONFIG_CMAKE}")
+
+macro(require_idf_targets)
+endmacro()
+
+function(idf_build_get_property var property)
+    cmake_parse_arguments(_ "GENERATOR_EXPRESSION" "" "" ${ARGN})
+    if(__GENERATOR_EXPRESSION)
+        message(FATAL_ERROR "Getting build property generator expression not
+                supported before idf_component_register().")
+    endif()
+    set(${var} ${property} PARENT_SCOPE)
+endfunction()
+
+function(print_requires requires priv_requires)
+    spaces2list(requires)
+    spaces2list(priv_requires)
+    string(REPLACE ";" ":" requires "${requires}")
+    string(REPLACE ";" ":" priv_requires "${priv_requires}")
+    message("${requires}:::${priv_requires}")
+endfunction()
+
+macro(idf_component_register)
+    set(options)
+    set(single_value)
+    set(multi_value SRCS SRC_DIRS EXCLUDE_SRCS
+                    INCLUDE_DIRS PRIV_INCLUDE_DIRS LDFRAGMENTS REQUIRES
+                    PRIV_REQUIRES REQUIRED_IDF_TARGETS EMBED_FILES EMBED_TXTFILES)
+    cmake_parse_arguments(_ "${options}" "${single_value}" "${multi_value}" "${ARGN}")
+    print_requires("${__REQUIRES}" "${__PRIV_REQUIRES}")
+    set(__is_component 1)
+    return()
+endmacro()
+
+macro(register_component)
+    print_requires("${COMPONENT_REQUIRES}" "${COMPONENT_PRIV_REQUIRES}")
+    set(__is_component 1)
+    return()
+endmacro()
+
+macro(register_config_only_component)
+    register_component()
+endmacro()
+
+set(CMAKE_BUILD_EARLY_EXPANSION)
+include(${COMPONENT_DIR}/CMakeLists.txt OPTIONAL)
diff --git a/tools/cmake/scripts/expand_requirements.cmake b/tools/cmake/scripts/expand_requirements.cmake
deleted file mode 100644 (file)
index 2623485..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-# expand_requirements.cmake is a utility cmake script to expand component requirements early in the build,
-# before the components are ready to be included.
-#
-# Parameters:
-# - COMPONENTS = Space-separated list of initial components to include in the build.
-#   Can be empty, in which case all components are in the build.
-# - COMPONENT_REQUIRES_COMMON = Components to always include in the build, and treated as dependencies
-#   of all other components.
-# - DEPENDENCIES_FILE = Path of generated cmake file which will contain the expanded dependencies for these
-#   components.
-# - COMPONENT_DIRS = List of paths to search for all components.
-# - DEBUG = Set -DDEBUG=1 to debug component lists in the build.
-#
-# If successful, DEPENDENCIES_FILE can be expanded to set BUILD_COMPONENTS & BUILD_COMPONENT_PATHS with all
-# components required for the build, and the get_component_requirements() function to return each component's
-# recursively expanded requirements.
-#
-# BUILD_COMPONENTS & BUILD_COMPONENT_PATHS will be ordered in a best-effort way so that dependencies are listed first.
-# (Note that IDF supports cyclic dependencies, and dependencies in a cycle have ordering guarantees.)
-#
-# Determinism:
-#
-# Given the the same list of names in COMPONENTS (regardless of order), and an identical value of
-# COMPONENT_REQUIRES_COMMON, and all the same COMPONENT_REQUIRES & COMPONENT_PRIV_REQUIRES values in
-# each component, then the output of BUILD_COMPONENTS should always be in the same
-# order.
-#
-# BUILD_COMPONENT_PATHS will be in the same component order as BUILD_COMPONENTS, even if the
-# actual component paths are different due to different paths.
-#
-# 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")
-
-set(ESP_PLATFORM 1)
-
-if(NOT DEPENDENCIES_FILE)
-    message(FATAL_ERROR "DEPENDENCIES_FILE must be set.")
-endif()
-
-if(NOT COMPONENT_DIRS)
-    message(FATAL_ERROR "COMPONENT_DIRS variable must be set")
-endif()
-spaces2list(COMPONENT_DIRS)
-
-spaces2list(COMPONENT_REQUIRES_COMMON)
-
-# Dummy register_component used to save requirements variables as global properties, for later expansion
-#
-# (expand_component_requirements() includes the component CMakeLists.txt, which then sets its component variables,
-# calls this dummy macro, and immediately exits again.)
-macro(register_component)
-    if(COMPONENT STREQUAL main AND NOT COMPONENT_REQUIRES)
-        set(main_component_requires ${COMPONENTS})
-        list(REMOVE_ITEM main_component_requires "main")
-
-        set_property(GLOBAL PROPERTY "${COMPONENT}_REQUIRES" "${main_component_requires}")
-    else()
-        spaces2list(COMPONENT_REQUIRES)
-        set_property(GLOBAL PROPERTY "${COMPONENT}_REQUIRES" "${COMPONENT_REQUIRES}")
-    endif()
-
-    spaces2list(COMPONENT_PRIV_REQUIRES)
-    set_property(GLOBAL PROPERTY "${COMPONENT}_PRIV_REQUIRES" "${COMPONENT_PRIV_REQUIRES}")
-
-    # This is tricky: we override register_component() so it returns out of the component CMakeLists.txt
-    # (as we're declaring it as a macro not a function, so it doesn't have its own scope.)
-    #
-    # This means no targets are defined, and the component expansion ends early.
-    return()
-endmacro()
-
-macro(register_config_only_component)
-    register_component()
-endmacro()
-
-function(require_idf_targets)
-    if(NOT ${IDF_TARGET} IN_LIST ARGN)
-        message(FATAL_ERROR "Component ${COMPONENT_NAME} only supports targets: ${ARGN}")
-    endif()
-endfunction()
-
-# expand_component_requirements: Recursively expand a component's requirements,
-# setting global properties BUILD_COMPONENTS & BUILD_COMPONENT_PATHS and
-# also invoking the components to call register_component() above,
-# which will add per-component global properties with dependencies, etc.
-function(expand_component_requirements component)
-    get_property(seen_components GLOBAL PROPERTY SEEN_COMPONENTS)
-    if(component IN_LIST seen_components)
-        return()  # already added, or in process of adding, this component
-    endif()
-    set_property(GLOBAL APPEND PROPERTY SEEN_COMPONENTS ${component})
-
-    find_component_path("${component}" "${ALL_COMPONENTS}" "${ALL_COMPONENT_PATHS}" COMPONENT_PATH)
-    debug("Expanding dependencies of ${component} @ ${COMPONENT_PATH}")
-    if(NOT COMPONENT_PATH)
-        set_property(GLOBAL APPEND PROPERTY COMPONENTS_NOT_FOUND ${component})
-        return()
-    endif()
-
-    # include the component CMakeLists.txt to expand its properties
-    # into the global cache (via register_component(), above)
-    unset(COMPONENT_REQUIRES)
-    unset(COMPONENT_PRIV_REQUIRES)
-    set(COMPONENT ${component})
-    include(${COMPONENT_PATH}/CMakeLists.txt)
-
-    get_property(requires GLOBAL PROPERTY "${component}_REQUIRES")
-    get_property(requires_priv GLOBAL PROPERTY "${component}_PRIV_REQUIRES")
-
-    # Recurse dependencies first, so that they appear first in the list (when possible)
-    foreach(req ${COMPONENT_REQUIRES_COMMON} ${requires} ${requires_priv})
-        expand_component_requirements(${req})
-    endforeach()
-
-    list(FIND TEST_COMPONENTS ${component} idx)
-
-    if(NOT idx EQUAL -1)
-        list(GET TEST_COMPONENTS ${idx} test_component)
-        list(GET TEST_COMPONENT_PATHS ${idx} test_component_path)
-        set_property(GLOBAL APPEND PROPERTY BUILD_TEST_COMPONENTS ${test_component})
-        set_property(GLOBAL APPEND PROPERTY BUILD_TEST_COMPONENT_PATHS ${test_component_path})
-    endif()
-
-    # Now append this component to the full list (after its dependencies)
-    set_property(GLOBAL APPEND PROPERTY BUILD_COMPONENT_PATHS ${COMPONENT_PATH})
-    set_property(GLOBAL APPEND PROPERTY BUILD_COMPONENTS ${component})
-endfunction()
-
-# filter_components_list: Filter the components included in the build
-# as specified by the user. Or, in the case of unit testing, filter out
-# the test components to be built.
-macro(filter_components_list)
-    spaces2list(COMPONENTS)
-    spaces2list(EXCLUDE_COMPONENTS)
-    spaces2list(TEST_COMPONENTS)
-    spaces2list(TEST_EXCLUDE_COMPONENTS)
-
-    list(LENGTH ALL_COMPONENTS all_components_length)
-    math(EXPR all_components_length "${all_components_length} - 1")
-
-    foreach(component_idx RANGE 0 ${all_components_length})
-        list(GET ALL_COMPONENTS ${component_idx} component)
-        list(GET ALL_COMPONENT_PATHS ${component_idx} component_path)
-
-        if(COMPONENTS)
-            if(${component} IN_LIST COMPONENTS)
-                set(add_component 1)
-            else()
-                set(add_component 0)
-            endif()
-        else()
-            set(add_component 1)
-        endif()
-
-        if(NOT ${component} IN_LIST EXCLUDE_COMPONENTS AND add_component EQUAL 1)
-            list(APPEND components ${component})
-            list(APPEND component_paths ${component_path})
-
-            if(BUILD_TESTS EQUAL 1)
-
-                if(TEST_COMPONENTS)
-                    if(${component} IN_LIST TEST_COMPONENTS)
-                        set(add_test_component 1)
-                    else()
-                        set(add_test_component 0)
-                    endif()
-                else()
-                    set(add_test_component 1)
-                endif()
-
-                if(${component} IN_LIST ALL_TEST_COMPONENTS)
-                    if(NOT ${component} IN_LIST TEST_EXCLUDE_COMPONENTS AND add_test_component EQUAL 1)
-                        list(APPEND test_components ${component}_test)
-                        list(APPEND test_component_paths ${component_path}/test)
-
-                        list(APPEND components ${component}_test)
-                        list(APPEND component_paths ${component_path}/test)
-                    endif()
-                endif()
-            endif()
-        endif()
-    endforeach()
-
-    set(COMPONENTS ${components})
-
-    set(TEST_COMPONENTS ${test_components})
-    set(TEST_COMPONENT_PATHS ${test_component_paths})
-
-    list(APPEND ALL_COMPONENTS "${TEST_COMPONENTS}")
-    list(APPEND ALL_COMPONENT_PATHS "${TEST_COMPONENT_PATHS}")
-endmacro()
-
-# Main functionality goes here
-# Find every available component in COMPONENT_DIRS, save as ALL_COMPONENT_PATHS and ALL_COMPONENTS
-components_find_all("${COMPONENT_DIRS}" ALL_COMPONENT_PATHS ALL_COMPONENTS ALL_TEST_COMPONENTS)
-
-filter_components_list()
-
-debug("ALL_COMPONENT_PATHS ${ALL_COMPONENT_PATHS}")
-debug("ALL_COMPONENTS ${ALL_COMPONENTS}")
-debug("ALL_TEST_COMPONENTS ${ALL_TEST_COMPONENTS}")
-
-set_property(GLOBAL PROPERTY SEEN_COMPONENTS "")  # anti-infinite-recursion
-set_property(GLOBAL PROPERTY BUILD_COMPONENTS "")
-set_property(GLOBAL PROPERTY BUILD_COMPONENT_PATHS "")
-set_property(GLOBAL PROPERTY BUILD_TEST_COMPONENTS "")
-set_property(GLOBAL PROPERTY BUILD_TEST_COMPONENT_PATHS "")
-set_property(GLOBAL PROPERTY COMPONENTS_NOT_FOUND "")
-
-# Indicate that the component CMakeLists.txt is being included in the early expansion phase of the build,
-# and might not want to execute particular operations.
-set(CMAKE_BUILD_EARLY_EXPANSION 1)
-foreach(component ${COMPONENTS})
-    debug("Expanding initial component ${component}")
-    expand_component_requirements(${component})
-endforeach()
-unset(CMAKE_BUILD_EARLY_EXPANSION)
-
-get_property(build_components GLOBAL PROPERTY BUILD_COMPONENTS)
-get_property(build_component_paths GLOBAL PROPERTY BUILD_COMPONENT_PATHS)
-get_property(build_test_components GLOBAL PROPERTY BUILD_TEST_COMPONENTS)
-get_property(build_test_component_paths GLOBAL PROPERTY BUILD_TEST_COMPONENT_PATHS)
-get_property(not_found GLOBAL PROPERTY COMPONENTS_NOT_FOUND)
-
-debug("components in build: ${build_components}")
-debug("components in build: ${build_component_paths}")
-debug("components not found: ${not_found}")
-
-function(line contents)
-    file(APPEND "${DEPENDENCIES_FILE}.tmp" "${contents}\n")
-endfunction()
-
-file(WRITE "${DEPENDENCIES_FILE}.tmp" "# Component requirements generated by expand_requirements.cmake\n\n")
-line("set(BUILD_COMPONENTS ${build_components})")
-line("set(BUILD_COMPONENT_PATHS ${build_component_paths})")
-line("set(BUILD_TEST_COMPONENTS ${build_test_components})")
-line("set(BUILD_TEST_COMPONENT_PATHS ${build_test_component_paths})")
-line("")
-
-line("# get_component_requirements: Generated function to read the dependencies of a given component.")
-line("#")
-line("# Parameters:")
-line("# - component: Name of component")
-line("# - var_requires: output variable name. Set to recursively expanded COMPONENT_REQUIRES ")
-line("#   for this component.")
-line("# - var_private_requires: output variable name. Set to recursively expanded COMPONENT_PRIV_REQUIRES ")
-line("#   for this component.")
-line("#")
-line("# Throws a fatal error if 'componeont' is not found (indicates a build system problem).")
-line("#")
-line("function(get_component_requirements component var_requires var_private_requires)")
-foreach(build_component ${build_components})
-    get_property(reqs GLOBAL PROPERTY "${build_component}_REQUIRES")
-    get_property(private_reqs GLOBAL PROPERTY "${build_component}_PRIV_REQUIRES")
-    line("  if(\"\$\{component}\" STREQUAL \"${build_component}\")")
-    line("    set(\${var_requires} \"${reqs}\" PARENT_SCOPE)")
-    line("    set(\${var_private_requires} \"${private_reqs}\" PARENT_SCOPE)")
-    line("    return()")
-    line("  endif()")
-endforeach()
-
-line("  message(FATAL_ERROR \"Component not found: \${component}\")")
-line("endfunction()")
-
-# only replace DEPENDENCIES_FILE if it has changed (prevents ninja/make build loops.)
-execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${DEPENDENCIES_FILE}.tmp" "${DEPENDENCIES_FILE}")
-execute_process(COMMAND ${CMAKE_COMMAND} -E remove "${DEPENDENCIES_FILE}.tmp")
index 2092985a82dd20c652b17911308eb4f569576c34..57d3b7640fffa8f7fc63484a9bd77fba0eace9f1 100644 (file)
@@ -1,6 +1,7 @@
-include(component_utils)
-
-macro(idf_set_target)
+#
+# Set the target used for the standard project build.
+#
+macro(__target_init)
     # Input is IDF_TARGET environement variable
     set(env_idf_target $ENV{IDF_TARGET})
 
@@ -26,28 +27,38 @@ macro(idf_set_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})
+#
+# Check that the set build target and the config target matches.
+#
+function(__target_check)
+    # Should be called after sdkconfig CMake file has been included.
+    idf_build_get_property(idf_target IDF_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()
+endfunction()
 
-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)
+#
+# Used by the project CMake file to set the toolchain before project() call.
+#
+macro(__target_set_toolchain)
+    idf_build_get_property(idf_path IDF_PATH)
+    # First try to load the toolchain file from the tools/cmake/directory of IDF
+    set(toolchain_file_global ${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
-        components_find_all("${IDF_COMPONENT_DIRS}" ALL_COMPONENT_PATHS ALL_COMPONENTS ALL_TEST_COMPONENTS)
-        find_component_path(${IDF_TARGET} "${ALL_COMPONENTS}" "${ALL_COMPONENT_PATHS}" target_component_path)
-        set(toolchain_file_component ${target_component_path}/toolchain-${IDF_TARGET}.cmake)
+        __component_get_target(component_target ${IDF_TARGET})
+        if(NOT component_target)
+            message(FATAL_ERROR "Unable to resolve '${IDF_TARGET}' for setting toolchain file.")
+        endif()
+        get_property(component_dir TARGET ${component_target} PROPERTY COMPONENT_DIR)
+        # Try to load the toolchain file from the directory of IDF_TARGET component
+        set(toolchain_file_component ${component_dir}/toolchain-${IDF_TARGET}.cmake)
         if(EXISTS ${toolchain_file_component})
             set(CMAKE_TOOLCHAIN_FILE ${toolchain_file_component})
         else()
@@ -55,4 +66,4 @@ macro(idf_set_toolchain)
                     "checked ${toolchain_file_global} and ${toolchain_file_component}")
         endif()
     endif()
-endmacro()
+endmacro()
\ No newline at end of file
index 36cfbc7654fe3e61edc1075ee88b3ad56bbdea84..4e64c16201736194c417b8a18df388d4953e7188 100644 (file)
@@ -77,21 +77,23 @@ endfunction()
 # by converting it to a generated source file which is then compiled
 # to a binary object as part of the build
 function(target_add_binary_data target embed_file embed_type)
+    idf_build_get_property(build_dir BUILD_DIR)
+    idf_build_get_property(idf_path IDF_PATH)
 
     get_filename_component(embed_file "${embed_file}" ABSOLUTE)
 
     get_filename_component(name "${embed_file}" NAME)
-    set(embed_srcfile "${IDF_BUILD_ARTIFACTS_DIR}/${name}.S")
+    set(embed_srcfile "${build_dir}/${name}.S")
 
     add_custom_command(OUTPUT "${embed_srcfile}"
         COMMAND "${CMAKE_COMMAND}"
         -D "DATA_FILE=${embed_file}"
         -D "SOURCE_FILE=${embed_srcfile}"
         -D "FILE_TYPE=${embed_type}"
-        -P "${IDF_PATH}/tools/cmake/scripts/data_file_embed_asm.cmake"
+        -P "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake"
         MAIN_DEPENDENCY "${embed_file}"
-        DEPENDS "${IDF_PATH}/tools/cmake/scripts/data_file_embed_asm.cmake"
-        WORKING_DIRECTORY "${IDF_BUILD_ARTIFACTS_DIR}"
+        DEPENDS "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake"
+        WORKING_DIRECTORY "${build_dir}"
         VERBATIM)
 
     set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${embed_srcfile}")
@@ -128,25 +130,50 @@ endfunction()
 # Automatically adds a -L search path for the containing directory (if found),
 # and then adds -T with the filename only. This allows INCLUDE directives to be
 # used to include other linker scripts in the same directory.
-function(target_linker_script target)
-    foreach(scriptfile ${ARGN})
+function(target_linker_script target scriptfiles)
+    cmake_parse_arguments(_ "PROCESS" "" "" ${ARGN})
+    foreach(scriptfile ${scriptfiles})
         get_filename_component(abs_script "${scriptfile}" ABSOLUTE)
         message(STATUS "Adding linker script ${abs_script}")
 
+        if(__PROCESS)
+            __ldgen_process_template(output ${abs_script})
+            set(abs_script ${output})
+        endif()
+
         get_filename_component(search_dir "${abs_script}" DIRECTORY)
         get_filename_component(scriptname "${abs_script}" NAME)
 
-        get_target_property(link_libraries "${target}" LINK_LIBRARIES)
+        get_target_property(type ${target} TYPE)
+        if(type STREQUAL "INTERFACE_LIBRARY")
+            set(is_interface "INTERFACE")
+        endif()
+
+        if(is_interface)
+            get_target_property(link_libraries "${target}" INTERFACE_LINK_LIBRARIES)
+        else()
+            get_target_property(link_libraries "${target}" LINK_LIBRARIES)
+        endif()
+
         list(FIND "${link_libraries}" "-L ${search_dir}" found_search_dir)
         if(found_search_dir EQUAL "-1")  # not already added as a search path
-            target_link_libraries("${target}" "-L ${search_dir}")
+            target_link_libraries("${target}" "${is_interface}" "-L ${search_dir}")
         endif()
 
-        target_link_libraries("${target}" "-T ${scriptname}")
+        target_link_libraries("${target}" "${is_interface}" "-T ${scriptname}")
 
         # Note: In ESP-IDF, most targets are libraries and libary LINK_DEPENDS don't propagate to
-        # executable(s) the library is linked to. This is done manually in components.cmake.
-        set_property(TARGET "${target}" APPEND PROPERTY LINK_DEPENDS "${abs_script}")
+        # executable(s) the library is linked to. Attach manually to executable once it is known.
+        #
+        # Property INTERFACE_LINK_DEPENDS is available in CMake 3.13 which should propagate link
+        # dependencies.
+        if(NOT __PROCESS)
+            if(is_interface)
+                set_property(TARGET ${target} APPEND PROPERTY INTERFACE_LINK_DEPENDS ${abs_script})
+            else()
+                set_property(TARGET ${target} APPEND PROPERTY LINK_DEPENDS ${abs_script})
+            endif()
+        endif()
     endforeach()
 endfunction()
 
@@ -177,6 +204,7 @@ endfunction()
 # We cannot use CMAKE_CONFIGURE_DEPENDS instead because it only works for files which exist at CMake runtime.
 #
 function(fail_at_build_time target_name message_line0)
+    idf_build_get_property(idf_path IDF_PATH)
     set(message_lines COMMAND ${CMAKE_COMMAND} -E echo "${message_line0}")
     foreach(message_line ${ARGN})
         set(message_lines ${message_lines} COMMAND ${CMAKE_COMMAND} -E echo "${message_line}")
@@ -184,6 +212,44 @@ function(fail_at_build_time target_name message_line0)
     add_custom_target(${target_name} ALL
         ${message_lines}
         COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/CMakeCache.txt"
-        COMMAND ${CMAKE_COMMAND} -P ${IDF_PATH}/tools/cmake/scripts/fail.cmake
+        COMMAND ${CMAKE_COMMAND} -P ${idf_path}/tools/cmake/scripts/fail.cmake
         VERBATIM)
 endfunction()
+
+function(check_exclusive_args args prefix)
+    set(_args ${args})
+    spaces2list(_args)
+    set(only_arg 0)
+    foreach(arg ${_args})
+        if(${prefix}_${arg} AND only_arg)
+            message(FATAL_ERROR "${args} are exclusive arguments")
+        endif()
+
+        if(${prefix}_${arg})
+            set(only_arg 1)
+        endif()
+    endforeach()
+endfunction()
+
+
+# add_compile_options variant for C++ code only
+#
+# This adds global options, set target properties for
+# component-specific flags
+function(add_cxx_compile_options)
+    foreach(option ${ARGV})
+        # note: the Visual Studio Generator doesn't support this...
+        add_compile_options($<$<COMPILE_LANGUAGE:CXX>:${option}>)
+    endforeach()
+endfunction()
+
+# add_compile_options variant for C code only
+#
+# This adds global options, set target properties for
+# component-specific flags
+function(add_c_compile_options)
+    foreach(option ${ARGV})
+        # note: the Visual Studio Generator doesn't support this...
+        add_compile_options($<$<COMPILE_LANGUAGE:C>:${option}>)
+    endforeach()
+endfunction()
index 346b8352b442a19231690f32bf5def9be3789f34..5d49c85392b2b49eedaa7a87d8656f5fed19ed19 100755 (executable)
@@ -301,6 +301,8 @@ def write_cmake(deprecated_options, config, filename):
 #
 """)
 
+        configs_list = list()
+
         def write_node(node):
             sym = node.item
             if not isinstance(sym, kconfiglib.Symbol):
@@ -314,10 +316,15 @@ def write_cmake(deprecated_options, config, filename):
                     val = ""  # write unset values as empty variables
                 write("set({}{} \"{}\")\n".format(
                     prefix, sym.name, val))
+
+                configs_list.append(prefix + sym.name)
                 dep_opt = deprecated_options.get_deprecated_option(sym.name)
                 if dep_opt:
                     tmp_dep_list.append("set({}{} \"{}\")\n".format(prefix, dep_opt, val))
+                    configs_list.append(prefix + dep_opt)
+
         config.walk_menu(write_node)
+        write("set(CONFIGS_LIST {})".format(";".join(configs_list)))
 
         if len(tmp_dep_list) > 0:
             write('\n# List of deprecated options for backward compatibility\n')