1# set_default 2# 3# Define a variable to a default value if otherwise unset. 4# 5# Priority for new value is: 6# - Existing cmake value (ie set with cmake -D, or already set in CMakeLists) 7# - Value of any non-empty environment variable of the same name 8# - Default value as provided to function 9# 10function(set_default variable default_value) 11 if(NOT ${variable}) 12 if(DEFINED ENV{${variable}} AND NOT "$ENV{${variable}}" STREQUAL "") 13 set(${variable} $ENV{${variable}} PARENT_SCOPE) 14 else() 15 set(${variable} ${default_value} PARENT_SCOPE) 16 endif() 17 endif() 18endfunction() 19 20# spaces2list 21# 22# Take a variable whose value was space-delimited values, convert to a cmake 23# list (semicolon-delimited) 24# 25# Note: if using this for directories, keeps the issue in place that 26# directories can't contain spaces... 27# 28# TODO: look at cmake separate_arguments, which is quote-aware 29function(spaces2list variable_name) 30 string(REPLACE " " ";" tmp "${${variable_name}}") 31 set("${variable_name}" "${tmp}" PARENT_SCOPE) 32endfunction() 33 34# lines2list 35# 36# Take a variable with multiple lines of output in it, convert it 37# to a cmake list (semicolon-delimited), one line per item 38# 39function(lines2list variable_name) 40 string(REGEX REPLACE "\r?\n" ";" tmp "${${variable_name}}") 41 string(REGEX REPLACE ";;" ";" tmp "${tmp}") 42 set("${variable_name}" "${tmp}" PARENT_SCOPE) 43endfunction() 44 45 46# move_if_different 47# 48# If 'source' has different md5sum to 'destination' (or destination 49# does not exist, move it across. 50# 51# If 'source' has the same md5sum as 'destination', delete 'source'. 52# 53# Avoids timestamp updates for re-generated files where content hasn't 54# changed. 55function(move_if_different source destination) 56 set(do_copy 1) 57 file(GLOB dest_exists ${destination}) 58 if(dest_exists) 59 file(MD5 ${source} source_md5) 60 file(MD5 ${destination} dest_md5) 61 if(source_md5 STREQUAL dest_md5) 62 set(do_copy "") 63 endif() 64 endif() 65 66 if(do_copy) 67 message("Moving ${source} -> ${destination}") 68 file(RENAME ${source} ${destination}) 69 else() 70 message("Not moving ${source} -> ${destination}") 71 file(REMOVE ${source}) 72 endif() 73 74endfunction() 75 76# target_add_binary_data adds binary data into the built target, 77# by converting it to a generated source file which is then compiled 78# to a binary object as part of the build 79function(target_add_binary_data target embed_file embed_type) 80 cmake_parse_arguments(_ "" "RENAME_TO" "DEPENDS" ${ARGN}) 81 idf_build_get_property(build_dir BUILD_DIR) 82 idf_build_get_property(idf_path IDF_PATH) 83 84 get_filename_component(embed_file "${embed_file}" ABSOLUTE) 85 86 get_filename_component(name "${embed_file}" NAME) 87 set(embed_srcfile "${build_dir}/${name}.S") 88 89 set(rename_to_arg) 90 if(__RENAME_TO) # use a predefined variable name 91 set(rename_to_arg -D "VARIABLE_BASENAME=${__RENAME_TO}") 92 endif() 93 94 add_custom_command(OUTPUT "${embed_srcfile}" 95 COMMAND "${CMAKE_COMMAND}" 96 -D "DATA_FILE=${embed_file}" 97 -D "SOURCE_FILE=${embed_srcfile}" 98 ${rename_to_arg} 99 -D "FILE_TYPE=${embed_type}" 100 -P "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake" 101 MAIN_DEPENDENCY "${embed_file}" 102 DEPENDS "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake" ${__DEPENDS} 103 WORKING_DIRECTORY "${build_dir}" 104 VERBATIM) 105 106 set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${embed_srcfile}") 107 108 target_sources("${target}" PRIVATE "${embed_srcfile}") 109endfunction() 110 111macro(include_if_exists path) 112 if(EXISTS "${path}") 113 include("${path}") 114 endif() 115endmacro() 116 117# Append a single line to the file specified 118# The line ending is determined by the host OS 119function(file_append_line file line) 120 if(DEFINED ENV{MSYSTEM} OR CMAKE_HOST_WIN32) 121 set(line_ending "\r\n") 122 else() # unix 123 set(line_ending "\n") 124 endif() 125 file(READ ${file} existing) 126 string(FIND ${existing} ${line_ending} last_newline REVERSE) 127 string(LENGTH ${existing} length) 128 math(EXPR length "${length}-1") 129 if(NOT length EQUAL last_newline) # file doesn't end with a newline 130 file(APPEND "${file}" "${line_ending}") 131 endif() 132 file(APPEND "${file}" "${line}${line_ending}") 133endfunction() 134 135# Add one or more linker scripts to the target, including a link-time dependency 136# 137# Automatically adds a -L search path for the containing directory (if found), 138# and then adds -T with the filename only. This allows INCLUDE directives to be 139# used to include other linker scripts in the same directory. 140function(target_linker_script target deptype scriptfiles) 141 cmake_parse_arguments(_ "" "PROCESS" "" ${ARGN}) 142 foreach(scriptfile ${scriptfiles}) 143 get_filename_component(abs_script "${scriptfile}" ABSOLUTE) 144 message(STATUS "Adding linker script ${abs_script}") 145 146 if(__PROCESS) 147 get_filename_component(output "${__PROCESS}" ABSOLUTE) 148 __ldgen_process_template(${abs_script} ${output}) 149 set(abs_script ${output}) 150 endif() 151 152 get_filename_component(search_dir "${abs_script}" DIRECTORY) 153 get_filename_component(scriptname "${abs_script}" NAME) 154 155 if(deptype STREQUAL INTERFACE OR deptype STREQUAL PUBLIC) 156 get_target_property(link_libraries "${target}" INTERFACE_LINK_LIBRARIES) 157 else() 158 get_target_property(link_libraries "${target}" LINK_LIBRARIES) 159 endif() 160 161 list(FIND "${link_libraries}" "-L \"${search_dir}\"" found_search_dir) 162 if(found_search_dir EQUAL "-1") # not already added as a search path 163 target_link_libraries("${target}" "${deptype}" "-L \"${search_dir}\"") 164 endif() 165 166 target_link_libraries("${target}" "${deptype}" "-T ${scriptname}") 167 168 # Note: In ESP-IDF, most targets are libraries and libary LINK_DEPENDS don't propagate to 169 # executable(s) the library is linked to. Attach manually to executable once it is known. 170 # 171 # Property INTERFACE_LINK_DEPENDS is available in CMake 3.13 which should propagate link 172 # dependencies. 173 if(NOT __PROCESS) 174 idf_build_set_property(__LINK_DEPENDS ${abs_script} APPEND) 175 endif() 176 endforeach() 177endfunction() 178 179# Convert a CMake list to a JSON list and store it in a variable 180function(make_json_list list variable) 181 string(REPLACE ";" "\", \"" result "[ \"${list}\" ]") 182 set("${variable}" "${result}" PARENT_SCOPE) 183endfunction() 184 185# add_prefix 186# 187# Adds a prefix to each item in the specified list. 188# 189function(add_prefix var prefix) 190 foreach(elm ${ARGN}) 191 list(APPEND newlist "${prefix}${elm}") 192 endforeach() 193 set(${var} "${newlist}" PARENT_SCOPE) 194endfunction() 195 196# fail_at_build_time 197# 198# Creates a phony target which fails the build and touches CMakeCache.txt to cause a cmake run next time. 199# 200# This is used when a missing file is required at CMake runtime, but we can't fail the build if it is not found, 201# because the "menuconfig" target may be required to fix the problem. 202# 203# We cannot use CMAKE_CONFIGURE_DEPENDS instead because it only works for files which exist at CMake runtime. 204# 205function(fail_at_build_time target_name message_line0) 206 idf_build_get_property(idf_path IDF_PATH) 207 set(message_lines COMMAND ${CMAKE_COMMAND} -E echo "${message_line0}") 208 foreach(message_line ${ARGN}) 209 set(message_lines ${message_lines} COMMAND ${CMAKE_COMMAND} -E echo "${message_line}") 210 endforeach() 211 # Generate a timestamp file that gets included. When deleted on build, this forces CMake 212 # to rerun. 213 string(RANDOM filename) 214 set(filename "${CMAKE_CURRENT_BINARY_DIR}/${filename}.cmake") 215 file(WRITE "${filename}" "") 216 include("${filename}") 217 set(fail_message "Failing the build (see errors on lines above)") 218 add_custom_target(${target_name} ALL 219 ${message_lines} 220 COMMAND ${CMAKE_COMMAND} -E remove "${filename}" 221 COMMAND ${CMAKE_COMMAND} -E env FAIL_MESSAGE=${fail_message} 222 ${CMAKE_COMMAND} -P ${idf_path}/tools/cmake/scripts/fail.cmake 223 VERBATIM) 224endfunction() 225 226# fail_target 227# 228# Creates a phony target which fails when invoked. This is used when the necessary conditions 229# for a target are not met, such as configuration. Rather than ommitting the target altogether, 230# we fail execution with a helpful message. 231function(fail_target target_name message_line0) 232 idf_build_get_property(idf_path IDF_PATH) 233 set(message_lines COMMAND ${CMAKE_COMMAND} -E echo "${message_line0}") 234 foreach(message_line ${ARGN}) 235 set(message_lines ${message_lines} COMMAND ${CMAKE_COMMAND} -E echo "${message_line}") 236 endforeach() 237 # Generate a timestamp file that gets included. When deleted on build, this forces CMake 238 # to rerun. 239 set(fail_message "Failed executing target (see errors on lines above)") 240 add_custom_target(${target_name} 241 ${message_lines} 242 COMMAND ${CMAKE_COMMAND} -E env FAIL_MESSAGE=${fail_message} 243 ${CMAKE_COMMAND} -P ${idf_path}/tools/cmake/scripts/fail.cmake 244 VERBATIM) 245endfunction() 246 247 248function(check_exclusive_args args prefix) 249 set(_args ${args}) 250 spaces2list(_args) 251 set(only_arg 0) 252 foreach(arg ${_args}) 253 if(${prefix}_${arg} AND only_arg) 254 message(FATAL_ERROR "${args} are exclusive arguments") 255 endif() 256 257 if(${prefix}_${arg}) 258 set(only_arg 1) 259 endif() 260 endforeach() 261endfunction() 262 263 264# add_compile_options variant for C++ code only 265# 266# This adds global options, set target properties for 267# component-specific flags 268function(add_cxx_compile_options) 269 foreach(option ${ARGV}) 270 # note: the Visual Studio Generator doesn't support this... 271 add_compile_options($<$<COMPILE_LANGUAGE:CXX>:${option}>) 272 endforeach() 273endfunction() 274 275# add_compile_options variant for C code only 276# 277# This adds global options, set target properties for 278# component-specific flags 279function(add_c_compile_options) 280 foreach(option ${ARGV}) 281 # note: the Visual Studio Generator doesn't support this... 282 add_compile_options($<$<COMPILE_LANGUAGE:C>:${option}>) 283 endforeach() 284endfunction() 285 286 287# add_prebuild_library 288# 289# Add prebuilt library with support for adding dependencies on ESP-IDF components. 290function(add_prebuilt_library target_name lib_path) 291 cmake_parse_arguments(_ "" "" "REQUIRES;PRIV_REQUIRES" ${ARGN}) 292 293 get_filename_component(lib_path "${lib_path}" 294 ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") 295 296 add_library(${target_name} STATIC IMPORTED) 297 set_property(TARGET ${target_name} PROPERTY IMPORTED_LOCATION ${lib_path}) 298 299 foreach(req ${__REQUIRES}) 300 idf_component_get_property(req_lib "${req}" COMPONENT_LIB) 301 set_property(TARGET ${target_name} APPEND PROPERTY LINK_LIBRARIES "${req_lib}") 302 set_property(TARGET ${target_name} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${req_lib}") 303 endforeach() 304 305 foreach(req ${__PRIV_REQUIRES}) 306 idf_component_get_property(req_lib "${req}" COMPONENT_LIB) 307 set_property(TARGET ${target_name} APPEND PROPERTY LINK_LIBRARIES "${req_lib}") 308 set_property(TARGET ${target_name} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "$<LINK_ONLY:${req_lib}>") 309 endforeach() 310endfunction() 311 312 313# file_generate 314# 315# Utility to generate file and have the output automatically added to cleaned files. 316function(file_generate output) 317 cmake_parse_arguments(_ "" "INPUT;CONTENT" "" ${ARGN}) 318 319 if(__INPUT) 320 file(GENERATE OUTPUT "${output}" INPUT "${__INPUT}") 321 elseif(__CONTENT) 322 file(GENERATE OUTPUT "${output}" CONTENT "${__CONTENT}") 323 else() 324 message(FATAL_ERROR "Content to generate not specified.") 325 endif() 326 327 set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 328 APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${output}") 329endfunction() 330 331# add_subdirectory_if_exists 332# 333# Like add_subdirectory, but only proceeds if the given source directory exists. 334function(add_subdirectory_if_exists source_dir) 335 get_filename_component(abs_dir "${source_dir}" 336 ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") 337 if(EXISTS "${abs_dir}") 338 add_subdirectory("${source_dir}" ${ARGN}) 339 else() 340 message(STATUS "Subdirectory '${abs_dir}' does not exist, skipped.") 341 endif() 342endfunction() 343 344 345# add_deprecated_target_alias 346# 347# Creates an alias for exising target and shows deprectation warning 348function(add_deprecated_target_alias old_target new_target) 349 add_custom_target(${old_target} 350 COMMAND ${CMAKE_COMMAND} -E echo 351 "Warning: Command \"${old_target}\" is deprecated and will be removed in the next major release. \ 352 Please use \"${new_target}\" instead." 353 ) 354 add_dependencies(${old_target} ${new_target}) 355endfunction() 356