File: FindOrBuildPackage.cmake

package info (click to toggle)
ycm-cmake-modules 0.13.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 3,640 kB
  • sloc: python: 319; sh: 181; makefile: 22
file content (265 lines) | stat: -rw-r--r-- 9,439 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# SPDX-FileCopyrightText: 2012-2021 Istituto Italiano di Tecnologia (IIT)
# SPDX-License-Identifier: BSD-3-Clause

#[=======================================================================[.rst:
FindOrBuildPackage
------------------

Searches for a package and builds it if it cannot be found.

.. command:: find_or_build_package

Searches for a package and builds it if it cannot be found::

 find_or_build_package(<PackageName> <find_package_args>)

This module tries to locate a package using :command:`find_package`.
If the package cannot be found it tries to build it, by including
``Build<package>``. This file should contain the instructions to
download and build the package (for example using
:module:`ExternalProject`)

The arguments passed to the function, are passed to the
:command:`find_package` command.

FIXME Figure out how to handle the REQUIRED and QUIET arguments

If the package was found, the ``USE_SYSTEM_<PackageName>`` cached
variable can be disabled in order to force CMake to build the package
instead of using the one found on the system. To automatically force
CMake to build all the packages that are also found  on the system,
the ``YCM_DISABLE_SYSTEM_PACKAGES`` cache variable can be enabled.

This function sets these variables::

 HAVE_SYSTEM_<PackageName> # The package was found on the system
 HAVE_<PackageName> # The package is available

The user should use the latest, to check if a package is available,
instead of checking using ``<PackageName>_FOUND``. For example

FIXME Check uppercase (${_PKG}) and lowercase (${_pkg}) variables

 .. code-block:: cmake

   find_or_build_package(Foo)
   if(HAVE_Foo)
     ...
   endif()


.. variable:: CMAKE_DISABLE_BUILD_PACKAGE_<PackageName>

Using this variable, building a package is explicitly forbidden.
therefore if the package cannot be found on the system, the
``HAVE_<PackageName>`` will be set to false.
#]=======================================================================]


if(DEFINED __FIND_OR_BUILD_PACKAGE_INCLUDED)
  return()
endif()
set(__FIND_OR_BUILD_PACKAGE_INCLUDED TRUE)


include(CMakeParseArguments)
include(CMakeDependentOption)
include(ExtractVersion)

function(FIND_OR_BUILD_PACKAGE _pkg)
    string(TOUPPER "${_pkg}" _PKG)
    string(REGEX REPLACE "[^A-Z0-9]" "_" _PKG "${_PKG}")

    # Extract version argument from ARGN (if available)
    if(${ARGC} GREATER 1)
        list(GET ARGN 0 _version)
        # Check all possible find_package arguments
        foreach(_arg in EXACT
                        QUIET
                        MODULE
                        REQUIRED
                        COMPONENTS
                        OPTIONAL_COMPONENTS
                        CONFIG
                        NO_MODULE
                        NO_POLICY_SCOPE
                        NAMES
                        CONFIGS
                        HINTS
                        PATHS
                        PATH_SUFFIXES
                        NO_DEFAULT_PATH
                        NO_CMAKE_ENVIRONMENT_PATH
                        NO_CMAKE_PATH
                        NO_SYSTEM_ENVIRONMENT_PATH
                        NO_CMAKE_PACKAGE_REGISTRY
                        NO_CMAKE_BUILDS_PATH
                        NO_CMAKE_SYSTEM_PATH
                        NO_CMAKE_SYSTEM_PACKAGE_REGISTRY
                        CMAKE_FIND_ROOT_PATH_BOTH
                        ONLY_CMAKE_FIND_ROOT_PATH
                        NO_CMAKE_FIND_ROOT_PATH)
            if("${_version}" STREQUAL "${_arg}")
                unset(_version)
                break()
            endif()
        endforeach()
        if(DEFINED _version)
            list(REMOVE_AT ARGN 0)
        endif()
    endif()

    if(${HAVE_${_PKG}})
        return()
    endif()

# Check arguments
    set(_options REQUIRED
                 QUIET
                 MODULE
                 CONFIG
                 NO_MODULE
                 NO_CMAKE_PACKAGE_REGISTRY
                 NO_CMAKE_SYSTEM_PACKAGE_REGISTRY)
    set(_oneValueArgs )
    set(_multiValueArgs COMPONENTS)

    cmake_parse_arguments(_${_PKG} "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" "${ARGN}")

# Arguments for the find_package command
# REQUIRED and QUIET will be set when necessary
    if(DEFINED _${_PKG}_COMPONENTS)
        set(_findArgs COMPONENTS ${_${_PKG}_COMPONENTS} ${_${_PKG}_UNPARSED_ARGUMENTS})
    else()
        set(_findArgs ${_${_PKG}_UNPARSED_ARGUMENTS})
    endif()

