CTK 0.1.0
The Common Toolkit is a community effort to provide support code for medical image analysis, surgical navigation, and related projects.
ctkMacroCheckExternalProjectDependency.cmake
Go to the documentation of this file.
1#.rst:
2# ExternalProjectDependency
3# -------------------------
4#
5# .. only:: html
6#
7# .. contents::
8
9###########################################################################
10#
11# Library: CTK
12#
13# Copyright (c) Kitware Inc.
14#
15# Licensed under the Apache License, Version 2.0 (the "License");
16# you may not use this file except in compliance with the License.
17# You may obtain a copy of the License at
18#
19# http://www.apache.org/licenses/LICENSE-2.0.txt
20#
21# Unless required by applicable law or agreed to in writing, software
22# distributed under the License is distributed on an "AS IS" BASIS,
23# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24# See the License for the specific language governing permissions and
25# limitations under the License.
26#
27###########################################################################
28
29include(CMakeParseArguments)
30
31#.rst:
32# Global Variables
33# ^^^^^^^^^^^^^^^^
34
35#.rst:
36# .. cmake:variable:: EXTERNAL_PROJECT_DIR
37#
38# This variable describes the directory in which external project files
39# matching ``<EXTERNAL_PROJECT_FILE_PREFIX><projectname>.cmake`` expression are globbed.
40#
41if(NOT EXISTS "${EXTERNAL_PROJECT_DIR}")
42 set(EXTERNAL_PROJECT_DIR ${CMAKE_SOURCE_DIR}/SuperBuild)
43endif()
44
45#.rst:
46# .. cmake:variable:: EXTERNAL_PROJECT_ADDITIONAL_DIR
47#
48# If set, this variable represents an other directory in which external project files
49# are searched for if not already found in ``EXTERNAL_PROJECT_DIR``.
50
51#.rst:
52# .. cmake:variable:: EXTERNAL_PROJECT_FILE_PREFIX
53#
54# This variable describes the prefix of the external project files looked up in
55# ``EXTERNAL_PROJECT_DIR``. It defaults to ``External_``.
56#
57if(NOT DEFINED EXTERNAL_PROJECT_FILE_PREFIX)
58 set(EXTERNAL_PROJECT_FILE_PREFIX "External_")
59endif()
60
61#.rst:
62# .. cmake:variable:: SUPERBUILD_TOPLEVEL_PROJECT
63#
64# This variable can be set to explicitly identify the name of the top-level project.
65# If not set, it default to the value of ``CMAKE_PROJECT_NAME``.
66if(NOT DEFINED SUPERBUILD_TOPLEVEL_PROJECT)
67 if(NOT DEFINED CMAKE_PROJECT_NAME)
68 message(FATAL_ERROR "Failed to initialize variable SUPERBUILD_TOPLEVEL_PROJECT. Variable CMAKE_PROJECT_NAME is not defined.")
69 endif()
70 set(SUPERBUILD_TOPLEVEL_PROJECT ${CMAKE_PROJECT_NAME})
71endif()
72
73#.rst:
74# .. cmake:variable:: EP_LIST_SEPARATOR
75#
76# This variable is used to separate list items when passed in various external project
77# ``..._COMMAND`` options.
78#
79# If defaults to ``^^``.
80if(NOT DEFINED EP_LIST_SEPARATOR)
81 set(EP_LIST_SEPARATOR "^^")
82endif()
83
84
85#.rst:
86# .. cmake:variable:: EP_GIT_PROTOCOL
87#
88# The value of this variable is controled by the option ``<SUPERBUILD_TOPLEVEL_PROJECT>_USE_GIT_PROTOCOL``
89# automatically defined by including this CMake module. Setting this option allows to update the value of
90# ``EP_GIT_PROTOCOL`` variable.
91#
92# If enabled, the variable ``EP_GIT_PROTOCOL`` is set to ``git``. Otherwise, it is set to ``https``.
93# The option is enabled by default.
94#
95# The variable ``EP_GIT_PROTOCOL`` can be used when adding external project. For example:
96#
97# .. code-block:: cmake
98#
99# ExternalProject_Add(${proj}
100# ${${proj}_EP_ARGS}
101# GIT_REPOSITORY "${EP_GIT_PROTOCOL}://github.com/Foo/Foo.git"
102# [...]
103# )
104#
105option(${SUPERBUILD_TOPLEVEL_PROJECT}_USE_GIT_PROTOCOL "If behind a firewall turn this off to use https instead." ON)
106set(EP_GIT_PROTOCOL "git")
107if(NOT ${SUPERBUILD_TOPLEVEL_PROJECT}_USE_GIT_PROTOCOL)
108 set(EP_GIT_PROTOCOL "https")
109endif()
110
111# Compute -G arg for configuring external projects with the same CMake generator:
112if(CMAKE_EXTRA_GENERATOR)
113 set(EP_CMAKE_GENERATOR "${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}")
114else()
115 set(EP_CMAKE_GENERATOR "${CMAKE_GENERATOR}")
116endif()
117set(EP_CMAKE_GENERATOR_PLATFORM "${CMAKE_GENERATOR_PLATFORM}")
118set(EP_CMAKE_GENERATOR_TOOLSET "${CMAKE_GENERATOR_TOOLSET}")
119
120#.rst:
121# Functions
122# ^^^^^^^^^
123
124#.rst:
125# .. cmake:function:: mark_as_superbuild
126#
127# .. code-block:: cmake
128#
129# mark_as_superbuild(<varname1>[:<vartype1>] [<varname2>[:<vartype2>] [...]])
130#
131# .. code-block:: cmake
132#
133# mark_as_superbuild(
134# VARS <varname1>[:<vartype1>] [<varname2>[:<vartype2>] [...]]
135# [PROJECTS <projectname> [<projectname> [...]] | ALL_PROJECTS]
136# [LABELS <label1> [<label2> [...]]]
137# )
138#
139# .. code-block:: cmake
140#
141# PROJECTS corresponds to a list of <projectname> that will be added using 'ExternalProject_Add' function.
142# If not specified and called within a project file, it defaults to the value of 'SUPERBUILD_TOPLEVEL_PROJECT'.
143# If instead 'ALL_PROJECTS' is specified, the variables and labels will be passed to all projects.
144#
145# VARS is an expected list of variables specified as <varname>:<vartype> to pass to <projectname>
146#
147#
148# LABELS is an optional list of label to associate with the variable names specified using 'VARS' and passed to
149# the <projectname> as CMake CACHE args of the form:
150# -D<projectname>_EP_LABEL_<label1>=<varname1>;<varname2>[...]
151# -D<projectname>_EP_LABEL_<label2>=<varname1>;<varname2>[...]
152#
153function(mark_as_superbuild)
154 set(options ALL_PROJECTS CMAKE_CMD)
155 set(oneValueArgs)
156 set(multiValueArgs VARS PROJECTS LABELS)
157 cmake_parse_arguments(_sb "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
158
159 set(_vars ${_sb_UNPARSED_ARGUMENTS})
160
161 set(_named_parameters_expected 0)
162 if(_sb_PROJECTS OR _sb_ALL_PROJECTS OR _sb_LABELS OR _sb_VARS)
163 set(_named_parameters_expected 1)
164 set(_vars ${_sb_VARS})
165 endif()
166
167 if(_named_parameters_expected AND _sb_UNPARSED_ARGUMENTS)
168 message(FATAL_ERROR "Arguments '${_sb_UNPARSED_ARGUMENTS}' should be associated with VARS parameter !")
169 endif()
170
171 if(_sb_PROJECTS AND _sb_ALL_PROJECTS)
172 message(FATAL_ERROR "Arguments 'PROJECTS' and 'ALL_PROJECTS' are mutually exclusive !")
173 endif()
174
175 foreach(var ${_vars})
176 set(_type_specified 0)
177 if(var MATCHES ":")
178 set(_type_specified 1)
179 endif()
180 # XXX Display warning with variable type is also specified for cache variable.
181 set(_var ${var})
182 if(NOT _type_specified)
183 get_property(_type_set_in_cache CACHE ${_var} PROPERTY TYPE SET)
184 set(_var_name ${_var})
185 set(_var_type "STRING")
186 if(_type_set_in_cache)
187 get_property(_var_type CACHE ${_var_name} PROPERTY TYPE)
188 endif()
189 set(_var ${_var_name}:${_var_type})
190 endif()
191 list(APPEND _vars_with_type ${_var})
192 endforeach()
193
194 if(_sb_ALL_PROJECTS)
195 set(optional_arg_ALL_PROJECTS "ALL_PROJECTS")
196 else()
197 set(optional_arg_ALL_PROJECTS PROJECTS ${_sb_PROJECTS})
198 endif()
199
200 _sb_append_to_cmake_args(
201 VARS ${_vars_with_type} LABELS ${_sb_LABELS} ${optional_arg_ALL_PROJECTS})
202endfunction()
203
204#
205# _sb_extract_varname_and_vartype(<cmake_varname_and_type> <varname_var> [<vartype_var>])
206#
207# <cmake_varname_and_type> corresponds to variable name and variable type passed as "<varname>:<vartype>"
208#
209# <varname_var> will be set to "<varname>"
210#
211# <vartype_var> is an optional variable name that will be set to "<vartype>"
212#
213function(_sb_extract_varname_and_vartype cmake_varname_and_type varname_var)
214 set(_vartype_var "${ARGV2}")
215 string(REPLACE ":" ";" varname_and_vartype ${cmake_varname_and_type})
216 list(GET varname_and_vartype 0 _varname)
217 list(GET varname_and_vartype 1 _vartype)
218 set(${varname_var} ${_varname} PARENT_SCOPE)
219 if(_vartype_var MATCHES ".+")
220 set(${_vartype_var} ${_vartype} PARENT_SCOPE)
221 endif()
222endfunction()
223
224
225function(_sb_list_to_string separator input_list output_string_var)
226 set(_string "")
227 # Get list length
228 list(LENGTH input_list list_length)
229 # If the list has 0 or 1 element, there is no need to loop over.
230 if(list_length LESS 2)
231 set(_string "${input_list}")
232 else()
233 math(EXPR last_element_index "${list_length} - 1")
234 foreach(index RANGE ${last_element_index})
235 # Get current item_value
236 list(GET input_list ${index} item_value)
237 if(NOT item_value STREQUAL "")
238 # .. and append non-empty value to output string
239 set(_string "${_string}${item_value}")
240 # Append separator if current element is NOT the last one.
241 if(NOT index EQUAL last_element_index)
242 set(_string "${_string}${separator}")
243 endif()
244 endif()
245 endforeach()
246 endif()
247 set(${output_string_var} ${_string} PARENT_SCOPE)
248endfunction()
249
250#
251# _sb_cmakevar_to_cmakearg(<cmake_varname_and_type> <cmake_arg_var> <has_cfg_intdir_var> [<varname_var> [<vartype_var>]])
252#
253# <cmake_varname_and_type> corresponds to variable name and variable type passed as "<varname>:<vartype>"
254#
255# <cmake_arg_var> is a variable name that will be set to "-D<varname>:<vartype>=${<varname>}"
256#
257# <has_int_dir_var> is set to either TRUE or FALSE.
258# FALSE means that the value does NOT reference ${CMAKE_CFG_INTDIR} and
259# the generated cmake argument should be passed to ExternalProject_Add as CMAKE_CACHE_ARGS.
260# TRUEmeans that the value does reference ${CMAKE_CFG_INTDIR} and
261# the generated cmake argument should be passed to ExternalProject_Add as CMAKE_ARGS.
262#
263# <varname_var> is an optional variable name that will be set to "<varname>"
264#
265# <vartype_var> is an optional variable name that will be set to "<vartype>"
266#
267function(_sb_cmakevar_to_cmakearg cmake_varname_and_type cmake_arg_var has_cfg_intdir_var)
268 set(_varname_var "${ARGV3}")
269 set(_vartype_var "${ARGV4}")
270
271 _sb_extract_varname_and_vartype(${cmake_varname_and_type} _varname _vartype)
272
273 set(_var_value "${${_varname}}")
274
275 # Use cache value unless it is INTERNAL
276 if(_vartype STREQUAL "INTERNAL")
277 set(_vartype "STRING")
278 else()
279 get_property(_value_set_in_cache CACHE ${_varname} PROPERTY VALUE SET)
280 if(_value_set_in_cache)
281 get_property(_var_value CACHE ${_varname} PROPERTY VALUE)
282 endif()
283 endif()
284
285 set(_has_cfg_intdir FALSE)
286 if(CMAKE_CONFIGURATION_TYPES)
287 string(FIND "${_var_value}" ${CMAKE_CFG_INTDIR} _index)
288 if(NOT _index EQUAL -1)
289 # Separate list item with <EP_LIST_SEPARATOR>
290 _sb_list_to_string(${EP_LIST_SEPARATOR} "${_var_value}" _var_value)
291 set(_has_cfg_intdir TRUE)
292 endif()
293 endif()
294
295 if(NOT _has_cfg_intdir)
296 string(REPLACE "\"" "\\\"" _var_value "${_var_value}")
297 endif()
298
299 set(${cmake_arg_var} -D${_varname}:${_vartype}=${_var_value} PARENT_SCOPE)
300 set(${has_cfg_intdir_var} ${_has_cfg_intdir} PARENT_SCOPE)
301
302 if(_varname_var MATCHES ".+")
303 set(${_varname_var} ${_varname} PARENT_SCOPE)
304 endif()
305 if(_vartype_var MATCHES ".+")
306 set(${_vartype_var} ${_vartype} PARENT_SCOPE)
307 endif()
308endfunction()
309
310set(_ALL_PROJECT_IDENTIFIER "ALLALLALL")
311
312#
313# _sb_append_to_cmake_args(
314# [VARS <varname1>:<vartype1> [<varname2>:<vartype2> [...]]]
315# [PROJECTS <projectname> [<projectname> [...]] | ALL_PROJECTS]
316# [LABELS <label1> [<label2> [...]]]
317# )
318#
319# PROJECTS corresponds to a list of <projectname> that will be added using 'ExternalProject_Add' function.
320# If not specified and called within a project file, it defaults to the value of 'SUPERBUILD_TOPLEVEL_PROJECT'.
321# If instead 'ALL_PROJECTS' is specified, the variables and labels will be passed to all projects.
322#
323# VARS is an expected list of variables specified as <varname>:<vartype> to pass to <projectname>
324#
325#
326# LABELS is an optional list of label to associate with the variable names specified using 'VARS' and passed to
327# the <projectname> as CMake CACHE args of the form:
328# -D<projectname>_EP_LABEL_<label1>=<varname1>;<varname2>[...]
329# -D<projectname>_EP_LABEL_<label2>=<varname1>;<varname2>[...]
330#
331function(_sb_append_to_cmake_args)
332 set(options ALL_PROJECTS)
333 set(oneValueArgs)
334 set(multiValueArgs VARS PROJECTS LABELS)
335 cmake_parse_arguments(_sb "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
336
337 if(NOT _sb_PROJECTS AND NOT _sb_ALL_PROJECTS)
338 set(_sb_PROJECTS ${SUPERBUILD_TOPLEVEL_PROJECT})
339 endif()
340
341 if(_sb_ALL_PROJECTS)
342 set(_sb_PROJECTS ${_ALL_PROJECT_IDENTIFIER})
343 endif()
344
345 foreach(_sb_PROJECT ${_sb_PROJECTS})
346
347 set(_ep_varnames "")
348 foreach(varname_and_vartype ${_sb_VARS})
349 if(NOT TARGET ${_sb_PROJECT})
350 set_property(GLOBAL APPEND PROPERTY ${_sb_PROJECT}_EP_CMAKE_ARGS ${varname_and_vartype})
351 _sb_extract_varname_and_vartype(${varname_and_vartype} _varname)
352 else()
353 message(FATAL_ERROR "Function _sb_append_to_cmake_args not allowed because project '${_sb_PROJECT}' already added !")
354 endif()
355 list(APPEND _ep_varnames ${_varname})
356 endforeach()
357
358 if(_sb_LABELS)
359 set_property(GLOBAL APPEND PROPERTY ${_sb_PROJECT}_EP_LABELS ${_sb_LABELS})
360 foreach(label ${_sb_LABELS})
361 set_property(GLOBAL APPEND PROPERTY ${_sb_PROJECT}_EP_LABEL_${label} ${_ep_varnames})
362 endforeach()
363 endif()
364 endforeach()
365endfunction()
366
367#.rst:
368# .. cmake:function:: ExternalProject_DeclareLabels
369#
370# .. code-block:: cmake
371#
372# ExternalProject_DeclareLabels(
373# [PROJECTS <projectname> [<projectname> [...]] | ALL_PROJECTS]
374# LABELS <label1> [<label2> [...]]
375# )
376#
377# .. code-block:: cmake
378#
379# PROJECTS corresponds to a list of <projectname> that will be added using 'ExternalProject_Add' function.
380# If not specified and called within a project file, it defaults to the value of 'SUPERBUILD_TOPLEVEL_PROJECT'.
381# If instead 'ALL_PROJECTS' is specified, the variables and labels will be passed to all projects.
382#
383# LABELS is a list of label to pass to the <projectname> as CMake CACHE args of the
384# form -D<projectname>_EP_LABEL_<label>= unless specific variables
385# have been associated with the labels using mark_as_superbuild.
386#
387function(ExternalProject_DeclareLabels)
388 set(options ALL_PROJECTS)
389 set(oneValueArgs)
390 set(multiValueArgs PROJECTS LABELS)
391 cmake_parse_arguments(_sb "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
392
393 if(_sb_PROJECTS AND _sb_ALL_PROJECTS)
394 message(FATAL_ERROR "Arguments 'PROJECTS' and 'ALL_PROJECTS' are mutually exclusive !")
395 endif()
396
397 if(_sb_ALL_PROJECTS)
398 set(optional_arg_ALL_PROJECTS "ALL_PROJECTS")
399 else()
400 set(optional_arg_ALL_PROJECTS PROJECTS ${_sb_PROJECTS})
401 endif()
402
403 _sb_append_to_cmake_args(
404 LABELS ${_sb_LABELS} ${optional_arg_ALL_PROJECTS})
405endfunction()
406
407function(_sb_get_external_project_arguments proj varname)
408
409 mark_as_superbuild(${SUPERBUILD_TOPLEVEL_PROJECT}_USE_SYSTEM_${proj}:BOOL)
410
411 function(_sb_collect_args proj)
412 # Set list of CMake args associated with each label
413 get_property(_labels GLOBAL PROPERTY ${proj}_EP_LABELS)
414 if(_labels)
415 list(REMOVE_DUPLICATES _labels)
416 foreach(label ${_labels})
417 get_property(${proj}_EP_LABEL_${label} GLOBAL PROPERTY ${proj}_EP_LABEL_${label})
418 if(${proj}_EP_LABEL_${label})
419 list(REMOVE_DUPLICATES ${proj}_EP_LABEL_${label})
420 endif()
421 _sb_append_to_cmake_args(PROJECTS ${proj}
422 VARS ${proj}_EP_LABEL_${label}:STRING)
423 endforeach()
424 endif()
425
426 get_property(_args GLOBAL PROPERTY ${proj}_EP_CMAKE_ARGS)
427 foreach(var ${_args})
428 _sb_cmakevar_to_cmakearg(${var} cmake_arg _has_cfg_intdir)
429 set(_ep_property "CMAKE_CACHE_ARGS")
430 if(_has_cfg_intdir)
431 set(_ep_property "CMAKE_ARGS")
432 endif()
433 set_property(GLOBAL APPEND PROPERTY ${proj}_EP_PROPERTY_${_ep_property} ${cmake_arg})
434 endforeach()
435
436 endfunction()
437
438 _sb_collect_args(${proj})
439 _sb_collect_args(${_ALL_PROJECT_IDENTIFIER})
440
441 set(_ep_arguments "")
442
443 # Automatically propagate CMake options
444 foreach(_cmake_option IN ITEMS
445 CMAKE_EXPORT_COMPILE_COMMANDS
446 CMAKE_JOB_POOL_COMPILE
447 CMAKE_JOB_POOL_LINK
448 CMAKE_JOB_POOLS
449 )
450 if(DEFINED ${_cmake_option})
451 list(APPEND _ep_arguments CMAKE_CACHE_ARGS
452 -D${_cmake_option}:BOOL=${${_cmake_option}}
453 )
454 endif()
455 endforeach()
456
457 foreach(property CMAKE_ARGS CMAKE_CACHE_ARGS)
458 get_property(${proj}_EP_PROPERTY_${property} GLOBAL PROPERTY ${proj}_EP_PROPERTY_${property})
459 get_property(${_ALL_PROJECT_IDENTIFIER}_EP_PROPERTY_${property} GLOBAL PROPERTY ${_ALL_PROJECT_IDENTIFIER}_EP_PROPERTY_${property})
460 set(_all ${${proj}_EP_PROPERTY_${property}} ${${_ALL_PROJECT_IDENTIFIER}_EP_PROPERTY_${property}})
461 list(LENGTH _all _num_properties)
462 if(_num_properties GREATER 0)
463 list(APPEND _ep_arguments ${property} ${_all})
464 endif()
465 endforeach()
466
467 list(APPEND _ep_arguments LIST_SEPARATOR ${EP_LIST_SEPARATOR})
468
469 list(APPEND _ep_arguments CMAKE_GENERATOR ${_sb_CMAKE_GENERATOR})
470 if(CMAKE_VERSION VERSION_GREATER "3.0")
471 list(APPEND _ep_arguments CMAKE_GENERATOR_PLATFORM ${_sb_CMAKE_GENERATOR_PLATFORM})
472 endif()
473 list(APPEND _ep_arguments CMAKE_GENERATOR_TOOLSET ${_sb_CMAKE_GENERATOR_TOOLSET})
474 if(CMAKE_VERSION VERSION_EQUAL "3.4" OR CMAKE_VERSION VERSION_GREATER "3.4")
475 # USES_TERMINAL_* options were introduced in CMake 3.4
476 foreach(step IN ITEMS DOWNLOAD UPDATE CONFIGURE BUILD TEST INSTALL)
477 list(APPEND _ep_arguments
478 USES_TERMINAL_${step} 1
479 )
480 endforeach()
481 endif()
482 set(${varname} ${_ep_arguments} PARENT_SCOPE)
483endfunction()
484
485function(_sb_update_indent proj)
486 superbuild_stack_size(SB_PROJECT_STACK _stack_size)
487 set(_indent "")
488 if(_stack_size GREATER 0)
489 foreach(not_used RANGE 1 ${_stack_size})
490 set(_indent " ${_indent}")
491 endforeach()
492 endif()
493 set_property(GLOBAL PROPERTY SUPERBUILD_${proj}_INDENT ${_indent})
494endfunction()
495
496#.rst:
497# .. cmake:function:: ExternalProject_Message
498#
499# .. code-block:: cmake
500#
501# ExternalProject_Message(<project_name> <msg> [condition])
502#
503function(ExternalProject_Message proj msg)
504 set(_display 1)
505 if(NOT "x${ARGV2}" STREQUAL "x")
506 set(_display ${ARGN})
507 endif()
508 if(${_display})
509 get_property(_indent GLOBAL PROPERTY SUPERBUILD_${proj}_INDENT)
510 message(STATUS "SuperBuild - ${_indent}${msg}")
511 endif()
512endfunction()
513
514#
515# superbuild_stack_content(<stack_name> <output_var>)
516#
517# <stack_name> corresponds to the name of stack.
518#
519# <output_var> is the name of CMake variable that will be set with the content
520# of the stack identified by <stack_name>.
521#
522function(superbuild_stack_content stack_name output_var)
523 get_property(_stack GLOBAL PROPERTY ${stack_name})
524 set(${output_var} ${_stack} PARENT_SCOPE)
525endfunction()
526
527#
528# superbuild_stack_size(<stack_name> <output_var>)
529#
530# <stack_name> corresponds to the name of stack.
531#
532# <output_var> is the name of CMake variable that will be set with the size
533# of the stack identified by <stack_name>.
534#
535function(superbuild_stack_size stack_name output_var)
536 get_property(_stack GLOBAL PROPERTY ${stack_name})
537 list(LENGTH _stack _stack_size)
538 set(${output_var} ${_stack_size} PARENT_SCOPE)
539endfunction()
540
541#
542# superbuild_stack_push(<stack_name> <value>)
543#
544# <stack_name> corresponds to the name of stack.
545#
546# <value> is appended to the stack identified by <stack_name>.
547#
548function(superbuild_stack_push stack_name value)
549 set_property(GLOBAL APPEND PROPERTY ${stack_name} ${value})
550endfunction()
551
552#
553# superbuild_stack_pop(<stack_name> <item_var>)
554#
555# <stack_name> corresponds to the name of stack.
556#
557# <item_var> names a CMake variable that will be set with the item
558# removed from the stack identified by <stack_name>
559#
560function(superbuild_stack_pop stack_name item_var)
561 get_property(_stack GLOBAL PROPERTY ${stack_name})
562 list(LENGTH _stack _stack_size)
563 if(_stack_size GREATER 0)
564 math(EXPR _index_to_remove "${_stack_size} - 1")
565 list(GET _stack ${_index_to_remove} _item)
566 list(REMOVE_AT _stack ${_index_to_remove})
567 set_property(GLOBAL PROPERTY ${stack_name} ${_stack})
568 set(${item_var} ${_item} PARENT_SCOPE)
569 endif()
570endfunction()
571
572function(_sb_is_optional proj output_var)
573 set(_include_project 1)
574 if(COMMAND superbuild_is_external_project_includable)
575 superbuild_is_external_project_includable("${proj}" _include_project)
576 endif()
577 set(optional 1)
578 if(_include_project)
579 set(optional 0)
580 endif()
581 set(${output_var} ${optional} PARENT_SCOPE)
582endfunction()
583
584#.rst:
585# .. cmake:function:: ExternalProject_Include_Dependencies
586#
587# .. code-block:: cmake
588#
589# ExternalProject_Include_Dependencies(<project_name>
590# [PROJECT_VAR <project_var>]
591# [EP_ARGS_VAR <external_project_args_var>]
592# [DEPENDS_VAR <depends_var>]
593# [USE_SYSTEM_VAR <use_system_var>]
594# [SUPERBUILD_VAR <superbuild_var>]
595# [CMAKE_GENERATOR <cmake_generator>]
596# [CMAKE_GENERATOR_PLATFORM <cmake_generator_platform>]
597# [CMAKE_GENERATOR_TOOLSET <cmake_generator_toolset>]
598# )
599#
600#
601# .. code-block:: cmake
602#
603# PROJECT_VAR Name of the variable containing the name of the included project.
604# By default, it is `proj` and it is set to `<project_name>`.
605#
606# EP_ARGS_VAR Name of the variable listing arguments to pass to ExternalProject.
607# If not specified, variable name default to `<project_name>_EP_ARGS`.
608#
609# DEPENDS_VAR Name of the variable containing the dependency of the included project.
610# By default, it is `<project_name>_DEPENDS`.
611#
612#
613# USE_SYSTEM_VAR Name of the variable indicating if the system version of <project_name>
614# should be looked up. Lookup of the project is left to the developer implementing
615# the external project file.
616# By default, it is `<SUPERBUILD_TOPLEVEL_PROJECT>_USE_SYSTEM_<project_name>`.
617#
618# SUPERBUILD_VAR Name of the variable indicating if the top-level or inner project is being built.
619# By default, it is `<SUPERBUILD_TOPLEVEL_PROJECT>_SUPERBUILD`.
620#
621#
622# CMAKE_GENERATOR
623# CMAKE_GENERATOR_PLATFORM
624# CMAKE_GENERATOR_TOOLSET These three options allow to overwrite the values set in the top-level project that
625# would otherwise automatically be propagated to dependent projects.
626#
627macro(ExternalProject_Include_Dependencies project_name)
628 set(options)
629 set(oneValueArgs PROJECT_VAR DEPENDS_VAR EP_ARGS_VAR USE_SYSTEM_VAR SUPERBUILD_VAR
630 CMAKE_GENERATOR
631 CMAKE_GENERATOR_PLATFORM
632 CMAKE_GENERATOR_TOOLSET
633 )
634 set(multiValueArgs)
635 cmake_parse_arguments(_sb "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
636
637 # Sanity checks
638 if(x${project_name} STREQUAL xPROJECT_VAR
639 OR x${project_name} STREQUAL xEP_ARGS_VAR
640 OR x${project_name} STREQUAL xDEPENDS_VAR
641 OR x${project_name} STREQUAL xUSE_SYSTEM_VAR
642 OR x${project_name} STREQUAL xSUPERBUILD_VAR
643 OR x${project_name} STREQUAL xCMAKE_GENERATOR
644 OR x${project_name} STREQUAL xCMAKE_GENERATOR_PLATFORM
645 OR x${project_name} STREQUAL xCMAKE_GENERATOR_TOOLSET
646 )
647 message(FATAL_ERROR "Argument <project_name> is missing !")
648 endif()
649 if(_sb_UNPARSED_ARGUMENTS)
650 message(FATAL_ERROR "Invalid arguments: ${_sb_UNPARSED_ARGUMENTS}")
651 endif()
652
653 # Set default for optional PROJECT_VAR parameter
654 if(NOT _sb_PROJECT_VAR)
655 set(_sb_PROJECT_VAR proj)
656 set(${_sb_PROJECT_VAR} ${project_name})
657 #message("[${project_name}] Setting _sb_PROJECT_VAR with default value '${_sb_PROJECT_VAR}'")
658 endif()
659
660 if(_sb_PROJECT_VAR AND NOT x${project_name} STREQUAL x${${_sb_PROJECT_VAR}})
661 message(FATAL_ERROR
662 "Argument <project_name>:${project_name} and PROJECT_VAR:${_sb_PROJECT_VAR}:${${_sb_PROJECT_VAR}} are different !")
663 endif()
664
665 set(_sb_proj ${project_name})
666
667 # Skip if project already included
668 get_property(_is_included GLOBAL PROPERTY SB_${_sb_proj}_FILE_INCLUDED)
669 if(_is_included)
670 return()
671 endif()
672
673 # Set default for optional DEPENDS_VAR and EP_ARGS parameters
674 foreach(param DEPENDS EP_ARGS)
675 if(NOT _sb_${param}_VAR)
676 set(_sb_${param}_VAR ${_sb_proj}_${param})
677 #message("[${project_name}] Setting _sb_${param}_VAR with default value '${_sb_${param}_VAR}'")
678 endif()
679 endforeach()
680
681 # Set top level project
682 superbuild_stack_size(SB_PROJECT_STACK _stack_size)
683 if(_stack_size EQUAL 0)
684 set(SUPERBUILD_TOPLEVEL_PROJECT ${_sb_proj})
685 endif()
686
687 # Set default for optional USE_SYSTEM_VAR parameter
688 if(NOT _sb_USE_SYSTEM_VAR)
689 set(_sb_USE_SYSTEM_VAR ${SUPERBUILD_TOPLEVEL_PROJECT}_USE_SYSTEM_${_sb_proj})
690 #message("[${project_name}] Setting _sb_USE_SYSTEM_VAR with default value '${_sb_USE_SYSTEM_VAR}'")
691 endif()
692
693 # Set default for optional SUPERBUILD_VAR parameter
694 if(NOT _sb_SUPERBUILD_VAR)
695 set(_sb_SUPERBUILD_VAR ${SUPERBUILD_TOPLEVEL_PROJECT}_SUPERBUILD)
696 #message("[${project_name}] Setting _sb_SUPERBUILD_VAR with default value '${_sb_SUPERBUILD_VAR}'")
697 endif()
698
699 # Set default for optional CMAKE_GENERATOR_* parameters
700 foreach(varname IN ITEMS
701 "CMAKE_GENERATOR"
702 "CMAKE_GENERATOR_PLATFORM"
703 "CMAKE_GENERATOR_TOOLSET"
704 )
705 if(NOT _sb_${varname})
706 set(_sb_${varname} ${EP_${varname}})
707 #message("[${project_name}] Setting _sb_${varname} with default value '${_sb_${varname}}'")
708 else()
709 #message("[${project_name}] Setting _sb_${varname} to value '${_sb_${varname}}'")
710 endif()
711 endforeach()
712
713 # Keeping track of variable name independently of the recursion
714 if(NOT DEFINED _sb_SB_VAR)
715 set(_sb_SB_VAR ${_sb_SUPERBUILD_VAR})
716 #message("[${project_name}] Setting _sb_SB_VAR with default value '${_sb_SB_VAR}'")
717 endif()
718
719 # Set local variables
720 set(_sb_DEPENDS ${${_sb_DEPENDS_VAR}})
721 set(_sb_USE_SYSTEM ${${_sb_USE_SYSTEM_VAR}})
722
723 _sb_update_indent(${_sb_proj})
724
725 # Keep track of the projects
726 list(APPEND SB_${SUPERBUILD_TOPLEVEL_PROJECT}_POSSIBLE_DEPENDS ${_sb_proj})
727
728 # Use system ?
729 get_property(_use_system_set GLOBAL PROPERTY SB_${_sb_proj}_USE_SYSTEM SET)
730 if(_use_system_set)
731 get_property(_sb_USE_SYSTEM GLOBAL PROPERTY SB_${_sb_proj}_USE_SYSTEM)
732 endif()
733
734 # Is this the first run ?
735 if(${_sb_proj} STREQUAL ${SUPERBUILD_TOPLEVEL_PROJECT} AND NOT DEFINED SB_FIRST_PASS)
736 message(STATUS "SuperBuild - First pass")
737 set(SB_FIRST_PASS TRUE)
738 endif()
739
740 set(_sb_REQUIRED_DEPENDS)
741 foreach(dep ${_sb_DEPENDS})
742 if(NOT ${_sb_proj} STREQUAL ${SUPERBUILD_TOPLEVEL_PROJECT})
743 # Set "use system" variable if it has NOT already been explicitly set
744 get_property(_sb_${dep}_USE_SYSTEM_VAR GLOBAL PROPERTY SB_${dep}_USE_SYSTEM_VAR)
745 if(_sb_USE_SYSTEM AND NOT DEFINED ${_sb_${dep}_USE_SYSTEM_VAR})
746 set_property(GLOBAL PROPERTY SB_${dep}_USE_SYSTEM ${_sb_USE_SYSTEM})
747 #message(${_sb_proj} "Property SB_${dep}_USE_SYSTEM set to [${_sb_USE_SYSTEM_VAR}:${_sb_USE_SYSTEM}]")
748 endif()
749 endif()
750 _sb_is_optional(${dep} _optional)
751 set_property(GLOBAL PROPERTY SB_${dep}_OPTIONAL ${_optional})
752 #message(${_sb_proj} "[${_sb_proj}] Property SB_${dep}_OPTIONAL set to ${_optional}")
753 if(NOT _optional)
754 list(APPEND _sb_REQUIRED_DEPENDS ${dep})
755 endif()
756 endforeach()
757
758 # Display dependency of project being processed
759 if(_sb_REQUIRED_DEPENDS AND SB_SECOND_PASS AND ${_sb_SB_VAR})
760 set(dependency_str "")
761 foreach(dep ${_sb_REQUIRED_DEPENDS})
762 get_property(_is_included GLOBAL PROPERTY SB_${dep}_FILE_INCLUDED)
763 set(_include_status "")
764 if(_is_included)
765 set(_include_status "[INCLUDED]")
766 endif()
767 set(dependency_str "${dependency_str}${dep}${_include_status}, ")
768 endforeach()
769 ExternalProject_Message(${_sb_proj} "${_sb_proj} => Requires ${dependency_str}")
770 endif()
771
772 # Save variables
773 set_property(GLOBAL PROPERTY SB_${_sb_proj}_REQUIRED_DEPENDS ${_sb_REQUIRED_DEPENDS})
774 set_property(GLOBAL PROPERTY SB_${_sb_proj}_DEPENDS ${_sb_DEPENDS})
775 set_property(GLOBAL PROPERTY SB_${_sb_proj}_DEPENDS_VAR ${_sb_DEPENDS_VAR})
776 set_property(GLOBAL PROPERTY SB_${_sb_proj}_EP_ARGS_VAR ${_sb_EP_ARGS_VAR})
777 set_property(GLOBAL PROPERTY SB_${_sb_proj}_USE_SYSTEM ${_sb_USE_SYSTEM})
778 set_property(GLOBAL PROPERTY SB_${_sb_proj}_USE_SYSTEM_VAR ${_sb_USE_SYSTEM_VAR})
779 set_property(GLOBAL PROPERTY SB_${_sb_proj}_PROJECT_VAR ${_sb_PROJECT_VAR})
780 foreach(varname IN ITEMS
781 "CMAKE_GENERATOR"
782 "CMAKE_GENERATOR_PLATFORM"
783 "CMAKE_GENERATOR_TOOLSET"
784 )
785 set_property(GLOBAL PROPERTY SB_${_sb_proj}_${varname} ${_sb_${varname}})
786 endforeach()
787 superbuild_stack_push(SB_PROJECT_STACK ${_sb_proj})
788
789 # Include dependencies
790 foreach(dep ${_sb_DEPENDS})
791 get_property(_included GLOBAL PROPERTY SB_${dep}_FILE_INCLUDED)
792 if(NOT _included)
793 # XXX - Refactor - Add a single variable named 'EXTERNAL_PROJECT_DIRS'
794 if(EXISTS "${EXTERNAL_PROJECT_DIR}/${EXTERNAL_PROJECT_FILE_PREFIX}${dep}.cmake")
795 include(${EXTERNAL_PROJECT_DIR}/${EXTERNAL_PROJECT_FILE_PREFIX}${dep}.cmake)
796 elseif(EXISTS "${${dep}_FILEPATH}")
797 include(${${dep}_FILEPATH})
798 elseif(EXISTS "${EXTERNAL_PROJECT_ADDITIONAL_DIR}/${EXTERNAL_PROJECT_FILE_PREFIX}${dep}.cmake")
799 include(${EXTERNAL_PROJECT_ADDITIONAL_DIR}/${EXTERNAL_PROJECT_FILE_PREFIX}${dep}.cmake)
800 else()
801 message(FATAL_ERROR "Can't find ${EXTERNAL_PROJECT_FILE_PREFIX}${dep}.cmake")
802 endif()
803 set_property(GLOBAL PROPERTY SB_${dep}_FILE_INCLUDED 1)
804 endif()
805 endforeach()
806
807 # Restore variables
808 superbuild_stack_pop(SB_PROJECT_STACK _sb_proj)
809 foreach(varname IN ITEMS
810 "CMAKE_GENERATOR"
811 "CMAKE_GENERATOR_PLATFORM"
812 "CMAKE_GENERATOR_TOOLSET"
813 )
814 get_property(_sb_${varname} GLOBAL PROPERTY SB_${_sb_proj}_${varname})
815 endforeach()
816 get_property(_sb_PROJECT_VAR GLOBAL PROPERTY SB_${_sb_proj}_PROJECT_VAR)
817 get_property(_sb_USE_SYSTEM_VAR GLOBAL PROPERTY SB_${_sb_proj}_USE_SYSTEM_VAR)
818 get_property(_sb_USE_SYSTEM GLOBAL PROPERTY SB_${_sb_proj}_USE_SYSTEM)
819 get_property(_sb_EP_ARGS_VAR GLOBAL PROPERTY SB_${_sb_proj}_EP_ARGS_VAR)
820 get_property(_sb_DEPENDS_VAR GLOBAL PROPERTY SB_${_sb_proj}_DEPENDS_VAR)
821 get_property(_sb_DEPENDS GLOBAL PROPERTY SB_${_sb_proj}_DEPENDS)
822 get_property(_sb_REQUIRED_DEPENDS GLOBAL PROPERTY SB_${_sb_proj}_REQUIRED_DEPENDS)
823
824 # Use system ?
825 set(_include_type "")
826 if(_sb_USE_SYSTEM)
827 set(_include_type " (SYSTEM)")
828 endif()
829 get_property(_optional GLOBAL PROPERTY SB_${_sb_proj}_OPTIONAL)
830 ExternalProject_Message(${_sb_proj} "${_sb_proj}[OK]${_include_type}" SB_SECOND_PASS AND ${_sb_SB_VAR} AND NOT _optional)
831
832 if(${_sb_proj} STREQUAL ${SUPERBUILD_TOPLEVEL_PROJECT} AND SB_FIRST_PASS)
833 set(SB_FIRST_PASS FALSE)
834 ExternalProject_Message(${_sb_proj} "First pass - done")
835
836 if(${_sb_SB_VAR})
837 foreach(possible_proj ${SB_${SUPERBUILD_TOPLEVEL_PROJECT}_POSSIBLE_DEPENDS})
838 get_property(_optional GLOBAL PROPERTY SB_${possible_proj}_OPTIONAL)
839 if(_optional)
840 ExternalProject_Message(${_sb_proj} "${possible_proj}[OPTIONAL]")
841 endif()
842 set_property(GLOBAL PROPERTY SB_${possible_proj}_FILE_INCLUDED 0)
843 endforeach()
844
845 set(${_sb_PROJECT_VAR} ${_sb_proj})
846
847 set(SB_SECOND_PASS TRUE)
848 set(_ep_include_deps_EXTRA_ARGS )
849 foreach(varname IN ITEMS
850 "CMAKE_GENERATOR"
851 "CMAKE_GENERATOR_PLATFORM"
852 "CMAKE_GENERATOR_TOOLSET"
853 )
854 list(APPEND _ep_include_deps_EXTRA_ARGS
855 ${varname} ${_sb_${varname}}
856 )
857 endforeach()
858 ExternalProject_Include_Dependencies(${_sb_proj}
859 PROJECT_VAR ${_sb_PROJECT_VAR}
860 DEPENDS_VAR ${_sb_DEPENDS_VAR}
861 EP_ARGS_VAR ${_sb_EP_ARGS_VAR}
862 USE_SYSTEM_VAR _sb_USE_SYSTEM
863 SUPERBUILD_VAR ${_sb_SB_VAR}
864 ${_ep_include_deps_EXTRA_ARGS}
865 )
866 set(SB_SECOND_PASS FALSE)
867 endif()
868 endif()
869
870 if(SB_FIRST_PASS OR _optional)
871 if(NOT ${_sb_proj} STREQUAL ${SUPERBUILD_TOPLEVEL_PROJECT})
872 return()
873 endif()
874 endif()
875
876 if(SB_SECOND_PASS)
877 _sb_get_external_project_arguments(${_sb_proj} ${_sb_EP_ARGS_VAR})
878 endif()
879
880 if(NOT SB_FIRST_PASS AND NOT SB_SECOND_PASS
881 AND ${_sb_proj} STREQUAL ${SUPERBUILD_TOPLEVEL_PROJECT})
882 #ExternalProject_Message(${_sb_proj} "Clean up")
883 unset(_sb_SB_VAR)
884 unset(SB_FIRST_PASS)
885 unset(SB_SECOND_PASS)
886 endif()
887
888 # Set public variables
889 set(${_sb_PROJECT_VAR} ${_sb_proj})
890 set(${_sb_DEPENDS_VAR} ${_sb_REQUIRED_DEPENDS})
891 set(${_sb_USE_SYSTEM_VAR} ${_sb_USE_SYSTEM})
892
893 #message("[${_sb_proj}] #################################")
894 #message("[${_sb_proj}] Setting ${_sb_PROJECT_VAR}:${_sb_proj}")
895 #message("[${_sb_proj}] Setting ${_sb_EP_ARGS_VAR}:${${_sb_EP_ARGS_VAR}}")
896 #message("[${_sb_proj}] Setting ${_sb_DEPENDS_VAR}:${${_sb_DEPENDS_VAR}}")
897 #message("[${_sb_proj}] Setting ${_sb_USE_SYSTEM_VAR}:${_sb_USE_SYSTEM}")
898endmacro()
899
900#.rst:
901# .. cmake:function:: ExternalProject_Add_Empty
902#
903# .. code-block:: cmake
904#
905# ExternalProject_Add_Empty(<project_name>
906# DEPENDS <depends>
907# )
908#
909macro(ExternalProject_Add_Empty project_name)
910 set(options)
911 set(oneValueArgs)
912 set(multiValueArgs DEPENDS)
913 cmake_parse_arguments(_sb "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
914
915 # Sanity checks
916 if(x${project_name} STREQUAL xDEPENDS)
917 message(FATAL_ERROR "Argument <project_name> is missing !")
918 endif()
919 if(_sb_UNPARSED_ARGUMENTS)
920 message(FATAL_ERROR "Invalid arguments: ${_sb_UNPARSED_ARGUMENTS}")
921 endif()
922
923 ExternalProject_Add(${project_name}
924 SOURCE_DIR ${CMAKE_BINARY_DIR}/${project_name}
925 BINARY_DIR ${project_name}-build
926 DOWNLOAD_COMMAND ""
927 CONFIGURE_COMMAND ""
928 BUILD_COMMAND ""
929 INSTALL_COMMAND ""
930 DEPENDS ${_sb_DEPENDS}
931 )
932endmacro()
933
934#.rst:
935# .. cmake:function:: ExternalProject_Install_CMake
936#
937# Install an external CMake-based project as part of the ``install`` target.
938#
939# .. code-block:: cmake
940#
941# ExternalProject_Install_CMake(<project_name>)
942#
943# This causes building the main project's ``install`` target to also execute
944# the CMake install script for the specified external project. The project must
945# be previously declared with :command:`ExternalProject_Add`.
946#
947function(ExternalProject_Install_CMake project_name)
948 ExternalProject_Get_Property(${project_name} binary_dir)
949
950 install(SCRIPT ${binary_dir}/cmake_install.cmake)
951endfunction()
952
953#.rst:
954# .. cmake:function:: ExternalProject_SetIfNotDefined
955#
956# Set a variable to its default value if not already defined.
957#
958# .. code-block:: cmake
959#
960# ExternalProject_SetIfNotDefined(<var> <defaultvalue> [OBFUSCATE] [QUIET])
961#
962# The default value is set with:
963# (1) if set, the value environment variable <var>.
964# (2) if set, the value of local variable variable <var>.
965# (3) if none of the above, the value passed as a parameter.
966#
967# Setting the optional parameter 'OBFUSCATE' will display 'OBFUSCATED' instead of the real value.
968# Setting the optional parameter 'QUIET' will not display any message.
969macro(ExternalProject_SetIfNotDefined var defaultvalue)
970 set(_obfuscate FALSE)
971 set(_quiet FALSE)
972 foreach(arg ${ARGN})
973 if(arg STREQUAL "OBFUSCATE")
974 set(_obfuscate TRUE)
975 endif()
976 if(arg STREQUAL "QUIET")
977 set(_quiet TRUE)
978 endif()
979 endforeach()
980 if(DEFINED ENV{${var}} AND NOT DEFINED ${var})
981 set(_value "$ENV{${var}}")
982 if(_obfuscate)
983 set(_value "OBFUSCATED")
984 endif()
985 if(NOT _quiet)
986 message(STATUS "Setting '${var}' variable with environment variable value '${_value}'")
987 endif()
988 set(${var} $ENV{${var}})
989 endif()
990 if(NOT DEFINED ${var})
991 set(_value "${defaultvalue}")
992 if(_obfuscate)
993 set(_value "OBFUSCATED")
994 endif()
995 if(NOT _quiet)
996 message(STATUS "Setting '${var}' variable with default value '${_value}'")
997 endif()
998 set(${var} "${defaultvalue}")
999 endif()
1000endmacro()
1001
1002#.rst:
1003# .. cmake:function:: ExternalProject_AlwaysConfigure
1004#
1005# Add a external project step named `forceconfigure` to `project_name` ensuring
1006# the project will always be reconfigured.
1007#
1008# .. code-block:: cmake
1009#
1010# ExternalProject_AlwaysConfigure(<project_name>)
1011function(ExternalProject_AlwaysConfigure proj)
1012 # This custom external project step forces the configure and later
1013 # steps to run.
1014 _ep_get_step_stampfile(${proj} "configure" stampfile)
1015 ExternalProject_Add_Step(${proj} forceconfigure
1016 COMMAND ${CMAKE_COMMAND} -E remove ${stampfile}
1017 COMMENT "Forcing configure step for '${proj}'"
1018 DEPENDEES build
1019 ALWAYS 1
1020 )
1021endfunction()