mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
Create a phony target that intentionally fails the build, displaying the error messages specified in the message arguments. An empty CMake file is created and included, which is then deleted when the target is executed. This forces CMake to rerun, as the file is added to the CMake rerun dependencies. This has been carried over from cmakev1 as it is used by several components. Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
877 lines
28 KiB
CMake
877 lines
28 KiB
CMake
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
# Note: CMake does not support nested lists. The functions idf_die, idf_warn,
|
|
# idf_msg, and idf_dbg use ARGV# values because this is the only way to prevent
|
|
# arguments from being altered by CMake. ARGV and ARGN contain a flattened list
|
|
# of arguments, making it impossible to determine if any argument was
|
|
# originally a list.
|
|
#
|
|
# Using these functions has a side effect: the actual origin of the message
|
|
# appears as the first line of the backtrace.
|
|
|
|
#[[api
|
|
.. cmakev2:function:: idf_die
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_die(<msg>...)
|
|
|
|
:msg[in]: Message to print.
|
|
|
|
Print error ``<msg>`` and abort the build process. Multiple messages are
|
|
concatenated into a single message with no separator between them.
|
|
|
|
#]]
|
|
function(idf_die)
|
|
set(joined "")
|
|
math(EXPR last "${ARGC} - 1")
|
|
foreach(i RANGE 0 ${last})
|
|
string(APPEND joined "${ARGV${i}}")
|
|
endforeach()
|
|
message(FATAL_ERROR " IDF: ${joined}")
|
|
endfunction()
|
|
|
|
#[[api
|
|
.. cmakev2:function:: idf_warn
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_warn(<msg>...)
|
|
|
|
:msg[in]: Message to print.
|
|
|
|
Print warning ``<msg>``. Multiple messages are concatenated into a single
|
|
message with no separator between them.
|
|
#]]
|
|
function(idf_warn)
|
|
set(joined "")
|
|
math(EXPR last "${ARGC} - 1")
|
|
foreach(i RANGE 0 ${last})
|
|
string(APPEND joined "${ARGV${i}}")
|
|
endforeach()
|
|
message(WARNING " IDF: ${joined}")
|
|
endfunction()
|
|
|
|
#[[api
|
|
.. cmakev2:function:: idf_msg
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_msg(<msg>...)
|
|
|
|
:msg[in]: Message to print.
|
|
|
|
Print status ``<msg>``. Multiple messages are concatenated into a single
|
|
message with no separator between them.
|
|
#]]
|
|
function(idf_msg)
|
|
set(joined "")
|
|
math(EXPR last "${ARGC} - 1")
|
|
foreach(i RANGE 0 ${last})
|
|
string(APPEND joined "${ARGV${i}}")
|
|
endforeach()
|
|
message(STATUS " IDF: ${joined}")
|
|
endfunction()
|
|
|
|
#[[api
|
|
.. cmakev2:function:: idf_dbg
|
|
|
|
.. code-block:: cmake
|
|
|
|
idf_dbg(<msg>...)
|
|
|
|
:msg[in]: Message to print.
|
|
|
|
Print debug ``<msg>``. Multiple messages are concatenated into a single
|
|
message with no separator between them.
|
|
#]]
|
|
function(idf_dbg)
|
|
set(joined "")
|
|
math(EXPR last "${ARGC} - 1")
|
|
foreach(i RANGE 0 ${last})
|
|
string(APPEND joined "${ARGV${i}}")
|
|
endforeach()
|
|
message(DEBUG " IDF: ${joined}")
|
|
endfunction()
|
|
|
|
#[[
|
|
__get_real_target(TARGET <target>
|
|
OUTPUT <variable>)
|
|
|
|
:TARGET[int]: Target name or target alias name.
|
|
:OUTPUT[out]: Output variable to store the real target name.
|
|
|
|
For a given ``<target>``, return the actual target if ``<target>`` is an
|
|
alias. If ``<target>`` is the target itself, return it. If ``<target>`` is
|
|
not a target at all, return ``NOTFOUND``.
|
|
#]]
|
|
function(__get_real_target)
|
|
set(options)
|
|
set(one_value TARGET OUTPUT)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_TARGET)
|
|
idf_die("TARGET option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
|
|
set(real_target NOTFOUND)
|
|
if(TARGET "${ARG_TARGET}")
|
|
get_target_property(aliased ${ARG_TARGET} ALIASED_TARGET)
|
|
if(aliased)
|
|
set(real_target ${aliased})
|
|
else()
|
|
set(real_target ${ARG_TARGET})
|
|
endif()
|
|
endif()
|
|
set(${ARG_OUTPUT} "${real_target}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
__get_absolute_paths(PATHS <path>...
|
|
BASE_DIR <base_dir>
|
|
OUTPUT <variable>)
|
|
|
|
:PATHS[in]: List of paths to convert to absolute paths.
|
|
:BASE_DIR[in,opt]: Evaluate relative paths based on the specified
|
|
``<base_dir>``. If not provided, use CMAKE_CURRENT_SOURCE_DIR
|
|
instead.
|
|
:OUTPUT[out]: Output variable to store absolute paths.
|
|
|
|
For a given ``PATHS``, return the absolute paths in ``OUTPUT``.
|
|
#]]
|
|
function(__get_absolute_paths)
|
|
set(options)
|
|
set(one_value BASE_DIR OUTPUT)
|
|
set(multi_value PATHS)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_PATHS)
|
|
set(ARG_PATHS "")
|
|
endif()
|
|
|
|
set(absolute_paths "")
|
|
foreach(path IN LISTS ARG_PATHS)
|
|
if(DEFINED ARG_BASE_DIR)
|
|
get_filename_component(path_abs ${path} ABSOLUTE BASE_DIR "${ARG_BASE_DIR}")
|
|
else()
|
|
get_filename_component(path_abs ${path} ABSOLUTE)
|
|
endif()
|
|
list(APPEND absolute_paths "${path_abs}")
|
|
endforeach()
|
|
|
|
set(${ARG_OUTPUT} "${absolute_paths}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
__get_default_value(VARIABLE <variable>
|
|
DEFAULT <value>)
|
|
OUTPUT <result>)
|
|
|
|
:VARIABLE[in]: The name of the variable for which to obtain its default
|
|
value.
|
|
:DEFAULT[in]: Default variable value.
|
|
:OUTPUT[out]: Variable to store the default value of ``<variable>``.
|
|
|
|
Set the ``<result>`` to the value of ``<variable>`` based on the following
|
|
order of precedence, with the first having the highest precedence.
|
|
|
|
1. environmental variable
|
|
2. CMake variable or value stored in CMake's cache
|
|
3. ``<value>`` as specified with the ``DEFAULT`` option
|
|
#]]
|
|
function(__get_default_value)
|
|
set(options)
|
|
set(one_value VARIABLE DEFAULT OUTPUT)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_VARIABLE)
|
|
idf_die("VARIABLE option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_DEFAULT)
|
|
set(ARG_DEFAULT "")
|
|
endif()
|
|
|
|
|
|
if(DEFINED ENV{${ARG_VARIABLE}})
|
|
set(value "$ENV{${ARG_VARIABLE}}")
|
|
elseif(DEFINED ${ARG_VARIABLE})
|
|
set(value "${${ARG_VARIABLE}}")
|
|
else()
|
|
set(value "${ARG_DEFAULT}")
|
|
endif()
|
|
|
|
set(${ARG_OUTPUT} "${value}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
__get_config_option(OPTION <option>
|
|
SDKCONFIG <path>)
|
|
OUTPUT <variable>)
|
|
|
|
:OPTION[in]: Option name in sdkconfig to obtain the value of.
|
|
:SDKCONFIG[in]: Sdkconfig file path.
|
|
:OUTPUT[out]: Variable to store the value of sdkconfig ``<option>``.
|
|
|
|
Search for the ``<option>`` in the sdkconfig file specified by ``<path>`` and store
|
|
its value in ``<variable>``. If the option is not found, return ``NOTFOUND``.
|
|
#]]
|
|
function(__get_sdkconfig_option)
|
|
set(options)
|
|
set(one_value OPTION SDKCONFIG OUTPUT)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_OPTION)
|
|
idf_die("OPTION option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_SDKCONFIG)
|
|
idf_die("SDKCONFIG option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
|
|
set(value NOTFOUND)
|
|
|
|
if(NOT EXISTS "${ARG_SDKCONFIG}")
|
|
set(${ARG_OUTPUT} "${value}" PARENT_SCOPE)
|
|
return()
|
|
endif()
|
|
|
|
file(STRINGS "${ARG_SDKCONFIG}" lines)
|
|
foreach(line ${lines})
|
|
if(NOT "${line}" MATCHES "^${ARG_OPTION}=\"[^\"]+\"$")
|
|
continue()
|
|
endif()
|
|
|
|
string(REGEX REPLACE "${ARG_OPTION}=\"([^\"]+)\"" "\\1" value "${line}")
|
|
break()
|
|
endforeach()
|
|
|
|
set(${ARG_OUTPUT} "${value}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
__set_property(TARGET <target>
|
|
PROPERTY <property>)
|
|
PROPERTIES <properties>
|
|
VALUE <value>
|
|
APPEND)
|
|
|
|
:TARGET[in]: The target name to attach the property.
|
|
:PROPERTY[in]: Property name.
|
|
:PROPERTIES[in]: The target property containing list of properties.
|
|
:VALUE[in]: Property value.
|
|
:APPEND: Append the value to the property's current value instead of
|
|
replacing it.
|
|
|
|
Set the ``PROPERTY`` to ``VALUE`` for the ``TARGET``, and also record the
|
|
``PROPERTY`` name in the list of all properties for the ``TARGET``. The
|
|
property name where all ``TARGET`` property names are stored is specified
|
|
by the ``PROPERTIES`` option. If ``APPEND`` is specified, the property will
|
|
be added rather than set.
|
|
|
|
Note: The cmake_parse_arguments function does not preserve empty arguments,
|
|
as explained in CMP0174. We do not verify if VALUE is defined because it
|
|
might have been passed as an empty string or list.
|
|
|
|
Note: The VALUE is treated as a multi-value because if a list is provided
|
|
as the value, CMake assigns only the first entry to the VALUE if parsed as
|
|
one-value. The remaining entries are stored in <prefix>_UNPARSED_ARGUMENTS.
|
|
#]]
|
|
function(__set_property)
|
|
set(options APPEND)
|
|
set(one_value TARGET PROPERTY PROPERTIES)
|
|
set(multi_value VALUE)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_TARGET)
|
|
idf_die("TARGET option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_PROPERTY)
|
|
idf_die("PROPERTY option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_PROPERTIES)
|
|
idf_die("PROPERTIES option is required")
|
|
endif()
|
|
|
|
get_property(properties TARGET ${ARG_TARGET} PROPERTY ${ARG_PROPERTIES})
|
|
if(NOT ARG_PROPERTY IN_LIST properties)
|
|
list(APPEND properties "${ARG_PROPERTY}")
|
|
set_property(TARGET ${ARG_TARGET} PROPERTY ${ARG_PROPERTIES} "${properties}")
|
|
endif()
|
|
if(ARG_APPEND)
|
|
set_property(TARGET ${ARG_TARGET} APPEND PROPERTY ${ARG_PROPERTY} "${ARG_VALUE}")
|
|
else()
|
|
set_property(TARGET ${ARG_TARGET} PROPERTY ${ARG_PROPERTY} "${ARG_VALUE}")
|
|
endif()
|
|
endfunction()
|
|
|
|
#[[
|
|
__get_property(TARGET <target>
|
|
PROPERTY <property>)
|
|
OUTPUT <variable>
|
|
GENERATOR_EXPRESSION)
|
|
|
|
:TARGET[in]: The target from which to obtain the property value.
|
|
:PROPERTY[in]: Property name.
|
|
:OUTPUT[out]: Output variable to store the property value.
|
|
:GENERATOR_EXPRESSION: Obtain the generator expression for the property
|
|
rather than the actual value.
|
|
|
|
Get the value of the specified ``PROPERTY`` from ``TARGET`` and store it in
|
|
the ``OUTPUT`` variable.
|
|
#]]
|
|
function(__get_property)
|
|
set(options GENERATOR_EXPRESSION)
|
|
set(one_value TARGET PROPERTY OUTPUT)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_TARGET)
|
|
idf_die("TARGET option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_PROPERTY)
|
|
idf_die("PROPERTY option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("PROPERTIES option is required")
|
|
endif()
|
|
|
|
if(ARG_GENERATOR_EXPRESSION)
|
|
set(value "$<TARGET_PROPERTY:${ARG_TARGET},${ARG_PROPERTY}>")
|
|
else()
|
|
get_property(value TARGET ${ARG_TARGET} PROPERTY ${ARG_PROPERTY})
|
|
endif()
|
|
|
|
set(${ARG_OUTPUT} ${value} PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
__dump_all_properties()
|
|
|
|
Dump all properties, including build properties, component properties, and
|
|
library properties.
|
|
#]]
|
|
function(__dump_all_properties)
|
|
__dump_build_properties()
|
|
idf_build_get_property(component_names COMPONENTS_DISCOVERED)
|
|
__dump_component_properties("${component_names}")
|
|
idf_build_get_property(library_interfaces LIBRARY_INTERFACES)
|
|
__dump_library_properties("${library_interfaces}")
|
|
endfunction()
|
|
|
|
#[[
|
|
__split(STRING <string>
|
|
OUTPUT <variable>
|
|
[SEPARATOR <sep>]
|
|
[REMOVE_EMPTY]
|
|
[STRIP])
|
|
|
|
:STRING[in]: Input string to be split.
|
|
:OUTPUT[out]: Output variable where the split strings will be stored.
|
|
:SEPARATOR[in,opt]: The separator string used to split the string.
|
|
By default split the string using newlines:
|
|
"\r\n", "\n", and "\r".
|
|
:REMOVE_EMPTY[opt]: Remove empty split strings.
|
|
:STRIP[opt]: Remove leading and trailing spaces from split strings.
|
|
|
|
Split the ``STRING`` using the ``SEPARATOR`` and store the resulting list of
|
|
split strings in ``OUTPUT``. If ``SEPARATOR`` is not provided, the split
|
|
will be based on newlines. When ``STRIP`` is specified, leading and trailing
|
|
whitespaces will be removed. If ``REMOVE_EMPTY`` is specified, empty strings
|
|
will be removed from the ``OUTPUT`` list.
|
|
#]]
|
|
function(__split)
|
|
set(options STRIP REMOVE_EMPTY)
|
|
set(one_value STRING SEPARATOR OUTPUT)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_STRING)
|
|
idf_die("STRING option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_SEPARATOR)
|
|
string(REPLACE "\r\n" "\n" ARG_STRING "${ARG_STRING}")
|
|
string(REPLACE "\r" "\n" ARG_STRING "${ARG_STRING}")
|
|
set(ARG_SEPARATOR "\n")
|
|
endif()
|
|
|
|
string(REPLACE "${ARG_SEPARATOR}" ";" lines "${ARG_STRING}")
|
|
|
|
if(NOT DEFINED ARG_STRIP AND NOT DEFINED ARG_REMOVE_EMPTY)
|
|
set(${ARG_OUTPUT} "${lines}" PARENT_SCOPE)
|
|
return()
|
|
endif()
|
|
|
|
set(filtered_lines "")
|
|
foreach(line IN LISTS lines)
|
|
if(ARG_STRIP)
|
|
string(STRIP "${line}" line)
|
|
endif()
|
|
if(ARG_REMOVE_EMPTY AND "${line}" STREQUAL "")
|
|
continue()
|
|
endif()
|
|
list(APPEND filtered_lines "${line}")
|
|
endforeach()
|
|
set(${ARG_OUTPUT} "${filtered_lines}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
__get_compile_options(OUTPUT <variable>)
|
|
|
|
:OUTPUT[out]: List of generator expressions for C, CXX, and ASM compile
|
|
options
|
|
|
|
Gather the compilation options from COMPILE_OPTIONS, C_COMPILE_OPTIONS,
|
|
CXX_COMPILE_OPTIONS, and ASM_COMPILE_OPTIONS build properties into a single
|
|
list using generator expressions. This list can then be used with the
|
|
target_compile_options call.
|
|
#]]
|
|
function(__get_compile_options)
|
|
set(options)
|
|
set(one_value OUTPUT)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
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(asm_compile_options ASM_COMPILE_OPTIONS GENERATOR_EXPRESSION)
|
|
|
|
foreach(option IN LISTS c_compile_options)
|
|
list(APPEND compile_options $<$<COMPILE_LANGUAGE:C>:${option}>)
|
|
endforeach()
|
|
foreach(option IN LISTS cxx_compile_options)
|
|
list(APPEND compile_options $<$<COMPILE_LANGUAGE:CXX>:${option}>)
|
|
endforeach()
|
|
foreach(option IN LISTS asm_compile_options)
|
|
list(APPEND compile_options $<$<COMPILE_LANGUAGE:ASM>:${option}>)
|
|
endforeach()
|
|
set(${ARG_OUTPUT} "${compile_options}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
remove_duplicated_flags(<flags> <uniq_flags>)
|
|
|
|
:flags[in]: Input string with compilation flags.
|
|
:uniq_flags[out]: Output string with unified compilation flags.
|
|
|
|
Remove duplicate entries from a string of compilation flags.
|
|
#]]
|
|
function(remove_duplicated_flags FLAGS UNIQFLAGS)
|
|
set(FLAGS_LIST "${FLAGS}")
|
|
# Convert the given flags, as a string, into a CMake list type
|
|
separate_arguments(FLAGS_LIST)
|
|
# Remove all the duplicated flags
|
|
list(REMOVE_DUPLICATES FLAGS_LIST)
|
|
# Convert the list back to a string
|
|
string(REPLACE ";" " " FLAGS_LIST "${FLAGS_LIST}")
|
|
# Return that string to the caller
|
|
set(${UNIQFLAGS} "${FLAGS_LIST}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
spaces2list(<variable>)
|
|
|
|
:variable[in,out]: String with space-delimited values.
|
|
|
|
Take a variable containing space-delimited values and convert it into a
|
|
CMake list with semicolon-delimited values.
|
|
|
|
Note: do not use this for directories or full paths, as they may contain
|
|
spaces.
|
|
|
|
TODO: look at cmake separate_arguments, which is quote-aware
|
|
#]]
|
|
function(spaces2list variable_name)
|
|
string(REPLACE " " ";" tmp "${${variable_name}}")
|
|
set("${variable_name}" "${tmp}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
add_prefix(<variable> <prefix> <item>...)
|
|
|
|
:variable[out]: Output variable with list of prefixed items.
|
|
:prefix[in]: Prefix string.
|
|
:item[in]: Items to be prefixed.
|
|
|
|
Add a prefix to each item in the given list.
|
|
#]]
|
|
function(add_prefix var prefix)
|
|
foreach(elm ${ARGN})
|
|
list(APPEND newlist "${prefix}${elm}")
|
|
endforeach()
|
|
set(${var} "${newlist}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
fail_target(<target> <line0> [<line>...])
|
|
|
|
:target[in]: Fail target name.
|
|
:line0[in]: First line of the failed message.
|
|
:line[in,opt]: Optional additional message lines.
|
|
|
|
Create a phony target which fails when invoked. This is used when the
|
|
necessary conditions for a target are not met, such as configuration. Rather
|
|
than omitting the target altogether, we fail execution with a helpful
|
|
message.
|
|
#]]
|
|
function(fail_target 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}")
|
|
endforeach()
|
|
# Generate a timestamp file that gets included. When deleted on build, this forces CMake
|
|
# to rerun.
|
|
set(fail_message "Failed executing target (see errors on lines above)")
|
|
add_custom_target(${target_name}
|
|
${message_lines}
|
|
COMMAND ${CMAKE_COMMAND} -E env FAIL_MESSAGE=${fail_message}
|
|
${CMAKE_COMMAND} -P ${idf_path}/tools/cmake/scripts/fail.cmake
|
|
VERBATIM)
|
|
endfunction()
|
|
|
|
#[[
|
|
file_generate(<output>
|
|
[INPUT <file>]
|
|
[CONTENT <string>])
|
|
|
|
:output[in]: Output file name.
|
|
:INPUT[in]: Input file name.
|
|
:CONTENT[in]: Input string.
|
|
|
|
Utility to generate file and have the output automatically added to cleaned
|
|
files.
|
|
#]]
|
|
function(file_generate output)
|
|
cmake_parse_arguments(_ "" "INPUT;CONTENT" "" ${ARGN})
|
|
|
|
if(__INPUT)
|
|
file(GENERATE OUTPUT "${output}" INPUT "${__INPUT}")
|
|
elseif(__CONTENT)
|
|
file(GENERATE OUTPUT "${output}" CONTENT "${__CONTENT}")
|
|
else()
|
|
message(FATAL_ERROR "Content to generate not specified.")
|
|
endif()
|
|
|
|
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
|
APPEND PROPERTY ADDITIONAL_CLEAN_FILES "${output}")
|
|
endfunction()
|
|
|
|
#[[
|
|
add_deprecated_target_alias(<old_target> <new_target>)
|
|
|
|
:old_target[in]: Old target name.
|
|
:new_target[in]: New target name.
|
|
|
|
Create an alias for the existing target and display a deprecation warning.
|
|
#]]
|
|
function(add_deprecated_target_alias old_target new_target)
|
|
add_custom_target(${old_target}
|
|
# `COMMAND` is important to print the `COMMENT` message at the end of the target action.
|
|
COMMAND ${CMAKE_COMMAND} -E echo ""
|
|
COMMENT "Warning: command \"${old_target}\" is deprecated. Have you wanted to run \"${new_target}\" instead?"
|
|
)
|
|
add_dependencies(${old_target} ${new_target})
|
|
endfunction()
|
|
|
|
#[[
|
|
target_add_binary_data(<target> <embed_file> <embed_type>
|
|
[RENAME_TO <symbol>])
|
|
[DEPENDS <dependency>...])
|
|
|
|
:target[in]: Attach the source file with embedded data to the specified
|
|
target.
|
|
:embed_file[in]: File containing the embedded data.
|
|
:embed_type[in]: BINARY or TEXT
|
|
:RENAME_TO[in,opt]: Use the given symbol name for the embedded data. If no
|
|
symbol name is provided, the embed_file file name will
|
|
be used instead.
|
|
:DEPENDS[in,opt]: List of additional dependencies for the generated file
|
|
containing embedded data.
|
|
|
|
Add binary data into the build target by converting it into a generated
|
|
source file, which is then compiled into a binary object as part of the
|
|
build process.
|
|
#]]
|
|
function(target_add_binary_data target embed_file embed_type)
|
|
cmake_parse_arguments(_ "" "RENAME_TO" "DEPENDS" ${ARGN})
|
|
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 "${build_dir}/${name}.S")
|
|
|
|
set(rename_to_arg)
|
|
if(__RENAME_TO) # use a predefined variable name
|
|
set(rename_to_arg -D "VARIABLE_BASENAME=${__RENAME_TO}")
|
|
endif()
|
|
|
|
add_custom_command(OUTPUT "${embed_srcfile}"
|
|
COMMAND "${CMAKE_COMMAND}"
|
|
-D "DATA_FILE=${embed_file}"
|
|
-D "SOURCE_FILE=${embed_srcfile}"
|
|
${rename_to_arg}
|
|
-D "FILE_TYPE=${embed_type}"
|
|
-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" ${__DEPENDS}
|
|
WORKING_DIRECTORY "${build_dir}"
|
|
VERBATIM)
|
|
|
|
set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_CLEAN_FILES "${embed_srcfile}")
|
|
|
|
target_sources("${target}" PRIVATE "${embed_srcfile}")
|
|
endfunction()
|
|
|
|
#[[
|
|
add_prebuilt_library(<target> <lib>
|
|
[REQUIRES <component>...])
|
|
[PRIV_REQUIRES <component>...])
|
|
|
|
:target[in]: Target name for the imported library.
|
|
:library[in]: Imported library path.
|
|
:REQUIRES[in,opt]: Optional dependency on other components.
|
|
:PRIV_REQUIRES[in,opt]: Optional private dependency on other components.
|
|
|
|
Add prebuilt library with support for adding dependencies on ESP-IDF
|
|
components.
|
|
#]]
|
|
function(add_prebuilt_library target_name lib_path)
|
|
cmake_parse_arguments(_ "" "" "REQUIRES;PRIV_REQUIRES" ${ARGN})
|
|
|
|
get_filename_component(lib_path "${lib_path}"
|
|
ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
|
|
|
add_library(${target_name} STATIC IMPORTED GLOBAL)
|
|
set_property(TARGET ${target_name} PROPERTY IMPORTED_LOCATION ${lib_path})
|
|
|
|
foreach(req ${__REQUIRES})
|
|
idf_component_get_property(req_lib "${req}" COMPONENT_LIB)
|
|
set_property(TARGET ${target_name} APPEND PROPERTY LINK_LIBRARIES "${req_lib}")
|
|
set_property(TARGET ${target_name} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${req_lib}")
|
|
endforeach()
|
|
|
|
foreach(req ${__PRIV_REQUIRES})
|
|
idf_component_get_property(req_lib "${req}" COMPONENT_LIB)
|
|
set_property(TARGET ${target_name} APPEND PROPERTY LINK_LIBRARIES "${req_lib}")
|
|
set_property(TARGET ${target_name} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "$<LINK_ONLY:${req_lib}>")
|
|
endforeach()
|
|
endfunction()
|
|
|
|
#[[
|
|
__get_target_dependencies(TARGET <target>
|
|
OUTPUT <variable>)
|
|
|
|
:TARGET[in]: Target for which to get dependencies.
|
|
:OUTPUT[out]: Output variable where the list of dependencies will be stored.
|
|
|
|
Recursively retrieve the list of dependencies for ``TARGET``.
|
|
#]]
|
|
function(__get_target_dependencies)
|
|
set(options)
|
|
set(one_value TARGET OUTPUT)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_TARGET)
|
|
idf_die("TARGET option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
|
|
set(targets_list "${${ARG_OUTPUT}}")
|
|
set(target_deps)
|
|
|
|
get_target_property(target_deps_link ${ARG_TARGET} LINK_LIBRARIES)
|
|
if(target_deps_link)
|
|
list(APPEND target_deps "${target_deps_link}")
|
|
endif()
|
|
get_target_property(target_deps_interface ${ARG_TARGET} INTERFACE_LINK_LIBRARIES)
|
|
if(target_deps_interface)
|
|
list(APPEND target_deps "${target_deps_interface}")
|
|
endif()
|
|
list(REMOVE_DUPLICATES target_deps)
|
|
|
|
if(NOT target_deps)
|
|
# This target has no dependencies, nothing to do.
|
|
return()
|
|
endif()
|
|
|
|
foreach(dep IN LISTS target_deps)
|
|
if(dep IN_LIST targets_list)
|
|
# This dependency has already been processed, skip it.
|
|
continue()
|
|
endif()
|
|
|
|
if(dep MATCHES "^\\$<.*>$")
|
|
# Skip generator expressions
|
|
continue()
|
|
endif()
|
|
|
|
if(NOT TARGET ${dep})
|
|
# LINK_LIBRARIES may contain various non-library-related linker flags
|
|
# (-u, -L, -l, etc.), skip them.
|
|
if(dep MATCHES "^-" OR dep MATCHES "^\:\:")
|
|
continue()
|
|
endif()
|
|
|
|
# If the dependency is not a target, it may be a library. Add it to the list.
|
|
list(APPEND targets_list ${dep})
|
|
else()
|
|
# Recursively add the dependencies of this target.
|
|
list(APPEND targets_list ${dep})
|
|
__get_target_dependencies(TARGET ${dep} OUTPUT targets_list)
|
|
endif()
|
|
endforeach()
|
|
|
|
set(${ARG_OUTPUT} ${targets_list} PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
__get_executable_library_or_die(TARGET <executable>
|
|
OUTPUT <library>)
|
|
|
|
:TARGET[in]: Executable target.
|
|
:OUTPUT[out]: Output variable to store the library interface target linked
|
|
to the executable.
|
|
|
|
Search for the library interface target created with the idf_build_library()
|
|
function and linked to the executable, examine the LINK_LIBRARIES for the
|
|
executable and the LIBRARY_INTERFACES build property, which stores all
|
|
library interface targets created by the idf_build_library() function.
|
|
#]]
|
|
function(__get_executable_library_or_die)
|
|
set(options)
|
|
set(one_value TARGET OUTPUT)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_TARGET)
|
|
idf_die("TARGET option is required")
|
|
endif()
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
|
|
get_target_property(type "${ARG_TARGET}" TYPE)
|
|
if(NOT type STREQUAL "EXECUTABLE")
|
|
idf_die("The type of target '${ARG_TARGET}' is '${type}', "
|
|
"but an 'EXECUTABLE' target type is expected.")
|
|
endif()
|
|
|
|
set(library NOTFOUND)
|
|
get_target_property(targets "${ARG_TARGET}" LINK_LIBRARIES)
|
|
idf_build_get_property(libraries LIBRARY_INTERFACES)
|
|
foreach(target IN LISTS targets)
|
|
if("${target}" IN_LIST libraries)
|
|
set(library "${target}")
|
|
break()
|
|
endif()
|
|
endforeach()
|
|
|
|
if("${library}" STREQUAL "NOTFOUND")
|
|
idf_die("No library interface target linked to the '${ARG_TARGET}' executable")
|
|
endif()
|
|
set(${ARG_OUTPUT} "${library}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
__make_json_list(<list> OUTPUT <variable>)
|
|
|
|
:list[in]: CMake string containing a list to be converted into JSON
|
|
format.
|
|
:OUTPUT[out]: Output variable name to store the JSON list.
|
|
|
|
Convert a CMake list to a JSON list and store it in a OUTPUT
|
|
variable.
|
|
#]]
|
|
function(__make_json_list input)
|
|
set(options)
|
|
set(one_value OUTPUT)
|
|
set(multi_value)
|
|
cmake_parse_arguments(ARG "${options}" "${one_value}" "${multi_value}" ${ARGN})
|
|
|
|
if(NOT DEFINED ARG_OUTPUT)
|
|
idf_die("OUTPUT option is required")
|
|
endif()
|
|
|
|
list(LENGTH input length)
|
|
if(${length})
|
|
string(REPLACE ";" "\", \"" result "[ \"${input}\" ]")
|
|
else()
|
|
set(result "[]")
|
|
endif()
|
|
set("${ARG_OUTPUT}" "${result}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
#[[
|
|
fail_at_build_time(<target> <message>...)
|
|
|
|
:target[in]: Target name to create and add to ALL.
|
|
:message[in]: Error message to display. Multiple messages may be provided.
|
|
|
|
Create a phony target that intentionally fails the build, displaying the
|
|
error messages specified in the message arguments. An empty CMake file is
|
|
created and included, which is then deleted when the target is executed.
|
|
This forces CMake to rerun, as the file is added to the CMake rerun
|
|
dependencies.
|
|
#]]
|
|
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}")
|
|
endforeach()
|
|
# Generate a timestamp file that gets included. When deleted on build, this forces CMake
|
|
# to rerun.
|
|
string(RANDOM filename)
|
|
set(filename "${CMAKE_CURRENT_BINARY_DIR}/${filename}.cmake")
|
|
file(WRITE "${filename}" "")
|
|
include("${filename}")
|
|
set(fail_message "Failing the build (see errors on lines above)")
|
|
add_custom_target(${target_name} ALL
|
|
${message_lines}
|
|
COMMAND ${CMAKE_COMMAND} -E remove "${filename}"
|
|
COMMAND ${CMAKE_COMMAND} -E env FAIL_MESSAGE=${fail_message}
|
|
${CMAKE_COMMAND} -P ${idf_path}/tools/cmake/scripts/fail.cmake
|
|
VERBATIM)
|
|
endfunction()
|