# Preliminary find_package to enable/disable USE_SYSTEM_${_PKG} option
    # Use the FindPkg.cmake module first
    if(NOT _${_PKG}_NO_MODULE AND NOT _${_PKG}_CONFIG)
        # FIXME This might require to check for all the other arguments, or they
        #       might conflict with the MODULE argument
        find_package(${_pkg} ${_version} ${_findArgs} MODULE QUIET)
    endif()

    # If the module failed, search a PkgConfig.cmake file
    if(NOT ${_pkg}_FOUND AND NOT ${_PKG}_FOUND AND NOT _${_PKG}_MODULE)
        find_package(${_pkg} ${_version} ${_findArgs} CONFIG QUIET)
    endif()

    if(${_pkg}_FOUND OR ${_PKG}_FOUND)
        set(HAVE_SYSTEM_${_PKG} 1)
        get_property(_ycm_projects GLOBAL PROPERTY YCM_PROJECTS)
        list(APPEND _ycm_projects ${_pkg})
        list(REMOVE_DUPLICATES _ycm_projects)
        set_property(GLOBAL PROPERTY YCM_PROJECTS ${_ycm_projects})
    endif()
    option(YCM_DISABLE_SYSTEM_PACKAGES "Disable use of all the system installed packages" OFF)
    mark_as_advanced(YCM_DISABLE_SYSTEM_PACKAGES)
    cmake_dependent_option(USE_SYSTEM_${_PKG} "Use system installed ${_pkg}" ON "HAVE_SYSTEM_${_PKG};NOT YCM_DISABLE_SYSTEM_PACKAGES" OFF)
    mark_as_advanced(USE_SYSTEM_${_PKG})


    if(USE_SYSTEM_${_PKG})
        # The system version was found and should be used
        set(HAVE_${_PKG} 1)
    elseif(CMAKE_DISABLE_BUILD_PACKAGE_${_PKG})
        # Building the package was explicitly disabled
        set(HAVE_${_PKG} 0)
    else()
        # The system version was not found or the user chose not to use it,
        # building the package was not disabled, therefore we need to build it.

        # Save variables to restore them at the end
        foreach(_var REQUIRED
                     QUIETLY
                     VERSION
                     VERSION_MAJOR
                     VERSION_MINOR
                     VERSION_PATCH
                     VERSION_TWEAK
                     VERSION_COUNT
                     COMPONENTS)
            unset(_${_PKG}_BUILD_${_var})
            if(DEFINED ${_PKG}_BUILD_${_var})
                set(_${_PKG}_BUILD_${_var} ${_PKG}_BUILD_${_var})
                unset(${_PKG}_BUILD_${_var})
            endif()
        endforeach()

        set(${_PKG}_BUILD_REQUIRED ${_${_PKG}_REQUIRED})
        set(${_PKG}_BUILD_QUIETLY ${_${_PKG}_QUIET})
        if(DEFINED ${_${_PKG}_VERSION})
            set(${_PKG}_BUILD_VERSION ${_${_PKG}_VERSION})
            extract_version(${_PKG}_BUILD REVERSE_NAME)
        endif()
        set(${_PKG}_BUILD_COMPONENTS ${_${_PKG}_COMPONENTS})

        if(TARGET ${_pkg})
            # Weird. We found the recipe, but this doesn't add a TARGET ${_pkg}
            # Should not happen, let's print an AUTHOR_WARNING
            message(AUTHOR_WARNING "A ${_pkg} target already exists before including Build${_pkg}.")
        endif()
        # Include the Build recipe
        include(Build${_pkg} OPTIONAL RESULT_VARIABLE _${_PKG}_RECIPE)
        if(NOT _${_PKG}_RECIPE)
            # We couldn't find the build recipe
            set(HAVE_${_PKG} 0)
        else()
            if(NOT TARGET ${_pkg})
                # Weird. We found the recipe, but this doesn't add a TARGET ${_pkg}
                # Should not happen, let's print an AUTHOR_WARNING
                message(AUTHOR_WARNING "The file ${_${_PKG}_RECIPE} does not define a ${_pkg} target.")
            endif()
            set(HAVE_${_PKG} 1)
        endif()

        # Restore original value for the variables
        foreach(_var REQUIRED
                     QUIETLY
                     VERSION
                     VERSION_MAJOR
                     VERSION_MINOR
                     VERSION_PATCH
                     VERSION_TWEAK
                     VERSION_COUNT
                     COMPONENTS)
            if(DEFINED _${_PKG}_BUILD_${_var})
                set(${_PKG}_BUILD_${_var} _${_PKG}_BUILD_${_var})
            else()
                unset(${_PKG}_BUILD_${_var})
            endif()
            unset(_${_PKG}_BUILD_${_var})
        endforeach()
    endif()

    # Display errors/messages
    set(_runFind 0)
    if(NOT HAVE_${_PKG})
        if(${_${_PKG}_REQUIRED})
            message(SEND_ERROR "Cannot find or build package ${_pkg}")
            set(_runFind 1)
        elseif(NOT ${_${_PKG}_QUIET})
            message(STATUS "Cannot find or build package ${_pkg}")
            set(_runFind 1)
        endif()
    elseif(NOT ${_${_PKG}_QUIET})
        if(USE_SYSTEM_${_PKG})
            set(_runFind 1)
        else()
            message(STATUS "Package ${_pkg} not found. Will be downloaded and built.")
        endif()
    endif()

    if(_runFind)
        # Rerun find_package with all the arguments to display output
        find_package(${_pkg} ${_version} ${ARGN})
    endif()

    get_cmake_property(_vars VARIABLES)
    foreach(_var ${_vars})
        if(_var MATCHES "^HAVE_")
            set(${_var} ${${_var}} PARENT_SCOPE)
        endif()
    endforeach()
endfunction()