1#------------------------------------------------------------------------------- 2# Copyright (c) 2021-2023, Arm Limited and Contributors. All rights reserved. 3# 4# SPDX-License-Identifier: BSD-3-Clause 5# 6#------------------------------------------------------------------------------- 7 8# Determine the number of processes to run while running parallel builds. 9# Pass -DPROCESSOR_COUNT=<n> to cmake to override. 10if(NOT DEFINED PROCESSOR_COUNT) 11 include(ProcessorCount) 12 ProcessorCount(PROCESSOR_COUNT) 13 set(PROCESSOR_COUNT ${PROCESSOR_COUNT} CACHE STRING "Number of cores to use for parallel builds.") 14endif() 15 16#[===[.rst: 17Common fetch interface for external dependencies. 18------------------------------------------------- 19 20The following variables can be set in cmake or in the environment to configure various aspect of 21building and external component. 22Note: <DEP_NAME> is the name of the dependency as set int the cmake files with all characters 23converted to uppercase. 24 25``<DEP_NAME>_VERBOSE_CONFIG`` 26 Global variable or environment variable. 27 Pass `--trace-expand` to cmake if set. 28 29``<DEP_NAME>_VERBOSE_BUILD`` 30 Global variable or environment variable. 31 Turn the build step to verbose mode if set. 32 33``<DEP_NAME>_GENERATOR`` 34 Global variable or environment variable. 35 Set the cmake generator to a specific value. If not set, the value of CMAKE_GENERATOR 36 will be used. 37#]===] 38 39#[===[.rst: 40.. cmake:command:: LazyFetch_Fetch 41 42 .. code:: cmake 43 LazyFetch_Fetch(DEP_NAME <dependency name> OPTIONS <list of options for FetchContent_Declare>) 44 45 INPUTS: 46 ``DEP_NAME`` 47 Unique name for the dependency, used by FetchContent_* functions 48 ``OPTIONS`` 49 List of options for FetchContent_Declare, e.g. git url and refspec, check cmake documentations for more 50 information 51 #]===] 52 53function(LazyFetch_Fetch DEP_NAME OPTIONS) 54 include(FetchContent) 55 56 # Fetching dependency 57 FetchContent_Declare( 58 ${DEP_NAME} 59 ${OPTIONS} 60 ) 61 62 FetchContent_GetProperties(${DEP_NAME}) 63 if(NOT "${DEP_NAME}_POPULATED") 64 message(STATUS "Fetching ${DEP_NAME}") 65 FetchContent_Populate(${DEP_NAME}) 66 endif() 67endfunction() 68 69#[===[.rst: 70.. cmake:command:: LazyFetch_ConfigAndBuild 71 72 .. code:: cmake 73 LazyFetch_ConfigAndBuild(DEP_NAME <dependency name> SRC_DIR <source code dir> BIN_DIR <binary dir> 74 CACHE_FILE <path to the initial cache file> INSTALL_DIR <install path>) 75 76 INPUTS: 77 ``DEP_NAME`` 78 Unique name for the dependency 79 ``SRC_DIR`` 80 Source directory 81 ``BIN_DIR`` 82 Build directory 83 ``CACHE_FILE`` 84 Path to the initial cache file. Setting cache variables in this file can be used to augment 85 the configure process. The file goes through :cmake:function:`configure_file(... @ONLY)`, this 86 can be used to pass variables to the external project. 87 ``INSTALL_DIR`` 88 Install path. If not set, the install step will be skipped. 89 ``<DEP_NAME>_VERBOSE_CONFIG`` 90 Global variable or environment variable. 91 Pass `--trace-expand` to cmake if set. 92 ``<DEP_NAME>_VERBOSE_BUILD`` 93 Global variable or environment variable. 94 Turn the build step to verbose mode if set. 95 #]===] 96 97function(LazyFetch_ConfigAndBuild) 98 set(oneValueArgs DEP_NAME SRC_DIR BIN_DIR CACHE_FILE INSTALL_DIR) 99 cmake_parse_arguments(BUILD "${__options}" "${oneValueArgs}" "${_multipleValueArgs}" ${ARGN}) 100 message(STATUS "Configuring and building ${BUILD_DEP_NAME}") 101 102 # Store config file in build dir, so it gets cleaned up 103 set(CONFIGURED_CACHE_FILE ${CMAKE_BINARY_DIR}/${BUILD_DEP_NAME}-init-cache.cmake) 104 configure_file(${BUILD_CACHE_FILE} ${CONFIGURED_CACHE_FILE} @ONLY) 105 106 string(TOUPPER ${BUILD_DEP_NAME} UC_DEP_NAME) 107 108 if (NOT DEFINED ${UC_DEP_NAME}_BUILD_TYPE) 109 message(FATAL_ERROR "Build type for external component ${DEP_NAME} is not set. Please pass " 110 "-D${UC_DEP_NAME}_BUILD_TYPE=<build type> to cmake. Supported build types are" 111 "component specific. Pleas refer to the upstream documentation for more information.") 112 endif() 113 114 if (DEFINED ${UC_DEP_NAME}_VERBOSE_CONFIG OR DEFINED ENV{${UC_DEP_NAME}_VERBOSE_CONFIG}) 115 set(_CMAKE_VERBOSE_CFG_FLAG "--trace-expand") 116 endif() 117 118 if(NOT DEFINED ${UC_DEP_NAME}_GENERATOR) 119 if(DEFINED ENV{${UC_DEP_NAME}_GENERATOR}) 120 set(${UC_DEP_NAME}_GENERATOR ENV{${UC_DEP_NAME}_GENERATOR} CACHE STRING "CMake generator used for ${UC_DEP_NAME}.") 121 else() 122 set(${UC_DEP_NAME}_GENERATOR ${CMAKE_GENERATOR} CACHE STRING "CMake generator used for ${UC_DEP_NAME}.") 123 endif() 124 endif() 125 126 execute_process(COMMAND 127 ${CMAKE_COMMAND} -E env "CROSS_COMPILE=${CROSS_COMPILE}" 128 ${CMAKE_COMMAND} 129 "-C${CONFIGURED_CACHE_FILE}" 130 -DCMAKE_BUILD_TYPE=${${UC_DEP_NAME}_BUILD_TYPE} 131 -G${${UC_DEP_NAME}_GENERATOR} 132 -S ${BUILD_SRC_DIR} 133 -B ${BUILD_BIN_DIR} 134 ${_CMAKE_VERBOSE_CFG_FLAG} 135 RESULT_VARIABLE 136 _exec_error 137 ) 138 if (NOT _exec_error EQUAL 0) 139 message(FATAL_ERROR "Configuring ${BUILD_DEP_NAME} build failed. `${_exec_error}`") 140 endif() 141 142 if (DEFINED ${UC_DEP_NAME}_VERBOSE_BUILD OR DEFINED ENV{${UC_DEP_NAME}_VERBOSE_BUILD}) 143 set(_CMAKE_VERBOSE_BLD_FLAG "--verbose") 144 endif() 145 146 if (BUILD_INSTALL_DIR) 147 execute_process(COMMAND 148 ${CMAKE_COMMAND} -E env "CROSS_COMPILE=${CROSS_COMPILE}" 149 ${CMAKE_COMMAND} 150 --build ${BUILD_BIN_DIR} 151 --parallel ${PROCESSOR_COUNT} 152 --target install 153 ${_CMAKE_VERBOSE_BLD_FLAG} 154 RESULT_VARIABLE 155 _exec_error 156 ) 157 else() 158 execute_process(COMMAND 159 ${CMAKE_COMMAND} -E env "CROSS_COMPILE=${CROSS_COMPILE}" 160 ${CMAKE_COMMAND} 161 --build ${BUILD_BIN_DIR} 162 --parallel ${PROCESSOR_COUNT} 163 ${_CMAKE_VERBOSE_BLD_FLAG} 164 RESULT_VARIABLE 165 _exec_error 166 ) 167 endif() 168 if (NOT _exec_error EQUAL 0) 169 message(FATAL_ERROR "Building ${BUILD_DEP_NAME} failed. ${_exec_error}") 170 endif() 171endfunction() 172 173#[===[.rst: 174.. cmake:command:: LazyFetch_MakeAvailable 175 176 .. code:: cmake 177 LazyFetch_MakeAvailable(DEP_NAME <dependency name> INSTALL_DIR <install path> 178 PACKAGE_DIR <directory of moduleConfig.cmake file> 179 CACHE_FILE <path to the cache init file> FETCH_OPTIONS <options for the fetching process>) 180 181 INPUTS: 182 ``DEP_NAME`` 183 If set, this path overwrites the default base path for the FetchContent process 184 ``SOURCE_DIR`` 185 Location of source code. 186 ``INSTALL_DIR`` 187 Build install path 188 ``PACKAGE_DIR`` 189 If set find_package will search this directory for the config file 190 ``CACHE_FILE`` 191 Path to the cache init file, setting cache variables in this file can be used to augment the 192 configure process. The file goes through the :cmake:function:`configure_file(... @ONLY)`, this 193 can be used to pass variables to the cache file 194 ``FETCH_OPTIONS`` 195 Configure the dependency fetching process, this is passed to FetchContent_Declare, check the 196 cmake documentation for more info 197 ``SOURCE_SUBDIR`` 198 A subdirectory relative to the top level directory of the fetched component, where the CMakeLists.txt file 199 can be found. 200 ``<DEP_NAME>_VERBOSE_CONFIG`` 201 Global variable or environment variable. 202 Pass `--trace-expand` to cmake if set. 203 ``<DEP_NAME>_VERBOSE_BUILD`` 204 Global variable or environment variable. 205 Turn the build step to verbose mode if set. 206 #]===] 207 208macro(LazyFetch_MakeAvailable) 209 set(oneValueArgs DEP_NAME SOURCE_DIR BINARY_DIR INSTALL_DIR PACKAGE_DIR CACHE_FILE SOURCE_SUBDIR) 210 set(multipleValueArgs FETCH_OPTIONS) 211 cmake_parse_arguments(MY "${__options}" "${oneValueArgs}" "${multipleValueArgs}" ${ARGN}) 212 message(STATUS "Looking for dependency ${MY_DEP_NAME}") 213 214 if (NOT DEFINED MY_DEP_NAME) 215 message(FATAL_ERROR "Mandatory parameter DEP_NAME is missing.") 216 endif() 217 218 # FetchContent* functions use this form 219 string(TOLOWER ${MY_DEP_NAME} MY_LC_DEP_NAME) 220 # We also need the upper case version 221 string(TOUPPER ${MY_DEP_NAME} MY_UC_DEP_NAME) 222 223 # Look for name collision. We can collide with project() commands, other external components defined with 224 # LazyFetch, FetchCOntent or ExternalProject. 225 if(DEFINED ${MY_LC_DEP_NAME}_BINARY_DIR AND NOT DEFINED ${MY_LC_DEP_NAME}_BINARY_DIR_LZF) 226 string(CONCAT _msg "External dependency name \"${MY_DEP_NAME}\" collides with a project or another external" 227 " dependency name.") 228 message(FATAL_ERROR ${_msg}) 229 endif() 230 # This variable is used to avoid false colision detection when re-configuring the project. 231 set(${MY_LC_DEP_NAME}_BINARY_DIR_LZF On CACHE BOOL "") 232 mark_as_advanced(${MY_LC_DEP_NAME}_BINARY_DIR_LZF) 233 # These two variables are also set by the normal FetchContent process and users could depend on them, 234 # so they are not unset at the end of the macro 235 if (MY_BINARY_DIR) 236 set(${MY_LC_DEP_NAME}_BINARY_DIR ${MY_BINARY_DIR}) 237 else() 238 set(${MY_LC_DEP_NAME}_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/_deps/${MY_LC_DEP_NAME}-build) 239 endif() 240 set(${MY_LC_DEP_NAME}_BINARY_DIR ${${MY_LC_DEP_NAME}_BINARY_DIR} CACHE PATH 241 "Build directory for ${MY_LC_DEP_NAME}" FORCE) 242 243 if (MY_SOURCE_DIR) 244 set(${MY_LC_DEP_NAME}_SOURCE_DIR ${MY_SOURCE_DIR}) 245 else() 246 set(${MY_LC_DEP_NAME}_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/_deps/${MY_LC_DEP_NAME}-src) 247 endif() 248 set(${MY_LC_DEP_NAME}_SOURCE_DIR ${${MY_LC_DEP_NAME}_SOURCE_DIR} CACHE PATH 249 "Source directory for ${MY_LC_DEP_NAME}" FORCE) 250 251 set(${MY_LC_DEP_NAME}_SUBBUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/_deps/${MY_LC_DEP_NAME}-subbuild CACHE 252 STRING "Sub-build directory for ${MY_LC_DEP_NAME}") 253 254 list(APPEND MY_FETCH_OPTIONS 255 SOURCE_DIR "${${MY_LC_DEP_NAME}_SOURCE_DIR}" 256 BINARY_DIR "${${MY_LC_DEP_NAME}_BINARY_DIR}" 257 SUBBUILD_DIR "${${MY_LC_DEP_NAME}_SUBBUILD_DIR}") 258 259 if (NOT DEFINED MY_INSTALL_DIR OR NOT EXISTS ${MY_INSTALL_DIR}) 260 if (NOT EXISTS ${${MY_LC_DEP_NAME}_BINARY_DIR} OR NOT EXISTS ${${MY_LC_DEP_NAME}_SOURCE_DIR}) 261 if (NOT EXISTS ${${MY_LC_DEP_NAME}_SOURCE_DIR}) 262 LazyFetch_Fetch(${MY_LC_DEP_NAME} "${MY_FETCH_OPTIONS}") 263 file(REMOVE_RECURSE "${${MY_LC_DEP_NAME}_BINARY_DIR}") 264 file(REMOVE_RECURSE "${${MY_LC_DEP_NAME}_SUBBUILD_DIR}") 265 endif() 266 if (MY_CACHE_FILE) 267 LazyFetch_ConfigAndBuild( 268 DEP_NAME ${MY_LC_DEP_NAME} 269 SRC_DIR ${${MY_LC_DEP_NAME}_SOURCE_DIR}/${MY_SOURCE_SUBDIR} 270 BIN_DIR ${${MY_LC_DEP_NAME}_BINARY_DIR} 271 CACHE_FILE ${MY_CACHE_FILE} 272 INSTALL_DIR ${MY_INSTALL_DIR} 273 ) 274 endif() 275 elseif(DEFINED MY_INSTALL_DIR) 276 if(DEFINED ${MY_UC_DEP_NAME}_VERBOSE_BUILD OR DEFINED ENV{${MY_UC_DEP_NAME}_VERBOSE_BUILD}) 277 set(_CMAKE_VERBOSE_BLD_FLAG "--verbose") 278 endif() 279 280 execute_process(COMMAND 281 ${CMAKE_COMMAND} -E env "CROSS_COMPILE=${CROSS_COMPILE}" 282 ${CMAKE_COMMAND} 283 --build ${${MY_LC_DEP_NAME}_BINARY_DIR} 284 --parallel ${PROCESSOR_COUNT} 285 --target install 286 ${_CMAKE_VERBOSE_BLD_FLAG} 287 RESULT_VARIABLE 288 _exec_error 289 ) 290 if (NOT _exec_error EQUAL 0) 291 message(FATAL_ERROR "Installing ${BUILD_DEP_NAME} failed. ${_exec_error}") 292 endif() 293 endif() 294 endif() 295 296 # Run find_package again if we just needed the build and install step 297 if (MY_PACKAGE_DIR) 298 unset(${MY_LC_DEP_NAME}_DIR) 299 unset(${MY_LC_DEP_NAME}_DIR CACHE) 300 unset(${MY_DEP_NAME}-FOUND CACHE) 301 find_package(${MY_DEP_NAME} CONFIG REQUIRED NO_DEFAULT_PATH PATHS ${MY_PACKAGE_DIR}) 302 set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${MY_DEP_NAME}_CONFIG) 303 endif() 304 305 unset(MY_DEP_NAME) 306 unset(MY_SOURCE_DIR) 307 unset(MY_BINARY_DIR) 308 unset(MY_INSTALL_DIR) 309 unset(MY_PACKAGE_DIR) 310 unset(MY_CACHE_FILE) 311 unset(MY_SOURCE_SUBDIR) 312 unset(MY_FETCH_OPTIONS) 313 unset(MY_LC_DEP_NAME) 314 unset(oneValueArgs) 315 unset(multipleValueArgs) 316endmacro()