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