1# Distributed under the OSI-approved BSD 3-Clause License. See accompanying 2# file Copyright.txt or https://cmake.org/licensing for details. 3 4#[=======================================================================[.rst: 5FindPackageHandleStandardArgs 6----------------------------- 7 8This module provides a function intended to be used in :ref:`Find Modules` 9implementing :command:`find_package(<PackageName>)` calls. It handles the 10``REQUIRED``, ``QUIET`` and version-related arguments of ``find_package``. 11It also sets the ``<PackageName>_FOUND`` variable. The package is 12considered found if all variables listed contain valid results, e.g. 13valid filepaths. 14 15.. command:: find_package_handle_standard_args 16 17 There are two signatures:: 18 19 find_package_handle_standard_args(<PackageName> 20 (DEFAULT_MSG|<custom-failure-message>) 21 <required-var>... 22 ) 23 24 find_package_handle_standard_args(<PackageName> 25 [FOUND_VAR <result-var>] 26 [REQUIRED_VARS <required-var>...] 27 [VERSION_VAR <version-var>] 28 [HANDLE_COMPONENTS] 29 [CONFIG_MODE] 30 [FAIL_MESSAGE <custom-failure-message>] 31 ) 32 33 The ``<PackageName>_FOUND`` variable will be set to ``TRUE`` if all 34 the variables ``<required-var>...`` are valid and any optional 35 constraints are satisfied, and ``FALSE`` otherwise. A success or 36 failure message may be displayed based on the results and on 37 whether the ``REQUIRED`` and/or ``QUIET`` option was given to 38 the :command:`find_package` call. 39 40 The options are: 41 42 ``(DEFAULT_MSG|<custom-failure-message>)`` 43 In the simple signature this specifies the failure message. 44 Use ``DEFAULT_MSG`` to ask for a default message to be computed 45 (recommended). Not valid in the full signature. 46 47 ``FOUND_VAR <result-var>`` 48 Obsolete. Specifies either ``<PackageName>_FOUND`` or 49 ``<PACKAGENAME>_FOUND`` as the result variable. This exists only 50 for compatibility with older versions of CMake and is now ignored. 51 Result variables of both names are always set for compatibility. 52 53 ``REQUIRED_VARS <required-var>...`` 54 Specify the variables which are required for this package. 55 These may be named in the generated failure message asking the 56 user to set the missing variable values. Therefore these should 57 typically be cache entries such as ``FOO_LIBRARY`` and not output 58 variables like ``FOO_LIBRARIES``. 59 60 ``VERSION_VAR <version-var>`` 61 Specify the name of a variable that holds the version of the package 62 that has been found. This version will be checked against the 63 (potentially) specified required version given to the 64 :command:`find_package` call, including its ``EXACT`` option. 65 The default messages include information about the required 66 version and the version which has been actually found, both 67 if the version is ok or not. 68 69 ``HANDLE_COMPONENTS`` 70 Enable handling of package components. In this case, the command 71 will report which components have been found and which are missing, 72 and the ``<PackageName>_FOUND`` variable will be set to ``FALSE`` 73 if any of the required components (i.e. not the ones listed after 74 the ``OPTIONAL_COMPONENTS`` option of :command:`find_package`) are 75 missing. 76 77 ``CONFIG_MODE`` 78 Specify that the calling find module is a wrapper around a 79 call to ``find_package(<PackageName> NO_MODULE)``. This implies 80 a ``VERSION_VAR`` value of ``<PackageName>_VERSION``. The command 81 will automatically check whether the package configuration file 82 was found. 83 84 ``FAIL_MESSAGE <custom-failure-message>`` 85 Specify a custom failure message instead of using the default 86 generated message. Not recommended. 87 88Example for the simple signature: 89 90.. code-block:: cmake 91 92 find_package_handle_standard_args(LibXml2 DEFAULT_MSG 93 LIBXML2_LIBRARY LIBXML2_INCLUDE_DIR) 94 95The ``LibXml2`` package is considered to be found if both 96``LIBXML2_LIBRARY`` and ``LIBXML2_INCLUDE_DIR`` are valid. 97Then also ``LibXml2_FOUND`` is set to ``TRUE``. If it is not found 98and ``REQUIRED`` was used, it fails with a 99:command:`message(FATAL_ERROR)`, independent whether ``QUIET`` was 100used or not. If it is found, success will be reported, including 101the content of the first ``<required-var>``. On repeated CMake runs, 102the same message will not be printed again. 103 104Example for the full signature: 105 106.. code-block:: cmake 107 108 find_package_handle_standard_args(LibArchive 109 REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR 110 VERSION_VAR LibArchive_VERSION) 111 112In this case, the ``LibArchive`` package is considered to be found if 113both ``LibArchive_LIBRARY`` and ``LibArchive_INCLUDE_DIR`` are valid. 114Also the version of ``LibArchive`` will be checked by using the version 115contained in ``LibArchive_VERSION``. Since no ``FAIL_MESSAGE`` is given, 116the default messages will be printed. 117 118Another example for the full signature: 119 120.. code-block:: cmake 121 122 find_package(Automoc4 QUIET NO_MODULE HINTS /opt/automoc4) 123 find_package_handle_standard_args(Automoc4 CONFIG_MODE) 124 125In this case, a ``FindAutmoc4.cmake`` module wraps a call to 126``find_package(Automoc4 NO_MODULE)`` and adds an additional search 127directory for ``automoc4``. Then the call to 128``find_package_handle_standard_args`` produces a proper success/failure 129message. 130#]=======================================================================] 131 132include(${CMAKE_CURRENT_LIST_DIR}/FindPackageMessage.cmake) 133 134# internal helper macro 135macro(_FPHSA_FAILURE_MESSAGE _msg) 136 if (${_NAME}_FIND_REQUIRED) 137 message(FATAL_ERROR "${_msg}") 138 else () 139 if (NOT ${_NAME}_FIND_QUIETLY) 140 message(STATUS "${_msg}") 141 endif () 142 endif () 143endmacro() 144 145 146# internal helper macro to generate the failure message when used in CONFIG_MODE: 147macro(_FPHSA_HANDLE_FAILURE_CONFIG_MODE) 148 # <name>_CONFIG is set, but FOUND is false, this means that some other of the REQUIRED_VARS was not found: 149 if(${_NAME}_CONFIG) 150 _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: missing:${MISSING_VARS} (found ${${_NAME}_CONFIG} ${VERSION_MSG})") 151 else() 152 # If _CONSIDERED_CONFIGS is set, the config-file has been found, but no suitable version. 153 # List them all in the error message: 154 if(${_NAME}_CONSIDERED_CONFIGS) 155 set(configsText "") 156 list(LENGTH ${_NAME}_CONSIDERED_CONFIGS configsCount) 157 math(EXPR configsCount "${configsCount} - 1") 158 foreach(currentConfigIndex RANGE ${configsCount}) 159 list(GET ${_NAME}_CONSIDERED_CONFIGS ${currentConfigIndex} filename) 160 list(GET ${_NAME}_CONSIDERED_VERSIONS ${currentConfigIndex} version) 161 string(APPEND configsText " ${filename} (version ${version})\n") 162 endforeach() 163 if (${_NAME}_NOT_FOUND_MESSAGE) 164 string(APPEND configsText " Reason given by package: ${${_NAME}_NOT_FOUND_MESSAGE}\n") 165 endif() 166 _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} ${VERSION_MSG}, checked the following files:\n${configsText}") 167 168 else() 169 # Simple case: No Config-file was found at all: 170 _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: found neither ${_NAME}Config.cmake nor ${_NAME_LOWER}-config.cmake ${VERSION_MSG}") 171 endif() 172 endif() 173endmacro() 174 175 176function(FIND_PACKAGE_HANDLE_STANDARD_ARGS _NAME _FIRST_ARG) 177 178# Set up the arguments for `cmake_parse_arguments`. 179 set(options CONFIG_MODE HANDLE_COMPONENTS) 180 set(oneValueArgs FAIL_MESSAGE VERSION_VAR FOUND_VAR) 181 set(multiValueArgs REQUIRED_VARS) 182 183# Check whether we are in 'simple' or 'extended' mode: 184 set(_KEYWORDS_FOR_EXTENDED_MODE ${options} ${oneValueArgs} ${multiValueArgs} ) 185 list(FIND _KEYWORDS_FOR_EXTENDED_MODE "${_FIRST_ARG}" INDEX) 186 187 if(${INDEX} EQUAL -1) 188 set(FPHSA_FAIL_MESSAGE ${_FIRST_ARG}) 189 set(FPHSA_REQUIRED_VARS ${ARGN}) 190 set(FPHSA_VERSION_VAR) 191 else() 192 cmake_parse_arguments(FPHSA "${options}" "${oneValueArgs}" "${multiValueArgs}" ${_FIRST_ARG} ${ARGN}) 193 194 if(FPHSA_UNPARSED_ARGUMENTS) 195 message(FATAL_ERROR "Unknown keywords given to FIND_PACKAGE_HANDLE_STANDARD_ARGS(): \"${FPHSA_UNPARSED_ARGUMENTS}\"") 196 endif() 197 198 if(NOT FPHSA_FAIL_MESSAGE) 199 set(FPHSA_FAIL_MESSAGE "DEFAULT_MSG") 200 endif() 201 202 # In config-mode, we rely on the variable <package>_CONFIG, which is set by find_package() 203 # when it successfully found the config-file, including version checking: 204 if(FPHSA_CONFIG_MODE) 205 list(INSERT FPHSA_REQUIRED_VARS 0 ${_NAME}_CONFIG) 206 list(REMOVE_DUPLICATES FPHSA_REQUIRED_VARS) 207 set(FPHSA_VERSION_VAR ${_NAME}_VERSION) 208 endif() 209 210 if(NOT FPHSA_REQUIRED_VARS) 211 message(FATAL_ERROR "No REQUIRED_VARS specified for FIND_PACKAGE_HANDLE_STANDARD_ARGS()") 212 endif() 213 endif() 214 215# now that we collected all arguments, process them 216 217 if("x${FPHSA_FAIL_MESSAGE}" STREQUAL "xDEFAULT_MSG") 218 set(FPHSA_FAIL_MESSAGE "Could NOT find ${_NAME}") 219 endif() 220 221 list(GET FPHSA_REQUIRED_VARS 0 _FIRST_REQUIRED_VAR) 222 223 string(TOUPPER ${_NAME} _NAME_UPPER) 224 string(TOLOWER ${_NAME} _NAME_LOWER) 225 226 if(FPHSA_FOUND_VAR) 227 if(FPHSA_FOUND_VAR MATCHES "^${_NAME}_FOUND$" OR FPHSA_FOUND_VAR MATCHES "^${_NAME_UPPER}_FOUND$") 228 set(_FOUND_VAR ${FPHSA_FOUND_VAR}) 229 else() 230 message(FATAL_ERROR "The argument for FOUND_VAR is \"${FPHSA_FOUND_VAR}\", but only \"${_NAME}_FOUND\" and \"${_NAME_UPPER}_FOUND\" are valid names.") 231 endif() 232 else() 233 set(_FOUND_VAR ${_NAME_UPPER}_FOUND) 234 endif() 235 236 # collect all variables which were not found, so they can be printed, so the 237 # user knows better what went wrong (#6375) 238 set(MISSING_VARS "") 239 set(DETAILS "") 240 # check if all passed variables are valid 241 set(FPHSA_FOUND_${_NAME} TRUE) 242 foreach(_CURRENT_VAR ${FPHSA_REQUIRED_VARS}) 243 if(NOT ${_CURRENT_VAR}) 244 set(FPHSA_FOUND_${_NAME} FALSE) 245 string(APPEND MISSING_VARS " ${_CURRENT_VAR}") 246 else() 247 string(APPEND DETAILS "[${${_CURRENT_VAR}}]") 248 endif() 249 endforeach() 250 if(FPHSA_FOUND_${_NAME}) 251 set(${_NAME}_FOUND TRUE) 252 set(${_NAME_UPPER}_FOUND TRUE) 253 else() 254 set(${_NAME}_FOUND FALSE) 255 set(${_NAME_UPPER}_FOUND FALSE) 256 endif() 257 258 # component handling 259 unset(FOUND_COMPONENTS_MSG) 260 unset(MISSING_COMPONENTS_MSG) 261 262 if(FPHSA_HANDLE_COMPONENTS) 263 foreach(comp ${${_NAME}_FIND_COMPONENTS}) 264 if(${_NAME}_${comp}_FOUND) 265 266 if(NOT DEFINED FOUND_COMPONENTS_MSG) 267 set(FOUND_COMPONENTS_MSG "found components: ") 268 endif() 269 string(APPEND FOUND_COMPONENTS_MSG " ${comp}") 270 271 else() 272 273 if(NOT DEFINED MISSING_COMPONENTS_MSG) 274 set(MISSING_COMPONENTS_MSG "missing components: ") 275 endif() 276 string(APPEND MISSING_COMPONENTS_MSG " ${comp}") 277 278 if(${_NAME}_FIND_REQUIRED_${comp}) 279 set(${_NAME}_FOUND FALSE) 280 string(APPEND MISSING_VARS " ${comp}") 281 endif() 282 283 endif() 284 endforeach() 285 set(COMPONENT_MSG "${FOUND_COMPONENTS_MSG} ${MISSING_COMPONENTS_MSG}") 286 string(APPEND DETAILS "[c${COMPONENT_MSG}]") 287 endif() 288 289 # version handling: 290 set(VERSION_MSG "") 291 set(VERSION_OK TRUE) 292 293 # check with DEFINED here as the requested or found version may be "0" 294 if (DEFINED ${_NAME}_FIND_VERSION) 295 if(DEFINED ${FPHSA_VERSION_VAR}) 296 set(_FOUND_VERSION ${${FPHSA_VERSION_VAR}}) 297 298 if(${_NAME}_FIND_VERSION_EXACT) # exact version required 299 # count the dots in the version string 300 string(REGEX REPLACE "[^.]" "" _VERSION_DOTS "${_FOUND_VERSION}") 301 # add one dot because there is one dot more than there are components 302 string(LENGTH "${_VERSION_DOTS}." _VERSION_DOTS) 303 if (_VERSION_DOTS GREATER ${_NAME}_FIND_VERSION_COUNT) 304 # Because of the C++ implementation of find_package() ${_NAME}_FIND_VERSION_COUNT 305 # is at most 4 here. Therefore a simple lookup table is used. 306 if (${_NAME}_FIND_VERSION_COUNT EQUAL 1) 307 set(_VERSION_REGEX "[^.]*") 308 elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 2) 309 set(_VERSION_REGEX "[^.]*\\.[^.]*") 310 elseif (${_NAME}_FIND_VERSION_COUNT EQUAL 3) 311 set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*") 312 else () 313 set(_VERSION_REGEX "[^.]*\\.[^.]*\\.[^.]*\\.[^.]*") 314 endif () 315 string(REGEX REPLACE "^(${_VERSION_REGEX})\\..*" "\\1" _VERSION_HEAD "${_FOUND_VERSION}") 316 unset(_VERSION_REGEX) 317 if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _VERSION_HEAD) 318 set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") 319 set(VERSION_OK FALSE) 320 else () 321 set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") 322 endif () 323 unset(_VERSION_HEAD) 324 else () 325 if (NOT ${_NAME}_FIND_VERSION VERSION_EQUAL _FOUND_VERSION) 326 set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is exact version \"${${_NAME}_FIND_VERSION}\"") 327 set(VERSION_OK FALSE) 328 else () 329 set(VERSION_MSG "(found suitable exact version \"${_FOUND_VERSION}\")") 330 endif () 331 endif () 332 unset(_VERSION_DOTS) 333 334 else() # minimum version specified: 335 if (${_NAME}_FIND_VERSION VERSION_GREATER _FOUND_VERSION) 336 set(VERSION_MSG "Found unsuitable version \"${_FOUND_VERSION}\", but required is at least \"${${_NAME}_FIND_VERSION}\"") 337 set(VERSION_OK FALSE) 338 else () 339 set(VERSION_MSG "(found suitable version \"${_FOUND_VERSION}\", minimum required is \"${${_NAME}_FIND_VERSION}\")") 340 endif () 341 endif() 342 343 else() 344 345 # if the package was not found, but a version was given, add that to the output: 346 if(${_NAME}_FIND_VERSION_EXACT) 347 set(VERSION_MSG "(Required is exact version \"${${_NAME}_FIND_VERSION}\")") 348 else() 349 set(VERSION_MSG "(Required is at least version \"${${_NAME}_FIND_VERSION}\")") 350 endif() 351 352 endif() 353 else () 354 # Check with DEFINED as the found version may be 0. 355 if(DEFINED ${FPHSA_VERSION_VAR}) 356 set(VERSION_MSG "(found version \"${${FPHSA_VERSION_VAR}}\")") 357 endif() 358 endif () 359 360 if(VERSION_OK) 361 string(APPEND DETAILS "[v${${FPHSA_VERSION_VAR}}(${${_NAME}_FIND_VERSION})]") 362 else() 363 set(${_NAME}_FOUND FALSE) 364 endif() 365 366 367 # print the result: 368 if (${_NAME}_FOUND) 369 FIND_PACKAGE_MESSAGE(${_NAME} "Found ${_NAME}: ${${_FIRST_REQUIRED_VAR}} ${VERSION_MSG} ${COMPONENT_MSG}" "${DETAILS}") 370 else () 371 372 if(FPHSA_CONFIG_MODE) 373 _FPHSA_HANDLE_FAILURE_CONFIG_MODE() 374 else() 375 if(NOT VERSION_OK) 376 _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE}: ${VERSION_MSG} (found ${${_FIRST_REQUIRED_VAR}})") 377 else() 378 _FPHSA_FAILURE_MESSAGE("${FPHSA_FAIL_MESSAGE} (missing:${MISSING_VARS}) ${VERSION_MSG}") 379 endif() 380 endif() 381 382 endif () 383 384 set(${_NAME}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) 385 set(${_NAME_UPPER}_FOUND ${${_NAME}_FOUND} PARENT_SCOPE) 386endfunction() 387