1# ============================================================================
2# Copyright (c) 2011-2012 University of Pennsylvania
3# Copyright (c) 2013-2014 Carnegie Mellon University
4# Copyright (c) 2013-2016 Andreas Schuh
5# All rights reserved.
6#
7# See COPYING file for license information or visit
8# https://cmake-basis.github.io/download.html#license
9# ============================================================================
10
11##############################################################################
12# @file  CommonTools.cmake
13# @brief Definition of common CMake functions.
14#
15# @ingroup CMakeTools
16##############################################################################
17
18if (__BASIS_COMMONTOOLS_INCLUDED)
19  return ()
20else ()
21  set (__BASIS_COMMONTOOLS_INCLUDED TRUE)
22endif ()
23
24
25include (CMakeParseArguments)
26
27
28## @addtogroup CMakeUtilities
29#  @{
30
31
32# ============================================================================
33# find other packages
34# ============================================================================
35
36# ----------------------------------------------------------------------------
37## @brief Overloaded find_package() command.
38#
39# This macro calls CMake's
40# <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package">
41# find_package()</a> command and converts obsolete all uppercase "<PKG>_<VAR>"
42# variables to case-sensitive "<Pkg>_<VAR>" variables.
43# It further ensures that the global variables CMAKE_FIND_LIBRARY_SUFFIXES
44# and CMAKE_FIND_EXECUTABLE_SUFFIX are reset to the values they had before
45# the call to find_package(). This is required if the "Find<Pkg>.cmake" module
46# has modified these variables, but not restored their initial value.
47macro (find_package)
48  if (BASIS_DEBUG)
49    message ("** find_package(${ARGV})")
50  endif ()
51  # attention: find_package() can be recursive. Hence, use "stack" to keep
52  #            track of library suffixes. Further note that we need to
53  #            maintain a list of lists, which is not supported by CMake.
54  list (APPEND _BASIS_FIND_LIBRARY_SUFFIXES "{${CMAKE_FIND_LIBRARY_SUFFIXES}}")
55  list (APPEND _BASIS_FIND_EXECUTABLE_SUFFIX "${CMAKE_FIND_EXECUTABLE_SUFFIX}")
56  _find_package(${ARGV})
57  # map common uppercase <PKG>_* variables to case-preserving <Pkg>_*
58  string (TOUPPER "${ARGV0}" _FP_ARGV0_U)
59  foreach (_FP_VAR IN ITEMS FOUND DIR USE_FILE
60                            VERSION VERSION_STRING
61                            VERSION_MAJOR VERSION_MINOR VERSION_SUBMINOR VERSION_PATCH
62                            MAJOR_VERSION MINOR_VERSION SUBMINOR_VERSION PATCH_VERSION
63                            INCLUDE_DIR INCLUDE_DIRS INCLUDE_PATH
64                            LIBRARY_DIR LIBRARY_DIRS LIBRARY_PATH
65                            EXECUTABLE COMPILER CONVERTER)
66    if (NOT ${ARGV0}_${_FP_VAR} AND DEFINED ${_FP_ARGV0_U}_${_FP_VAR})
67      set (${ARGV0}_${_FP_VAR} "${${_FP_ARGV0_U}_${_FP_VAR}}")
68    endif ()
69  endforeach ()
70  unset (_FP_VAR)
71  unset (_FP_ARGV0_U)
72  # restore CMAKE_FIND_LIBRARY_SUFFIXES
73  string (REGEX REPLACE ";?{([^}]*)}$" "" _BASIS_FIND_LIBRARY_SUFFIXES "${_BASIS_FIND_LIBRARY_SUFFIXES}")
74  set (CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_MATCH_1}")
75  # restore CMAKE_FIND_EXECUTABLE_SUFFIX
76  list (LENGTH _BASIS_FIND_EXECUTABLE_SUFFIX _FP_LAST)
77  if (_FP_LAST GREATER 0)
78    math (EXPR _FP_LAST "${_FP_LAST} - 1")
79    list (REMOVE_AT _BASIS_FIND_EXECUTABLE_SUFFIX ${_FP_LAST})
80  endif ()
81  unset (_FP_LAST)
82endmacro ()
83
84# ----------------------------------------------------------------------------
85## @brief Tokenize dependency specification.
86#
87# This function parses a dependency specification such as
88# "ITK-4.1{TestKernel,IO}" into the package name, i.e., ITK, the requested
89# (minimum) package version(s), i.e., 4.1, and a list of package components, i.e.,
90# TestKernel and IO. A valid dependency specification must specify the package
91# name of the dependency (case-sensitive). The version and components
92# specification are optional. Note that the components specification may
93# be separated by an arbitrary number of whitespace characters including
94# newlines. The same applies to the specification of the components themselves.
95# This allows one to format the dependency specification as follows, for example:
96# @code
97# ITK {
98#   TestKernel,
99#   IO
100# }
101# @endcode
102#
103# VTK-7|6{}
104#
105# @param [in]  DEP Dependency specification, i.e., "<Pkg>[-<version>[|...]][{<Component1>[,...]}]".
106# @param [out] PKG Package name.
107# @param [out] VER Package version(s).
108# @param [out] CMP List of package components.
109function (basis_tokenize_dependency DEP PKG VER CMP)
110  set (CMPS)
111  if (DEP MATCHES "^([^ ]+)[ \\n\\t]*{([^}]*)}$")
112    set (DEP "${CMAKE_MATCH_1}")
113    string (REPLACE "," ";" COMPONENTS "${CMAKE_MATCH_2}")
114    foreach (C IN LISTS COMPONENTS)
115      string (STRIP "${C}" C)
116      list (APPEND CMPS ${C})
117    endforeach ()
118  endif ()
119  if (DEP MATCHES "^(.*)-([0-9]+(\\.[0-9]+)?(\\.[0-9]+)?(\\|[0-9]+(\\.[0-9]+)?(\\.[0-9]+)?)*)$")
120    string (REPLACE "|" ";" CMAKE_MATCH_2 "${CMAKE_MATCH_2}")
121    set (${PKG} "${CMAKE_MATCH_1}" PARENT_SCOPE)
122    set (${VER} "${CMAKE_MATCH_2}" PARENT_SCOPE)
123  else ()
124    set (${PKG} "${DEP}" PARENT_SCOPE)
125    set (${VER} ""       PARENT_SCOPE)
126  endif ()
127  set (${CMP} "${CMPS}" PARENT_SCOPE)
128endfunction ()
129
130## @brief Get installation prefix given path of <PKG>Config directory
131function (_basis_config_to_prefix_dir PKG PKG_DIR PREFIX)
132  if (PKG_DIR)
133    if (APPLE)
134      if (PKG_DIR MATCHES "^(.*)/([^/]+)\\.(framework|app)(/|$)")
135        set (${PREFIX} "${CMAKE_MATCH_1}/${CMAKE_MATCH_2}.${CMAKE_MATCH_3}" PARENT_SCOPE)
136        return ()
137      endif ()
138    endif ()
139    string (REGEX REPLACE "/+(cmake|CMake)/*$" "" prefix "${PKG_DIR}")
140    set (SUBDIR_RE "/+(lib[0-9]*(/[^/]+)?|share)(/+cmake)?")
141    if (UNIX AND prefix MATCHES "${SUBDIR_RE}(/+[^/]+|/*$)")
142      get_filename_component (subdir "${prefix}" NAME)
143      string (TOLOWER "${subdir}" subdir)
144      string (TOLOWER "${PKG}"    pkg)
145      if ("^${subdir}$" STREQUAL "^${pkg}$")
146        get_filename_component (prefix "${prefix}" PATH)
147      endif ()
148      string(REGEX REPLACE "${SUBDIR_RE}/*$" "" prefix "${prefix}")
149    endif ()
150  else ()
151    set(prefix "NOTFOUND")
152  endif ()
153  set(${PREFIX} "${prefix}" PARENT_SCOPE)
154endfunction ()
155
156# ----------------------------------------------------------------------------
157## @brief Find external software package or other project module.
158#
159# This macro replaces CMake's
160# <a href="https://cmake.org/cmake/help/v2.8.12/cmake.html#command:find_package">
161# find_package()</a> command and extends its functionality.
162# In particular, if the given package name is the name of another module
163# of this project (the top-level project), it ensures that this module is
164# found instead of an external package.
165#
166# If the package is found, but only optionally used, i.e., the @c REQUIRED
167# argument was not given to this macro, a <tt>WITH_&lt;PACKAGE&gt;</tt> option
168# is added by this macro which is by default @c OFF. This option can be set to
169# @c ON by the user in order to require the discovery of the package.
170# When this option is OFF, the (non-cached) <tt>USE_&lt;PACKAGE&gt;</tt> variable
171# can be used by project developers to still request the discovery of the
172# optional package, but no error is raised when the package is not found.
173# This allows a project to use an optional dependency when an installation
174# is found regardless of the <tt>WITH_&lt;PACKAGE&gt;</tt> option. Note
175# that when <tt>USE_&lt;PACKAGE&gt;</tt> is defined, no <tt>WITH_&lt;PACKAGE&gt;</tt>
176# entry is added by this macro to the cache.
177#
178# @param [in] PACKAGE Name of software package or other project module.
179#                     Optionally, the package name can include a version
180#                     specification as suffix which is separated from the
181#                     package name using a dash (-), i.e., &lt;Package&gt;[-major[.minor[.patch[.tweak]]]].
182#                     Multiple alternative versions have to be separated by
183#                     a pipe character "|", the logical OR.
184#                     If a version specification is given, it is passed on as
185#                     @c version argument to CMake's
186#                     <a href="https://cmake.org/cmake/help/v2.8.12/cmake.html#command:find_package">
187#                     find_package()</a> command. The discovery of multiple
188#                     alternative versions is only supported for the CONFIG
189#                     mode of the find_package command.
190# @param [in] ARGN    Additional arguments for
191#                     <a href="https://cmake.org/cmake/help/v2.8.12/cmake.html#command:find_package">
192#                     find_package()</a>.
193#
194# @retval &lt;PACKAGE&gt;_FOUND            Whether the given package was found.
195# @retval &lt;PACKAGE&gt;_COMPONENTS_FOUND Names of found components.
196#                                          Optional components are only included when
197#                                          "<PACKAGE>_<COMPONENT>_FOUND" is set to @c TRUE
198#                                          for each found component by the find_package call,
199#                                          i.e., either the "Find<PACKAGE>" module or the
200#                                          "<PACKAGE>Config" file.
201#
202# @sa https://cmake.org/cmake/help/v2.8.12/cmake.html#command:find_package
203#
204# @ingroup CMakeAPI
205macro (basis_find_package PACKAGE)
206  # Note that this MUST be a macro such that non-cached variables
207  # set by find_package are set within the callers scope
208  # ------------------------------------------------------------------------
209  # parse arguments
210  set (_BFP_OPTIONS
211    QUIET
212    REQUIRED
213    MODULE
214    NO_MODULE
215    CONFIG
216    NO_NOTFOUND_ERROR
217  )
218  set (_BFP_MULTI_ARGS
219    COMPONENTS
220    OPTIONAL_COMPONENTS
221  )
222  cmake_parse_arguments (
223    _BFP_ARGN
224      "${_BFP_OPTIONS}"
225      ""
226      "${_BFP_MULTI_ARGS}"
227    ${ARGN}
228  )
229  # ------------------------------------------------------------------------
230  # tokenize dependency specification
231  basis_tokenize_dependency ("${PACKAGE}" PKG _BFP_VERSIONS _BFP_COMPONENTS)
232  list (GET _BFP_ARGN_UNPARSED_ARGUMENTS 0 _BFP_VERSION)
233  if (_BFP_VERSION MATCHES "^[0-9]+(\\.[0-9]+)*(\\|[0-9]+(\\.[0-9]+)*)*$")
234    if (_BFP_VERSIONS)
235      message (FATAL_ERROR "Cannot use both version specification as part of "
236                           "package name and explicit version argument.")
237    endif ()
238    list (REMOVE_AT _BFP_ARGN_UNPARSED_ARGUMENTS 0)
239    string(REPLACE "|" ";" _BFP_VERSIONS ${_BFP_VERSION})
240  endif ()
241  list (LENGTH _BFP_VERSIONS _BFP_VERSIONS_COUNT)
242  if (_BFP_ARGN_MODULE AND _BFP_VERSIONS GREATER 1)
243    message (FATAL_ERROR "Cannot look for multiple alternative package versions"
244                         " in MODULE mode. The CONFIG|NO_MODULE mode of find_package"
245                         " is used in this case. When MODULE mode is required"
246                         " by package ${PKG}, only one version can be specified.")
247  endif ()
248  string (TOLOWER "${PKG}" PKG_L)
249  string (TOUPPER "${PKG}" PKG_U)
250  # ------------------------------------------------------------------------
251  # set <PKG>_FIND_REQUIRED_<CMP>
252  foreach (_BFP_CMP IN LISTS _BFP_COMPONENTS)
253    set (${PKG}_FIND_REQUIRED_${_BFP_CMP} ${_BFP_ARGN_REQUIRED})
254  endforeach ()
255  foreach (_BFP_CMP IN LISTS _BFP_ARGN_COMPONENTS)
256    set (${PKG}_FIND_REQUIRED_${_BFP_CMP} TRUE)
257  endforeach ()
258  foreach (_BFP_CMP IN LISTS _BFP_ARGN_OPTIONAL_COMPONENTS)
259    set (${PKG}_FIND_REQUIRED_${_BFP_CMP} FALSE)
260  endforeach ()
261  list (APPEND _BFP_ARGN_COMPONENTS ${_BFP_COMPONENTS})
262  if (_BFP_ARGN_COMPONENTS)
263    list (REMOVE_DUPLICATES _BFP_ARGN_COMPONENTS)
264  endif ()
265  # ------------------------------------------------------------------------
266  # prefix of package variable names set by Find<Pkg> or <Pkg>Config
267  if (PKG MATCHES "^((P|J)ython)Interp$")
268    string (TOUPPER "${CMAKE_MATCH_1}" _BFP_NS)
269  else ()
270    set (_BFP_NS "${PKG}")
271  endif ()
272  # ------------------------------------------------------------------------
273  # some debugging output
274  if (BASIS_DEBUG)
275    set (_BFP_ARGS)
276    if (_BFP_ARGN_REQUIRED)
277      list (APPEND _BFP_ARGS REQUIRED)
278    endif ()
279    if (_BFP_ARGN_QUIET)
280      list (APPEND _BFP_ARGS QUIET)
281    endif ()
282    if (_BFP_ARGN_MODULE)
283      list (APPEND _BFP_ARGS MODULE)
284    endif ()
285    if (_BFP_ARGN_NO_MODULE OR _BFP_ARGN_CONFIG)
286      list (APPEND _BFP_ARGS CONFIG)
287    endif ()
288    list (APPEND _BFP_ARGS ${_BFP_ARGN_UNPARSED_ARGUMENTS})
289    set (_BFP_INFO "** basis_find_package()")
290    set (_BFP_INFO "${_BFP_INFO}\n**     Package:    ${PKG}")
291    if (_BFP_VERSIONS)
292    set (_BFP_INFO "${_BFP_INFO}\n**     Versions:   ${_BFP_VERSIONS}")
293    endif ()
294    set (_BFP_INFO "${_BFP_INFO}\n**     Arguments:  [${_BFP_ARGS}]")
295    if (_BFP_ARGN_COMPONENTS OR _BFP_ARGN_OPTIONAL_COMPONENTS)
296    set (_BFP_INFO "${_BFP_INFO}\n**     Components: ")
297      if (_BFP_ARGN_COMPONENTS AND _BFP_ARGN_OPTIONAL_COMPONENTS)
298        set (_BFP_INFO "${_BFP_INFO}[${_BFP_ARGN_COMPONENTS}] and [${_BFP_ARGN_OPTIONAL_COMPONENTS}] (optional)")
299      elseif (_BFP_ARGN_COMPONENTS)
300        set (_BFP_INFO "${_BFP_INFO}[${_BFP_ARGN_COMPONENTS}]")
301      else ()
302        set (_BFP_INFO "${_BFP_INFO}[${_BFP_ARGN_OPTIONAL_COMPONENTS}] (optional)")
303      endif ()
304    endif ()
305    message ("${_BFP_INFO}")
306    unset (_BFP_INFO)
307    unset (_BFP_ARGS)
308  endif ()
309  # ------------------------------------------------------------------------
310  # find other modules of same project
311  set (_BFP_IS_PROJECT FALSE)
312  set (_BFP_IS_MODULE  FALSE)
313  if (PROJECT_IS_MODULE)
314    # allow modules to specify top-level project as dependency,
315    # respectively, other modules as components of top-level project
316    if ("^${PKG}$" STREQUAL "^${TOPLEVEL_PROJECT_NAME}$")
317      if (_BFP_ARGN_COMPONENTS OR _BFP_ARGN_OPTIONAL_COMPONENTS)
318        if (BASIS_DEBUG)
319          message ("**     This is the top-level project. Components must be other modules of this project.")
320        endif ()
321        foreach (_BFP_CMP IN LISTS _BFP_ARGN_COMPONENTS _BFP_ARGN_OPTIONAL_COMPONENTS)
322          list (FIND PROJECT_MODULES "${_BFP_CMP}" _BFP_CMPIDX)
323          if (_BFP_CMPIDX EQUAL -1)
324            message (FATAL_ERROR "Module ${PROJECT_NAME} has module ${_BFP_CMP} of top-level project ${PKG}"
325                                 " as dependency, but no such module exists.")
326          endif ()
327          list (FIND PROJECT_MODULES_ENABLED "${_BFP_CMP}" _BFP_CMPIDX)
328          if (_BFP_CMPIDX EQUAL -1)
329            if (BASIS_DEBUG)
330              message ("**     Identified it as disabled module of this project.")
331            endif ()
332            if (${PKG}_FIND_REQUIRED_${_BFP_CMP})
333              if (_BFP_ARGN_REQUIRED)
334                message (FATAL_ERROR
335                  "Module ${PROJECT_NAME} requires module ${_BFP_CMP} of top-level project ${PKG},"
336                  " but module ${_BFP_CMP} is not enabled."
337                )
338              elseif (NOT _BFP_ARGN_QUIET)
339                message (STATUS
340                  "Module ${PROJECT_NAME} optionally uses module ${_BFP_CMP} of top-level project ${PKG},"
341                  " but module ${_BFP_CMP} is not enabled."
342                )
343              endif ()
344            endif ()
345            set (${PKG}_${_BFP_CMP}_FOUND FALSE)
346          else ()
347            if (BASIS_DEBUG)
348              message ("**     Identified it as enabled module of this project.")
349            endif ()
350            include ("${BINARY_LIBCONF_DIR}/${TOPLEVEL_PROJECT_PACKAGE_CONFIG_PREFIX}${_BFP_CMP}Config.cmake")
351            set (${PKG}_${_BFP_CMP}_FOUND TRUE)
352          endif ()
353        endforeach ()
354      else ()
355        if (BASIS_DEBUG)
356          message ("**     This is the top-level project.")
357        endif ()
358      endif ()
359      set (${PKG}_FOUND    TRUE)
360      set (_BFP_IS_PROJECT TRUE)
361    # look for other module of top-level project
362    else ()
363      list (FIND PROJECT_MODULES "${PKG}" _BFP_PKGIDX)
364      if (NOT _BFP_PKGIDX EQUAL -1)
365        set (_BFP_IS_MODULE TRUE)
366        list (FIND PROJECT_MODULES_ENABLED "${PKG}" _BFP_PKGIDX)
367        if (_BFP_PKGIDX EQUAL -1)
368          if (BASIS_DEBUG)
369            message ("**     Identified it as disable module of this project.")
370          endif ()
371          if (_BFP_ARGN_REQUIRED)
372            message (FATAL_ERROR
373              "Module ${PROJECT_NAME} requires module ${_BFP_CMP} of top-level project ${PKG},"
374              " but module ${_BFP_CMP} is not enabled."
375            )
376          elseif (NOT _BFP_ARGN_QUIET)
377            message (STATUS
378              "Module ${PROJECT_NAME} optionally uses module ${_BFP_CMP} of top-level project ${PKG},"
379              " but module ${_BFP_CMP} is not enabled."
380            )
381          endif ()
382          set (${PKG}_FOUND FALSE)
383        else ()
384          if (BASIS_DEBUG)
385            message ("**     Identified it as enabled module of this project.")
386          endif ()
387          include ("${BINARY_LIBCONF_DIR}/${TOPLEVEL_PROJECT_PACKAGE_CONFIG_PREFIX}${PKG}Config.cmake")
388          set (${PKG}_FOUND TRUE)
389        endif ()
390      endif ()
391    endif ()
392  # --------------------------------------------------------------------------
393  # find bundled packages
394  else ()
395    list (FIND BUNDLE_PROJECTS "${PKG}" _BFP_PKGIDX)
396    if (NOT _BFP_PKGIDX EQUAL -1)
397      if  (EXISTS "${CMAKE_INSTALL_PREFIX}/${INSTALL_CONFIG_DIR}/${PKG}Config.cmake")
398        set (_BFP_CONFIG_FILE "${CMAKE_INSTALL_PREFIX}/${INSTALL_CONFIG_DIR}/${PKG}Config.cmake")
399      else ()
400        if (EXISTS "${CMAKE_INSTALL_PREFIX}/${INSTALL_CONFIG_DIR}/${PKG_L}-config.cmake")
401          set (_BFP_CONFIG_FILE "${CMAKE_INSTALL_PREFIX}/${INSTALL_CONFIG_DIR}/${PKG_L}-config.cmake")
402        else ()
403          set (_BFP_CONFIG_FILE)
404        endif ()
405      endif ()
406      if (_BFP_CONFIG_FILE)
407        if (BASIS_DEBUG)
408          message ("**     Identified it as other package of this bundle.")
409        endif ()
410        get_filename_component (_BFP_CONFIG_DIR "${_BFP_CONFIG_FILE}" PATH)
411        _basis_config_to_prefix_dir (${PKG} "${_BFP_CONFIG_DIR}" _BFP_PREFIX)
412        basis_set_or_update_value (DEPENDS_${PKG}_DIR "${_BFP_PREFIX}" PATH)
413        include ("${_BFP_CONFIG_FILE}")
414        set (${PKG}_FOUND TRUE)
415        unset (_BFP_CONFIG_DIR)
416      endif ()
417      unset (_BFP_CONFIG_FILE)
418    endif ()
419  endif ()
420  # --------------------------------------------------------------------------
421  # otherwise, look for external package
422  if (NOT _BFP_IS_PROJECT AND NOT _BFP_IS_MODULE)
423    # ------------------------------------------------------------------------
424    # provide option which allows users to request use of optional packages
425    #
426    # - WITH_<PKG>  == ON:  optional dependency is required
427    # - WITH_<PKG>  == OFF:
428    #   - USE_<PKG> == ON:  dependency is looked for and used if available
429    #   - USE_<PKG> == OFF: dependency is ignored
430    #
431    # The default of the WITH_<PKG> option is OFF unless WITH_<PKG>_DEFAULT
432    # is set in the Depends.cmake, the CMake command-line using the -D switch
433    # or the root CMakeLists.txt file before the basis_project_begin call.
434    # The (uncached) WITH_${PKG}_DEFAULT variable can be used by a project
435    # to require optional dependencies by default, e.g., to enable optional
436    # program features that depend on these external packages.
437    if (NOT _BFP_ARGN_REQUIRED)
438      if (NOT DEFINED WITH_${PKG}_DEFAULT)
439        set (WITH_${PKG}_DEFAULT OFF)
440      endif ()
441      if (DEFINED USE_${PKG} AND NOT DEFINED WITH_${PKG})
442        set (WITH_${PKG} ${WITH_${PKG}_DEFAULT})
443      else ()
444        option (WITH_${PKG} "Build with optional ${PKG} dependency" ${WITH_${PKG}_DEFAULT})
445      endif ()
446    endif ()
447    # look for external package only if required, built with optional dependency
448    # enabled by user (cf. WITH_<PKG> option above) or deprecated -DUSE_<PKG>=ON
449    if (_BFP_ARGN_REQUIRED OR WITH_${PKG} OR USE_${PKG})
450      # ----------------------------------------------------------------------
451      # Use more user friendly hybrid DEPENDS_<PKG>_DIR cache variable which
452      # allows grouping of DEPENDS paths cache entry, but still consider more
453      # common variables named <PKG>_DIR, <PKG>_ROOT, <PKG>ROOT, or <PKG>_ROOT_DIR
454      # set in the user shell environment or on the CMake command-line using -D.
455      set (
456        DEPENDS_${PKG}_DIR "${DEPENDS_${PKG}_DIR}" CACHE PATH
457        "Top-level installation directory of ${PKG} or directory containing ${PKG}Config.cmake file."
458      )
459      if (DEPENDS_${PKG}_DIR)
460        file (TO_CMAKE_PATH "${DEPENDS_${PKG}_DIR}" _BFP_PREFIX)
461        if (NOT "^${DEPENDS_${PKG}_DIR}$" STREQUAL "^${_BFP_PREFIX}$")
462          basis_update_value (DEPENDS_${PKG}_DIR "${_BFP_PREFIX}")
463        endif ()
464      endif ()
465      # Names of considered alternative find search path variables excl. <PKG>_DIR
466      set (_BFP_PKG_DIR_VARS
467        ${PKG}_ROOT     ${PKG_U}_ROOT
468        ${PKG}ROOT      ${PKG_U}ROOT
469        ${PKG}_ROOT_DIR ${PKG_U}_ROOT_DIR
470      )
471      list (REMOVE_DUPLICATES _BFP_PKG_DIR_VARS)
472      # Override DEPENDS_<PKG>_DIR by alternative search path variable value if these
473      # were specified on the command line using the -D option. Note that these variables
474      # cannot be set in the CMake GUI because their type is changed here to INTERNAL.
475      # This has two reasons, firstly to not have duplicate variables with different
476      # names for the same purpose, and secondly to be able to recognize when their
477      # value is changed using the -D command line option of the cmake command.
478      #
479      # Order of precedence:
480      # 1. <PKG>_DIR
481      # 2. <PKG>_ROOT... CMake variable
482      # 3. <PKG>_ROOT... environment variable
483      foreach (_BFP_VAR IN LISTS _BFP_PKG_DIR_VARS)
484        file (TO_CMAKE_PATH "${${_BFP_VAR}}" _BFP_PREFIX) # CMake (cache) variable
485        if (_BFP_PREFIX)
486          # first configure run or new value specified using -D option of cmake command
487          if (NOT DEFINED _DEPENDS_${PKG}_DIR OR (DEFINED _DEPENDS_${PKG}_DIR AND NOT "^${_BFP_PREFIX}$" STREQUAL "^${_DEPENDS_${PKG}_DIR}$"))
488            basis_update_value (DEPENDS_${PKG}_DIR "${_BFP_PREFIX}")
489            break ()
490          endif ()
491        endif ()
492      endforeach ()
493      if (${PKG}_DIR) # find_package CONFIG mode variable
494        # first configure run or new value specified using -D option of cmake command
495        if (NOT DEFINED _${PKG}_DIR OR (DEFINED _${PKG}_DIR AND NOT "^${${PKG}_DIR}$" STREQUAL "^${_${PKG}_DIR}$"))
496          file (TO_CMAKE_PATH "${${PKG}_DIR}" _BFP_PREFIX)
497          _basis_config_to_prefix_dir(${PKG} "${_BFP_PREFIX}" _BFP_PREFIX)
498          basis_update_value (DEPENDS_${PKG}_DIR "${_BFP_PREFIX}")
499        endif ()
500      endif ()
501      # mark alternatives as internal cache entries
502      foreach (_BFP_VAR IN LISTS _BFP_PKG_DIR_VARS)
503        basis_is_cached (_BFP_CACHED ${_BFP_VAR})
504        if (_BFP_CACHED)
505          basis_update_type_of_variable (${_BFP_VAR} INTERNAL)
506        endif ()
507      endforeach ()
508      # if still not set, use common environment variables to set DEPENDS_<PKG>_DIR
509      if (NOT DEPENDS_${PKG}_DIR)
510        foreach (_BFP_VAR IN LISTS _BFP_PKG_DIR_VARS)
511          file (TO_CMAKE_PATH "$ENV{${_BFP_VAR}}" _BFP_PREFIX) # shell environment variable
512          if (_BFP_PREFIX)
513            basis_update_value (DEPENDS_${PKG}_DIR "${_BFP_PREFIX}")
514            break()
515          endif ()
516        endforeach ()
517      endif ()
518      # circumvent issue with find_package interpreting <PKG>_DIR relative
519      # to the current binary directory instead of the top-level directory
520      if (DEPENDS_${PKG}_DIR AND NOT IS_ABSOLUTE "${DEPENDS_${PKG}_DIR}")
521        get_filename_component (_BFP_PREFIX "${CMAKE_BINARY_DIR}/${DEPENDS_${PKG}_DIR}" ABSOLUTE)
522        basis_update_value (DEPENDS_${PKG}_DIR "${_BFP_PREFIX}")
523      endif ()
524      # --------------------------------------------------------------------
525      # reset _?<PKG>[_-]* variables when DEPENDS_<PKG>_DIR has changed
526      if (_DEPENDS_${PKG}_DIR AND DEPENDS_${PKG}_DIR)
527        if (NOT "^${_DEPENDS_${PKG}_DIR}$" STREQUAL "^${DEPENDS_${PKG}_DIR}$")
528          get_cmake_property (_BFP_VARS VARIABLES)
529          basis_sanitize_for_regex (PKG_RE     "${PKG}")
530          basis_sanitize_for_regex (PKG_U_RE   "${PKG_U}")
531          basis_sanitize_for_regex (_BFP_NS_RE "${_BFP_NS}")
532          foreach (_BFP_VAR IN LISTS _BFP_VARS)
533            if (_BFP_VAR MATCHES "^_?(${_BFP_NS_RE}|${PKG_RE}|${PKG_U_RE})[_-]")
534              basis_is_cached (_BFP_CACHED ${_BFP_VAR})
535              if (_BFP_CACHED)
536                set_property (CACHE ${_BFP_VAR} PROPERTY VALUE "${_BFP_VAR}-NOTFOUND")
537                set_property (CACHE ${_BFP_VAR} PROPERTY TYPE  INTERNAL)
538              elseif (NOT _BFP_VAR MATCHES "^${PKG_RE}_FIND_")
539                unset (${_BFP_VAR})
540              endif ()
541            endif ()
542          endforeach ()
543          unset (PKG_RE)
544          unset (PKG_U_RE)
545          unset (_BFP_NS_RE)
546        endif ()
547      endif ()
548      # ----------------------------------------------------------------------
549      # determine if additional components of found package should be discovered
550      if (${PKG}_FOUND)
551        set (_BFP_NO_FIND_PACKAGE TRUE)
552        if (${PKG}_COMPONENTS_FOUND)
553          # previously called with COMPONENTS
554          set (_BFP_FIND_COMPONENTS)
555          set (_BFP_FIND_OPTIONAL_COMPONENTS)
556          if (_BFP_ARGN_COMPONENTS OR _BFP_ARGN_OPTIONAL_COMPONENTS)
557            foreach (_BFP_CMP IN LISTS _BFP_ARGN_COMPONENTS _BFP_ARGN_OPTIONAL_COMPONENTS)
558              list (FIND ${PKG}_COMPONENTS_FOUND "${_BFP_CMP}" _BFP_CMPIDX)
559              if (_BFP_CMPIDX EQUAL -1)
560                if (${PKG}_FIND_REQUIRED_${_BFP_CMP})
561                  list (APPEND _BFP_FIND_COMPONENTS "${_BFP_CMP}")
562                elseif (NOT DEFINED ${PKG}_${_BFP_CMP}_FOUND)
563                  list (APPEND _BFP_FIND_OPTIONAL_COMPONENTS "${_BFP_CMP}")
564                endif ()
565              endif ()
566            endforeach ()
567          else ()
568            # Not sure if "default" components were found when find_package
569            # was previously invoked with the COMPONENTS argument, but
570            # now without it. This depends on the Find<PKG> module.
571            set (_BFP_NO_FIND_PACKAGE FALSE)
572          endif ()
573        else ()
574          # previously called without COMPONENTS
575          set (_BFP_FIND_COMPONENTS          ${_BFP_ARGN_COMPONENTS})
576          set (_BFP_FIND_OPTIONAL_COMPONENTS ${_BFP_ARGN_OPTIONAL_COMPONENTS})
577        endif ()
578        if (_BFP_FIND_COMPONENTS OR _BFP_FIND_OPTIONAL_COMPONENTS)
579          set (_BFP_NO_FIND_PACKAGE FALSE)
580        endif ()
581      else ()
582        set (_BFP_NO_FIND_PACKAGE          FALSE)
583        set (_BFP_FIND_COMPONENTS          ${_BFP_ARGN_COMPONENTS})
584        set (_BFP_FIND_OPTIONAL_COMPONENTS ${_BFP_ARGN_OPTIONAL_COMPONENTS})
585      endif ()
586      # ----------------------------------------------------------------------
587      # look for external package if not found or additional components needed
588      if (NOT ${PKG}_FOUND AND NOT _BFP_ARGN_REQUIRED AND NOT WITH_${PKG} AND ((DEFINED USE_${PKG} AND NOT USE_${PKG}) OR NOT DEFINED USE_${PKG}))
589        # skip if package is optional and user did not ask us to look for it
590        # when package was found before, still perform steps below to ensure
591        # that everything is set properly even when find_package was called
592        mark_as_advanced (FORCE DEPENDS_${PKG}_DIR)
593      else ()
594        # status message with information what we are looking for
595        if (_BFP_ARGN_QUIET)
596          set (_BFP_STATUS)
597        else ()
598          set (_BFP_STATUS "Looking for ${PKG}")
599          if (_BFP_VERSIONS)
600            string (REPLACE ";" " or " _BFP_VERSION_STRING "${_BFP_VERSIONS}")
601            set (_BFP_STATUS "${_BFP_STATUS} ${_BFP_VERSION_STRING}")
602            unset (_BFP_VERSION_STRING)
603          endif ()
604          if (_BFP_FIND_COMPONENTS)
605            set (_BFP_STATUS "${_BFP_STATUS} [${_BFP_FIND_COMPONENTS}]")
606          endif ()
607          if (_BFP_FIND_OPTIONAL_COMPONENTS)
608            set (_BFP_STATUS "${_BFP_STATUS}, optional components [${_BFP_FIND_OPTIONAL_COMPONENTS}]")
609          endif ()
610          if (NOT _BFP_ARGN_REQUIRED)
611            set (_BFP_STATUS "${_BFP_STATUS} (optional)")
612          endif ()
613          set (_BFP_STATUS "${_BFP_STATUS}...")
614          message (STATUS "${_BFP_STATUS}")
615        endif ()
616        # make copy of previous <Pkg>_VERSION_STRING if already set which is used
617        # as "last resort" when variable not set by the following find_package
618        set (_BFP_VERSION_STRING "${${_BFP_NS}_VERSION_STRING}")
619        unset (${_BFP_NS}_VERSION_STRING)
620        # set internal <Pkg>_DIR used by find_package to locate <Pkg>Config
621        set (${PKG}_DIR "${DEPENDS_${PKG}_DIR}" CACHE INTERNAL "Directory containing ${PKG}Config.cmake file." FORCE)
622        # call find_package if not all components found yet
623        set (_BFP_FOUND "${${PKG}_FOUND}") # used to decide what the intersection of
624                                           # multiple find invocations for the same
625                                           # package with different components will be
626                                           # for the setting of <PKG>_FOUND
627        if (NOT _BFP_NO_FIND_PACKAGE)
628          # reset <PKG>_FOUND
629          set (${PKG}_FOUND   FALSE)
630          set (${PKG_U}_FOUND FALSE)
631          # make copy of find_* search path variables
632          foreach (_BFP_VAR IN ITEMS CMAKE_PREFIX_PATH CMAKE_PROGRAM_PATH)
633            set (_BFP_${_BFP_VAR} "${${_BFP_VAR}}")
634          endforeach ()
635          # insert DEPENDS_<PKG>_DIR into find_* search paths
636          if (IS_DIRECTORY "${DEPENDS_${PKG}_DIR}")
637            # add directory to CMAKE_PROGRAM_PATH when the name
638            # of the last subdirectory is "bin", "Bin", "sbin",
639            # or "texbin" (i.e., MacTeX's "/Library/TeX/texbin" path)
640            if (DEPENDS_${PKG}_DIR MATCHES "/([bB]in|sbin|texbin)/+$")
641              list (INSERT CMAKE_PROGRAM_PATH 0 "${DEPENDS_${PKG}_DIR}")
642              list (REMOVE_DUPLICATES CMAKE_PROGRAM_PATH)
643            # add directory to CMAKE_PREFIX_PATH otherwise as users
644            # tend to specify the installation prefix instead of the
645            # actual directory containing the package configuration file
646            else ()
647              list (INSERT CMAKE_PREFIX_PATH 0 "${DEPENDS_${PKG}_DIR}")
648              list (REMOVE_DUPLICATES CMAKE_PREFIX_PATH)
649            endif ()
650          endif ()
651          # now look for the package using find_package
652          set (_BFP_FIND_PACKAGE_ARGS ${_BFP_ARGN_UNPARSED_ARGUMENTS})
653          if (_BFP_ARGN_QUIET OR "^${PKG}$" STREQUAL "^Boost$")
654            list (APPEND _BFP_FIND_PACKAGE_ARGS "QUIET")
655          endif ()
656          if (_BFP_FIND_COMPONENTS OR _BFP_FIND_OPTIONAL_COMPONENTS)
657            if (${PKG}_COMPONENTS_FOUND OR _BFP_FIND_COMPONENTS)
658              list (APPEND _BFP_FIND_PACKAGE_ARGS "COMPONENTS" ${${PKG}_COMPONENTS_FOUND} ${_BFP_FIND_COMPONENTS})
659            endif ()
660            if (_BFP_FIND_OPTIONAL_COMPONENTS)
661              list (APPEND _BFP_FIND_PACKAGE_ARGS "OPTIONAL_COMPONENTS" ${_BFP_FIND_OPTIONAL_COMPONENTS})
662            endif ()
663          endif ()
664          if (${_BFP_VERSIONS_COUNT} GREATER 1)
665            list (APPEND _BFP_FIND_PACKAGE_ARGS CONFIG)
666            if (DEPENDS_${PKG}_DIR)
667              foreach (_BFP_VERSION ${_BFP_VERSIONS})
668                find_package (${PKG} ${_BFP_VERSION} ${_BFP_FIND_PACKAGE_ARGS} QUIET
669                  PATHS ${DEPENDS_${PKG}_DIR} NO_DEFAULT_PATH
670                )
671                if (${PKG}_FOUND)
672                  break ()
673                endif ()
674              endforeach ()
675            endif ()
676            if (NOT ${PKG}_FOUND)
677              foreach (_BFP_VERSION ${_BFP_VERSIONS})
678                find_package (${PKG} ${_BFP_VERSION} ${_BFP_FIND_PACKAGE_ARGS} QUIET)
679                if (${PKG}_FOUND)
680                  break ()
681                endif ()
682              endforeach ()
683            endif ()
684          else ()
685            if (_BFP_ARGN_MODULE)
686              list (APPEND _BFP_FIND_PACKAGE_ARGS MODULE)
687            endif ()
688            if (_BFP_ARGN_NO_MODULE OR _BFP_ARGN_CONFIG)
689              list (APPEND _BFP_FIND_PACKAGE_ARGS CONFIG)
690            endif ()
691            find_package (${PKG} ${_BFP_VERSIONS} ${_BFP_FIND_PACKAGE_ARGS} QUIET)
692          endif ()
693          unset (_BFP_FIND_PACKAGE_ARGS)
694          # restore find_* search path variables
695          foreach (_BFP_VAR IN ITEMS CMAKE_PREFIX_PATH CMAKE_PROGRAM_PATH)
696            set (${_BFP_VAR} "${_BFP_${_BFP_VAR}}")
697            unset (_BFP_${_BFP_VAR})
698          endforeach ()
699          # ensure that <PKG>_DIR is still an internal cache entry
700          basis_update_type_of_variable (${PKG}_DIR INTERNAL)
701          # force reinclusion of package use file
702          if (${PKG}_FOUND)
703            if (${PKG}_USE_FILE_INCLUDED)
704              set (${PKG}_USE_FILE_INCLUDED 0)
705            endif ()
706            if (BASIS_USE_${PKG}_INCLUDED)
707              set (BASIS_USE_${PKG}_INCLUDED FALSE)
708            endif ()
709          endif ()
710        endif () # NOT _BFP_NO_FIND_PACKAGE
711        # set common <Pkg>_VERSION_STRING variable if possible and not set
712        if (NOT DEFINED ${_BFP_NS}_VERSION_STRING)
713          if (DEFINED ${_BFP_NS}_VERSION_MAJOR)
714            set (${_BFP_NS}_VERSION_STRING ${${_BFP_NS}_VERSION_MAJOR})
715            if (DEFINED ${_BFP_NS}_VERSION_MINOR)
716              set (${_BFP_NS}_VERSION_STRING ${${_BFP_NS}_VERSION_STRING}.${${_BFP_NS}_VERSION_MINOR})
717              if (DEFINED ${_BFP_NS}_VERSION_PATCH)
718                set (${_BFP_NS}_VERSION_STRING ${${_BFP_NS}_VERSION_STRING}.${${_BFP_NS}_VERSION_PATCH})
719              elseif (DEFINED ${_BFP_NS}_SUBMINOR_VERSION) # e.g., FindBoost
720                set (${_BFP_NS}_VERSION_STRING ${${_BFP_NS}_VERSION_STRING}.${${_BFP_NS}_SUBMINOR_VERSION})
721              endif ()
722            endif ()
723          elseif (DEFINED ${_BFP_NS}_MAJOR_VERSION)
724            set (${_BFP_NS}_VERSION_STRING ${${_BFP_NS}_MAJOR_VERSION})
725            if (DEFINED ${_BFP_NS}_MINOR_VERSION)
726              set (${_BFP_NS}_VERSION_STRING ${${_BFP_NS}_VERSION_STRING}.${${_BFP_NS}_MINOR_VERSION})
727              if (DEFINED ${PKG}_PATCH_VERSION)
728                set (${_BFP_NS}_VERSION_STRING ${${_BFP_NS}_VERSION_STRING}.${${_BFP_NS}_PATCH_VERSION})
729              elseif (DEFINED ${_BFP_NS}_SUBMINOR_VERSION) # e.g., FindBoost
730                set (${_BFP_NS}_VERSION_STRING ${${_BFP_NS}_VERSION_STRING}.${${_BFP_NS}_SUBMINOR_VERSION})
731              endif ()
732            endif ()
733          elseif (DEFINED ${_BFP_NS}_VERSION)
734            set (${_BFP_NS}_VERSION_STRING ${${_BFP_NS}_VERSION})
735          else ()
736            set (${_BFP_NS}_VERSION_STRING "${_BFP_VERSION_STRING}")
737          endif ()
738        endif ()
739        unset (_BFP_VERSION_STRING)
740        # update DEPENDS_<PKG>_DIR from variables set by find_package
741        if (${PKG}_FOUND)
742          if (${PKG}_DIR AND IS_ABSOLUTE "${${PKG}_DIR}" AND
743              (EXISTS "${${PKG}_DIR}/${PKG}Config.cmake" OR
744               EXISTS "${${PKG}_DIR}/${PKG_L}-config.cmake"))
745            _basis_config_to_prefix_dir(${PKG} "${${PKG}_DIR}" _BFP_PREFIX)
746            basis_update_value (DEPENDS_${PKG}_DIR "${_BFP_PREFIX}")
747          elseif (NOT DEPENDS_${PKG}_DIR)
748            if (${_BFP_NS}_INCLUDE_DIR)
749              list (GET ${_BFP_NS}_INCLUDE_DIR 0 _BFP_PREFIX)
750              string (REGEX REPLACE "^(.*)/[iI]ncludes?(/.*)?$" "\\1" _BFP_PREFIX "${_BFP_PREFIX}")
751            elseif (${_BFP_NS}_LIBRARY_DIR)
752              list (GET ${_BFP_NS}_LIBRARY_DIR 0 _BFP_PREFIX)
753              string (REGEX REPLACE "^(.*)/[lL]ib(s|exec|64)?(/.*)?$" "\\1" _BFP_PREFIX "${_BFP_PREFIX}")
754            else ()
755              set (_BFP_VARS
756                ${_BFP_NS}_EXECUTABLE # e.g., FindBASH
757                ${_BFP_NS}_COMPILER   # e.g., FindLATEX
758                ${_BFP_NS}_CONVERTER  # e.g., FindLATEX
759              )
760              foreach (_BFP_VAR IN LISTS _BFP_VARS)
761                if (${_BFP_VAR})
762                  get_filename_component (_BFP_PREFIX "${${_BFP_VAR}}" PATH)
763                  break ()
764                endif ()
765              endforeach ()
766            endif ()
767            basis_update_value (DEPENDS_${PKG}_DIR "${_BFP_PREFIX}")
768          endif ()
769        else ()
770          basis_update_value (DEPENDS_${PKG}_DIR "NOTFOUND")
771        endif ()
772        # make internal copy of DEPENDS_<PKG>_DIR used to detect change
773        set (_DEPENDS_${PKG}_DIR "${DEPENDS_${PKG}_DIR}" CACHE INTERNAL "(Previous) DEPENDS_${PKG}_DIR value." FORCE)
774        # make internal search path cache entries consistent with DEPENDS_<PKG>_DIR
775        foreach (_BFP_VAR IN LISTS _BFP_PKG_DIR_VARS)
776          basis_is_cached (_BFP_CACHED ${_BFP_VAR})
777          if (_BFP_CACHED)
778            set (${_BFP_VAR} "${DEPENDS_${PKG}_DIR}" CACHE INTERNAL "Installation prefix of ${PKG}." FORCE)
779          endif ()
780        endforeach ()
781        # make internal copy of <PKG>_DIR used to detect change via -D option
782        #
783        # Note: All other alternative variables such as <PKG>_ROOT are forced to
784        #       be equal DEPENDS_<PKG>_DIR. Only <PKG>_DIR usually points to the
785        #       <PKG>Config, while DEPENDS_<PKG>_DIR is the installation prefix.
786        set (_${PKG}_DIR "${${PKG}_DIR}" CACHE INTERNAL "(Previous) ${PKG}_DIR value." FORCE)
787        # status message with information about found package
788        if (_BFP_STATUS)
789          if (${PKG}_FOUND)
790            if (_BFP_NO_FIND_PACKAGE)
791              set (_BFP_STATUS "${_BFP_STATUS} - already found")
792            else ()
793              set (_BFP_STATUS "${_BFP_STATUS} - found")
794            endif ()
795            if ("^${PKG}$" STREQUAL "^Boost$")
796              set (_BFP_STATUS "${_BFP_STATUS} v${${_BFP_NS}_MAJOR_VERSION}.${${_BFP_NS}_MINOR_VERSION}.${${_BFP_NS}_SUBMINOR_VERSION}")
797            elseif (DEFINED ${_BFP_NS}_VERSION_STRING AND NOT ${_BFP_NS}_VERSION_STRING MATCHES "^(0(\\.0)?(\\.0)?)?$")
798              set (_BFP_STATUS "${_BFP_STATUS} v${${_BFP_NS}_VERSION_STRING}")
799            endif ()
800            if (BASIS_VERBOSE AND DEPENDS_${PKG}_DIR)
801              set (_BFP_STATUS "${_BFP_STATUS} at ${DEPENDS_${PKG}_DIR}")
802            endif ()
803          else ()
804            set (_BFP_STATUS "${_BFP_STATUS} - not found")
805          endif ()
806          message (STATUS "${_BFP_STATUS}")
807        endif ()
808        unset (_BFP_STATUS)
809        # raise error when a required package was not found
810        if (NOT ${PKG}_FOUND AND NOT _BFP_ARGN_NO_NOTFOUND_ERROR AND (_BFP_ARGN_REQUIRED OR WITH_${PKG}))
811          set (_BFP_ERROR)
812          if (PROJECT_IS_MODULE)
813            set (_BFP_ERROR "Module")
814          else ()
815            set (_BFP_ERROR "Project")
816          endif ()
817          set (_BFP_ERROR "${_BFP_ERROR} ${PROJECT_NAME}")
818          if (_BFP_ARGN_REQUIRED)
819            set (_BFP_ERROR "${_BFP_ERROR} requires ${PKG}")
820          else ()
821            set (_BFP_ERROR "${_BFP_ERROR} was requested to be build with ${PKG}")
822          endif ()
823          if (_BFP_VERSIONS)
824            set (_BFP_ERROR "${_BFP_ERROR} version ${_BFP_VERSIONS}")
825          endif ()
826          set (_BFP_ERROR "${_BFP_ERROR}. Please ensure that the package is installed in a"
827                          " standard system location or set DEPENDS_${PKG}_DIR to the"
828                          " installation prefix (i.e., root directory of the installation).")
829          if (NOT _BFP_ARGN_REQUIRED AND DEFINED WITH_${PKG})
830            set (_BFP_ERROR "${_BFP_ERROR} To disable features which require this optional dependency,"
831                            " set the WITH_${PKG} option to OFF and try again.")
832          endif ()
833          if (DEFINED ${PKG}_DIR)
834            set (_BFP_ERROR "${_BFP_ERROR}\nThe DEPENDS_${PKG}_DIR variable can alternatively be set"
835                            " to the directory containing a ${PKG}Config.cmake or ${PKG_L}-config.cmake"
836                            " file. If no such file exists, contact either the developer of"
837                            " this project or CMake BASIS to provide a Find${PKG}.cmake file.")
838          endif ()
839          basis_list_to_string(_BFP_ERROR ${_BFP_ERROR})
840          message (FATAL_ERROR "\n${_BFP_ERROR}\n")
841        endif ()
842        # update list of found components
843        if (${PKG}_FOUND)
844          if (_BFP_FIND_COMPONENTS)
845            list (APPEND ${PKG}_COMPONENTS_FOUND ${_BFP_FIND_COMPONENTS})
846          endif ()
847          foreach (_BFP_CMP IN LISTS _BFP_FIND_OPTIONAL_COMPONENTS)
848            if (${PKG}_${_BFP_CMP}_FOUND)
849              list (APPEND ${PKG}_COMPONENTS_FOUND ${_BFP_CMP})
850            endif ()
851          endforeach ()
852          if (${PKG}_COMPONENTS_FOUND)
853            list (REMOVE_DUPLICATES ${PKG}_COMPONENTS_FOUND)
854          endif ()
855        endif ()
856        # if previously this package or components of it where found and the
857        # re-discovery of the package or additional components is only optional,
858        # set <PKG>_FOUND to TRUE again
859        if (_BFP_FOUND AND NOT _BFP_ARGN_REQUIRED AND NOT WITH_${PKG})
860          set (${PKG}_FOUND TRUE)
861        endif ()
862        unset (_BFP_FOUND)
863      endif ()
864    endif ()
865  endif ()
866  # --------------------------------------------------------------------------
867  # unset locally used variables
868  foreach (_BFP_CMP IN LISTS _BFP_ARGN_COMPONENTS _BFP_ARGN_OPTIONAL_COMPONENTS)
869    unset (${PKG}_FIND_REQUIRED_${_BFP_CMP})
870  endforeach ()
871  foreach (_BFP_VAR IN LISTS _BFP_OPTIONS _BFP_MULTI_ARGS)
872    unset (_BFP_ARGN_${_BFP_VAR})
873  endforeach ()
874  unset (_BFP_COMPONENTS)
875  unset (_BFP_ARGN_UNPARSED_ARGUMENTS)
876  unset (_BFP_OPTIONS)
877  unset (_BFP_MULTI_ARGS)
878  unset (_BFP_IS_MODULE)
879  unset (_BFP_IS_PROJECT)
880  unset (_BFP_VERSION)
881  unset (_BFP_VERSIONS)
882  unset (_BFP_VERSIONS_COUNT)
883  unset (_BFP_PKGIDX)
884  unset (_BFP_CMPIDX)
885  unset (_BFP_CMP)
886  unset (_BFP_VAR)
887  unset (_BFP_VARS)
888  unset (_BFP_CACHED)
889  unset (_BFP_TYPE)
890  unset (_BFP_PREFIX)
891  unset (_BFP_PKG_DIR_VARS)
892  unset (_BFP_FIND_COMPONENTS)
893  unset (_BFP_FIND_OPTIONAL_COMPONENTS)
894  unset (_BFP_NO_FIND_PACKAGE)
895  unset (_BFP_NS)
896  unset (PKG)
897  unset (PKG_L)
898  unset (PKG_U)
899endmacro ()
900
901# ----------------------------------------------------------------------------
902## @brief Use found package.
903#
904# This macro includes the package's use file if the variable @c &lt;Pkg&gt;_USE_FILE
905# is defined. Otherwise, it adds the include directories to the search path
906# for include paths if possible. Therefore, the corresponding package
907# configuration file has to set the proper CMake variables, i.e.,
908# either @c &lt;Pkg&gt;_INCLUDES, @c &lt;Pkg&gt;_INCLUDE_DIRS, or @c &lt;Pkg&gt;_INCLUDE_DIR.
909#
910# If the given package name is the name of another module of this project
911# (the top-level project), this function includes the use file of the specified
912# module.
913#
914# @note As some packages still use all captial variables instead of ones
915#       prefixed by a string that follows the same capitalization as the
916#       package's name, this function also considers these if defined instead.
917#       Hence, if @c &lt;PKG&gt;_INCLUDES is defined, but not @c &lt;Pkg&gt;_INCLUDES, it
918#       is used in place of the latter.
919#
920# @note According to an email on the CMake mailing list, it is not a good idea
921#       to use basis_link_directories() any more given that the arguments to
922#       basis_target_link_libraries() are absolute paths to the library files.
923#       Therefore, this code is commented and not used. It remains here as a
924#       reminder only.
925#
926# @param [in] PACKAGE Name of other package. Optionally, the package name
927#                     can include a version specification as suffix which
928#                     is separated by the package name using a dash (-), i.e.,
929#                     &lt;Package&gt;[-major[.minor[.patch[.tweak]]]].
930#                     A version specification is simply ignored by this macro.
931#
932# @ingroup CMakeAPI
933macro (basis_use_package PACKAGE)
934  # tokenize package specification
935  basis_tokenize_dependency ("${PACKAGE}" PKG VER CMPS)
936  # use package
937  foreach (A IN ITEMS "WORKAROUND FOR NOT BEING ABLE TO USE RETURN")
938    if (BASIS_DEBUG)
939      message ("** basis_use_package()")
940      message ("**     Package: ${PKG}")
941    endif ()
942    if (PROJECT_IS_MODULE)
943      # ignore BASIS as module dependency
944      # important if BASIS itself is a project module
945      if ("^${PKG}$" STREQUAL "^BASIS$")
946        if (BASIS_DEBUG)
947          message ("**     Ignoring BASIS dependency as it clearly is used already by the top-level project.")
948        endif ()
949        break ()
950      # allow modules to specify top-level project as dependency
951      elseif ("^${PKG}$" STREQUAL "^${TOPLEVEL_PROJECT_NAME}$")
952        if (CMPS)
953          if (BASIS_DEBUG)
954            message ("**     These are other modules of the top-level project.")
955          endif ()
956          foreach (CMP IN LISTS CMPS)
957            list (FIND PROJECT_MODULES "${CMP}" CMPIDX)
958            if (CMPIDX EQUAL -1)
959              message (FATAL_ERROR "Module ${PROJECT_NAME} has module ${CMP} of project ${TOPLEVEL_PROJECT_NAME}"
960                                   " as dependency, but no such module exists.")
961            endif ()
962            list (FIND PROJECT_MODULES_ENABLED "${CMP}" CMPIDX)
963            if (NOT CMPIDX EQUAL -1)
964              if (BASIS_DEBUG)
965                message ("**     Include package use file of module ${CMP}.")
966              endif ()
967              include ("${${${CMP}_CONFIG_PREFIX}_USE_FILE}")
968              if (PROJECT_PACKAGE_NAME)
969                add_definitions(-DHAVE_${PROJECT_PACKAGE_NAME}_${CMP})
970              else ()
971                add_definitions(-DHAVE_${TOPLEVEL_PROJECT_NAME}_${CMP})
972              endif ()
973            endif ()
974          endforeach ()
975          unset (CMPIDX)
976          unset (CMP)
977        else ()
978          if (BASIS_DEBUG)
979            message ("**     This is the top-level project.")
980          endif ()
981        endif ()
982        break () # instead of return()
983      # use other module of top-level project
984      else ()
985        list (FIND PROJECT_MODULES "${PKG}" PKGIDX)
986        if (NOT PKGIDX EQUAL -1 AND ${PKG}_FOUND)
987          if (BASIS_DEBUG)
988            message ("**     Include package use file of other module.")
989          endif ()
990          include ("${${PKG}_USE_FILE}")
991          add_definitions(-DHAVE_${PKG})
992          unset (PKGIDX)
993          break () # instead of return()
994        endif ()
995        unset (PKGIDX)
996      endif ()
997    endif ()
998    # if this package is an external project, i.e., a project build as part
999    # of the same superbuild as this project, set BUNDLE_PROJECT to TRUE.
1000    # it is used by (basis_)link_directories() and add_library() to mark
1001    # the imported link directories and target as belonging to the same
1002    # installation. this is in particular important for the RPATH settings.
1003    # whether this package is an external project or not, is decided by the
1004    # BUNDLE_PROJECTS variable which must be set using the -D option of
1005    # cmake to a list naming all the other packages which are part of the
1006    # superbuild.
1007    if (BUNDLE_PROJECTS)
1008      list (FIND BUNDLE_PROJECTS "${PKG}" PKGIDX)
1009      if (PKGIDX EQUAL -1)
1010        set (BUNDLE_PROJECT FALSE)
1011      else ()
1012        set (BUNDLE_PROJECT TRUE)
1013      endif ()
1014      unset (PKGIDX)
1015    endif ()
1016    # use external package
1017    if (${PKG}_FOUND)
1018      # use package only if basis_use_package() not invoked before
1019      if (BASIS_USE_${PKG}_INCLUDED)
1020        if (BASIS_DEBUG)
1021          message ("**     External package used before already.")
1022        endif ()
1023        break ()
1024      endif ()
1025      if (${PKG}_USE_FILE)
1026        if ("^${PKG}$" STREQUAL "^VTK$" AND VTK_VERSION VERSION_GREATER_EQUAL 8.90.0)
1027          if (BASIS_DEBUG)
1028            message ("**     Ignore deprecated use file of VTK >=8.90.0.")
1029          endif ()
1030        else ()
1031          if (BASIS_DEBUG)
1032            message ("**     Include package use file of external package.")
1033          endif ()
1034          if ("^${PKG}$" STREQUAL "^BASIS$")
1035            include ("${${PKG}_USE_FILE}" NO_POLICY_SCOPE)
1036          else ()
1037            include ("${${PKG}_USE_FILE}")
1038          endif ()
1039        endif()
1040      else ()
1041        if (BASIS_DEBUG)
1042          message ("**     Use variables which were set by basis_find_package().")
1043        endif ()
1044        # OpenCV
1045        if ("^${PKG}$" STREQUAL "^OpenCV$")
1046          # the cv.h may be found as part of PerlLibs, the include path of
1047          # which is added at first by BASISConfig.cmake
1048          if (OpenCV_INCLUDE_DIRS)
1049            basis_include_directories (BEFORE ${OpenCV_INCLUDE_DIRS})
1050          elseif (OpenCV_INCLUDE_DIR)
1051            basis_include_directories (BEFORE ${OpenCV_INCLUDE_DIR})
1052          endif ()
1053        # generic
1054        else ()
1055          if (${PKG}_INCLUDE_DIRS)
1056            basis_include_directories (${${PKG}_INCLUDE_DIRS})
1057          elseif (${PKG}_INCLUDES)
1058            basis_include_directories (${${PKG}_INCLUDES})
1059          elseif (${PKG}_INCLUDE_PATH)
1060            basis_include_directories (${${PKG}_INCLUDE_PATH})
1061          elseif (${PKG}_INCLUDE_DIR)
1062            basis_include_directories (${${PKG}_INCLUDE_DIR})
1063          endif ()
1064        endif ()
1065      endif ()
1066      add_definitions(-DHAVE_${PKG})
1067      set (BASIS_USE_${PKG}_INCLUDED TRUE)
1068    elseif (ARGC GREATER 1 AND "^${ARGV1}$" STREQUAL "^REQUIRED$")
1069      if (BASIS_DEBUG)
1070        basis_dump_variables ("${PROJECT_BINARY_DIR}/VariablesAfterFind${PKG}.cmake")
1071      endif ()
1072      message (FATAL_ERROR "Package ${PACKAGE} not found!")
1073    endif ()
1074    # reset switch that identifies currently imported targets and link directories
1075    # as belonging to an external project which is part of the same superbuild
1076    set (BUNDLE_PROJECT FALSE)
1077  endforeach ()
1078endmacro ()
1079
1080# ============================================================================
1081# basis_get_filename_component / basis_get_relative_path
1082# ============================================================================
1083
1084# ----------------------------------------------------------------------------
1085## @brief Fixes CMake's
1086#         <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_filename_component">
1087#         get_filename_component()</a> command.
1088#
1089# The get_filename_component() command of CMake returns the entire portion
1090# after the first period (.) [including the period] as extension. However,
1091# only the component following the last period (.) [including the period]
1092# should be considered to be the extension.
1093#
1094# @note Consider the use of the basis_get_filename_component() macro as
1095#       an alias to emphasize that this function is different from CMake's
1096#       <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_filename_component">
1097#       get_filename_component()</a> command.
1098#
1099# @todo Fix issue http://public.kitware.com/Bug/view.php?id=15743 which
1100#       affects also basis_get_relative_path.
1101#
1102# @param [in,out] ARGN Arguments as accepted by get_filename_component().
1103#
1104# @returns Sets the variable named by the first argument to the requested
1105#          component of the given file path.
1106#
1107# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_filename_component
1108# @sa basis_get_filename_component()
1109function (get_filename_component VAR STR CMP)
1110  list (FIND ARGN "CACHE" IDX)
1111  if (IDX EQUAL -1)
1112    set (OPT_CACHE 0)
1113  else ()
1114    list (REMOVE_ITEM ARGN "CACHE")
1115    set (OPT_CACHE 1)
1116  endif ()
1117  if (CMP MATCHES "^EXT")
1118    _get_filename_component (${VAR} "${STR}" ${CMP} ${ARGN})
1119    string (REGEX MATCHALL "\\.[^.]*" PARTS "${${VAR}}")
1120    list (LENGTH PARTS LEN)
1121    if (LEN GREATER 1)
1122      math (EXPR LEN "${LEN} - 1")
1123      list (GET PARTS ${LEN} ${VAR})
1124    endif ()
1125  elseif (CMP MATCHES "NAME_WE")
1126    _get_filename_component (${VAR} "${STR}" NAME ${ARGN})
1127    string (REGEX REPLACE "\\.[^.]*$" "" ${VAR} ${${VAR}})
1128  else ()
1129    _get_filename_component (${VAR} "${STR}" ${CMP} ${ARGN})
1130    if (CMP MATCHES "PROGRAM")
1131      list (FIND ARGN "PROGRAM_ARGS" IDX)
1132      if (NOT IDX EQUAL -1)
1133        math (EXPR IDX "${IDX} + 1")
1134        list (GET ARGN ${IDX} ARG_VAR)
1135        set (${ARG_VAR} "${${ARG_VAR}}" PARENT_SCOPE)
1136      endif ()
1137    endif ()
1138  endif ()
1139  if (OPT_CACHE)
1140    set (${VAR} "${${VAR}}" CACHE STRING "")
1141  else ()
1142    set (${VAR} "${${VAR}}" PARENT_SCOPE)
1143  endif ()
1144endfunction ()
1145
1146# ----------------------------------------------------------------------------
1147## @brief Alias for the overwritten get_filename_component() function.
1148#
1149# @sa get_filename_component()
1150#
1151# @ingroup CMakeAPI
1152macro (basis_get_filename_component)
1153  get_filename_component (${ARGN})
1154endmacro ()
1155
1156# ----------------------------------------------------------------------------
1157## @brief Get path relative to a given base directory.
1158#
1159# Unlike the file(RELATIVE_PATH ...) command of CMake which if @p PATH and
1160# @p BASE are the same directory returns an empty string, this function
1161# returns a dot (.) in this case instead.
1162#
1163# @param [out] REL  @c PATH relative to @c BASE.
1164# @param [in]  BASE Path of base directory. If a relative path is given, it
1165#                   is made absolute using basis_get_filename_component()
1166#                   with ABSOLUTE as last argument.
1167# @param [in]  PATH Absolute or relative path. If a relative path is given
1168#                   it is made absolute using basis_get_filename_component()
1169#                   with ABSOLUTE as last argument.
1170#
1171# @returns Sets the variable named by the first argument to the relative path.
1172#
1173# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:file
1174#
1175# @ingroup CMakeAPI
1176function (basis_get_relative_path REL BASE PATH)
1177  if (BASE MATCHES "^$")
1178    message (FATAL_ERROR "Empty string given where (absolute) base directory path expected!")
1179  endif ()
1180  if (PATH MATCHES "^$")
1181    set (PATH ".")
1182  endif ()
1183  # Attention: http://public.kitware.com/Bug/view.php?id=15743
1184  basis_get_filename_component (PATH "${PATH}" ABSOLUTE)
1185  basis_get_filename_component (BASE "${BASE}" ABSOLUTE)
1186  if (NOT PATH)
1187    message (FATAL_ERROR "basis_get_relative_path(): No PATH given!")
1188  endif ()
1189  if (NOT BASE)
1190    message (FATAL_ERROR "basis_get_relative_path(): No BASE given!")
1191  endif ()
1192  file (RELATIVE_PATH P "${BASE}" "${PATH}")
1193  if ("${P}" STREQUAL "")
1194    set (P ".")
1195  endif ()
1196  set (${REL} "${P}" PARENT_SCOPE)
1197endfunction ()
1198
1199## @brief Create a string from a list of variables indicating if they are defined and their values.
1200#
1201# Useful for debug and user errors, for example:
1202# @code
1203# set(VAR1 "I'm a string")
1204# set(VAR2 2)
1205# basis_variable_value_status(VAR_INFO_STRING VAR1 VAR2 VAR3)
1206# message(STATUS ${VAR_INFO_STRING})
1207# @endcode
1208#
1209# @param[out] VAR_INFO_STRING The output string variable that will set with the debug string.
1210# @param[in]  ARGN            List of variables to be put into a string along with their value.
1211function(basis_variable_value_status VAR_INFO_STRING)
1212  set (OUTPUT_STRING)
1213  foreach (VARIABLE_NAME IN ITEMS ${ARGN})
1214    if (DEFINED ${VARIABLE_NAME})
1215      set (OUTPUT_STRING "${OUTPUT_STRING}\n  variable name: ${VARIABLE_NAME}  value: ${${VARIABLE_NAME}}")
1216    else ()
1217      set (OUTPUT_STRING "${OUTPUT_STRING}\n  variable name: ${VARIABLE_NAME}  value is not defined")
1218    endif ()
1219  endforeach ()
1220  set (${VAR_INFO_STRING} ${OUTPUT_STRING} PARENT_SCOPE)
1221endfunction()
1222
1223## @brief Checks for a list of variables required later in the script.
1224#
1225# Produces a clear error message explaining the problem and how to fix it if they are not present.
1226#
1227# @code
1228# basis_variable_check(
1229#    REQUIRED
1230#       LIBRARY1_INCLUDE_DIRS
1231#       LIBRARY2_INCLUDE_DIRS
1232#       LIBRARY2_LIBRARIES
1233#    OPTIONAL
1234#       LIBRARY3_INCLUDE_DIRS
1235#       LIBRARY3_LIBRARIES
1236#    OPTIONAL_PATH
1237#
1238# )
1239# @endcode
1240#
1241# @param [in] ARGN This argument list is parsed and the following
1242#                  arguments are extracted.
1243# @par
1244# <table border="0">
1245#   <tr>
1246#     @tp @b REQUIRED var... @endtp
1247#     <td>List of variables that MUST be set to run this script correctly.
1248#         Will produce a FATAL_ERROR message explaining which variables
1249#         are misisng and exit the cmake script.</td>
1250#   </tr>
1251#   <tr>
1252#     @tp @b OPTIONAL var... @endtp
1253#     <td>List of variables need not be set to run this script correctly.</td>
1254#   </tr>
1255#   <tr>
1256#     @tp @b PATH_EXISTS var... @endtp
1257#     <td>List of path variables that MUST be set to a location that exists.</td>
1258#   </tr>
1259#   <tr>
1260#     @tp @b OPTIONAL_PATH_EXISTS var... @endtp
1261#     <td>List of path variables that are optional, but once set must be empty
1262#         or provide a path to location that exists.</td>
1263#   </tr>
1264# </table>
1265function(basis_variable_check)
1266
1267  set(options ) # currently none
1268  set(oneValueArgs ) # currently none
1269  set(multiValueArgs REQUIRED OPTIONAL PATH_EXISTS OPTIONAL_PATH_EXISTS )
1270  cmake_parse_arguments(VARIABLE_CONFIGURATION "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
1271
1272
1273  #-------------------------------------------
1274  # Create the error strings for missing REQUIRED variables
1275  set(MISSING_REQUIRED_VARIABLES)
1276  foreach(VARIABLE_NAME IN LISTS VARIABLE_CONFIGURATION_REQUIRED)
1277     if(NOT ${VARIABLE_NAME})
1278        list(APPEND MISSING_REQUIRED_VARIABLES ${VARIABLE_NAME})
1279     endif()
1280  endforeach()
1281  if(MISSING_REQUIRED_VARIABLES)
1282    basis_variable_value_status(MISSING_REQUIRED_VARIABLES_STATUS ${MISSING_REQUIRED_VARIABLES})
1283    set(MISSING_VARIABLES_ERROR "\nThe following variables are marked as REQUIRED but they are not set to a valid value. Please define the variables correctly in your cmake script or on the command line using -D. ${MISSING_REQUIRED_VARIABLES_STATUS}")
1284  endif(MISSING_REQUIRED_VARIABLES)
1285
1286
1287  #-------------------------------------------
1288  # Create and print the warning strings for missing OPTIONAL variables
1289  set(MISSING_OPTIONAL_VARIABLES)
1290  foreach(VARIABLE_NAME IN LISTS VARIABLE_CONFIGURATION_OPTIONAL)
1291     if(NOT ${VARIABLE_NAME})
1292        list(APPEND MISSING_OPTIONAL_VARIABLES ${VARIABLE_NAME})
1293     endif()
1294  endforeach()
1295  if(MISSING_OPTIONAL_VARIABLES)
1296    basis_variable_value_status(MISSING_OPTIONAL_VARIABLES_STATUS ${MISSING_OPTIONAL_VARIABLES})
1297    set(MISSING_VARIABLES_WARNING "\nThe following variables are marked as OPTIONAL but they are not set to a valid value. Please define the variables correctly in your cmake script or on the command line using -D. ${MISSING_OPTIONAL_VARIABLES_STATUS}")
1298    message(AUTHOR_WARNING "${MISSING_VARIABLES_WARNING}")
1299  endif(MISSING_OPTIONAL_VARIABLES)
1300
1301
1302
1303  #-------------------------------------------
1304  # Create the error strings for missing or nonexistant REQUIRED PATH variables
1305  set(MISSING_PATH_EXISTS)
1306  foreach(VARIABLE_NAME IN LISTS VARIABLE_CONFIGURATION_PATH_EXISTS)
1307     if(NOT DEFINED ${VARIABLE_NAME} OR NOT EXISTS ${${VARIABLE_NAME}})
1308        list(APPEND MISSING_PATH_EXISTS ${VARIABLE_NAME})
1309     endif()
1310  endforeach()
1311  if(MISSING_PATH_EXISTS)
1312    basis_variable_value_status(MISSING_PATH_EXISTS_STATUS ${MISSING_PATH_EXISTS})
1313    set(PATH_EXISTS_ERROR "\nThe following PATH variables are marked as REQUIRED but they are not set to a valid location. Please define the variables correctly in your cmake script or on the command line using -D. ${MISSING_PATH_EXISTS_STATUS}")
1314  endif(MISSING_PATH_EXISTS)
1315
1316
1317
1318  #-------------------------------------------
1319  # Create the error strings for missing or nonexistant OPTIONAL PATH variables
1320  set(MISSING_OPTIONAL_PATH_EXISTS)
1321  foreach(VARIABLE_NAME IN LISTS VARIABLE_CONFIGURATION_OPTIONAL_PATH_EXISTS)
1322     if(DEFINED ${VARIABLE_NAME} AND NOT ${${VARIABLE_NAME}} STREQUAL "" AND NOT EXISTS ${${VARIABLE_NAME}})
1323       # add VARIABLE_NAME to error list if a nonempty path is defined, but does not point to a real location
1324        list(APPEND MISSING_OPTIONAL_PATH_EXISTS ${VARIABLE_NAME})
1325     endif()
1326  endforeach()
1327  if(MISSING_OPTIONAL_PATH_EXISTS)
1328    basis_variable_value_status(MISSING_OPTIONAL_PATH_EXISTS_STATUS ${MISSING_OPTIONAL_PATH_EXISTS})
1329    #debug:
1330    #message(STATUS "MISSING_OPTIONAL_PATH_EXISTS: ${MISSING_OPTIONAL_PATH_EXISTS}")
1331    #message(STATUS "MISSING_OPTIONAL_PATH_EXISTS_STATUS: ${MISSING_OPTIONAL_PATH_EXISTS_STATUS}")
1332    set(OPTIONAL_PATH_EXISTS_ERROR "\nThe following PATH variables are marked as OPTIONAL but they are not set to a valid location. Please define the variables correctly in your cmake script or on the command line using -D. ${MISSING_OPTIONAL_PATH_EXISTS_STATUS}")
1333  endif(MISSING_OPTIONAL_PATH_EXISTS)
1334
1335  #-------------------------------------------
1336  # Print all fatal errors at once, because the script will halt
1337  if(MISSING_VARIABLES_ERROR OR PATH_EXISTS_ERROR OR OPTIONAL_PATH_EXISTS_ERROR)
1338    message(FATAL_ERROR "${MISSING_VARIABLES_ERROR}${PATH_EXISTS_ERROR}${OPTIONAL_PATH_EXISTS_ERROR}\n")
1339  endif(MISSING_VARIABLES_ERROR OR PATH_EXISTS_ERROR OR OPTIONAL_PATH_EXISTS_ERROR)
1340
1341endfunction(basis_variable_check)
1342
1343
1344
1345# ============================================================================
1346# name / version
1347# ============================================================================
1348
1349# ----------------------------------------------------------------------------
1350## @brief Convert string to lowercase only or mixed case.
1351#
1352# Strings in all uppercase or all lowercase are converted to all lowercase
1353# letters because these are usually used for acronymns. All other strings
1354# are returned unmodified with the one exception that the first letter has
1355# to be uppercase for mixed case strings.
1356#
1357# This function is in particular used to normalize the project name for use
1358# in installation directory paths and namespaces.
1359#
1360# @param [out] OUT String in CamelCase.
1361# @param [in]  STR String.
1362function (basis_normalize_name OUT STR)
1363  # strings in all uppercase or all lowercase such as acronymns are an
1364  # exception and shall be converted to all lowercase instead
1365  string (TOLOWER "${STR}" L)
1366  string (TOUPPER "${STR}" U)
1367  if ("${STR}" STREQUAL "${L}" OR "${STR}" STREQUAL "${U}")
1368    set (${OUT} "${L}" PARENT_SCOPE)
1369  # change first letter to uppercase
1370  else ()
1371    string (SUBSTRING "${U}"   0  1 A)
1372    string (SUBSTRING "${STR}" 1 -1 B)
1373    set (${OUT} "${A}${B}" PARENT_SCOPE)
1374  endif ()
1375endfunction ()
1376
1377# ----------------------------------------------------------------------------
1378## @brief Extract version numbers from version string.
1379#
1380# @param [in]  VERSION Version string in the format "MAJOR[.MINOR[.PATCH]]".
1381# @param [out] MAJOR   Major version number if given or 0.
1382# @param [out] MINOR   Minor version number if given or 0.
1383# @param [out] PATCH   Patch number if given or 0.
1384#
1385# @returns See @c [out] parameters.
1386function (basis_version_numbers VERSION MAJOR MINOR PATCH)
1387  if (VERSION MATCHES "([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(rc[1-9][0-9]*|[a-z]+)?")
1388    if (CMAKE_MATCH_1)
1389      set (VERSION_MAJOR ${CMAKE_MATCH_1})
1390    else ()
1391      set (VERSION_MAJOR 0)
1392    endif ()
1393    if (CMAKE_MATCH_2)
1394      set (VERSION_MINOR ${CMAKE_MATCH_2})
1395      string (REGEX REPLACE "^\\." "" VERSION_MINOR "${VERSION_MINOR}")
1396    else ()
1397      set (VERSION_MINOR 0)
1398    endif ()
1399    if (CMAKE_MATCH_3)
1400      set (VERSION_PATCH ${CMAKE_MATCH_3})
1401      string (REGEX REPLACE "^\\." "" VERSION_PATCH "${VERSION_PATCH}")
1402    else ()
1403      set (VERSION_PATCH 0)
1404    endif ()
1405  else ()
1406    set (VERSION_MAJOR 0)
1407    set (VERSION_MINOR 0)
1408    set (VERSION_PATCH 0)
1409  endif ()
1410  set ("${MAJOR}" "${VERSION_MAJOR}" PARENT_SCOPE)
1411  set ("${MINOR}" "${VERSION_MINOR}" PARENT_SCOPE)
1412  set ("${PATCH}" "${VERSION_PATCH}" PARENT_SCOPE)
1413endfunction ()
1414
1415# ============================================================================
1416# set
1417# ============================================================================
1418
1419# ----------------------------------------------------------------------------
1420## @brief Set flag given mutually exclusive
1421#         ARGN_&lt;FLAG&gt; and ARGN_NO&lt;FLAG&gt; function arguments.
1422#
1423# @param [in]  PREFIX  Prefix of function arguments. Set to the first argument
1424#                      of the CMAKE_PARSE_ARGUMENTS() command.
1425# @param [out] FLAG    Name of flag.
1426# @param [in]  DEFAULT Default flag value if neither <tt>ARGN_&lt;FLAG;gt;</tt>
1427#                      nor <tt>ARGN_NO&lt;FLAG;gt;</tt> evaluates to true.
1428macro (basis_set_flag PREFIX FLAG DEFAULT)
1429  if (${PREFIX}_${FLAG} AND ${PREFIX}_NO${FLAG})
1430    message (FATAL_ERROR "Options ${FLAG} and NO${FLAG} are mutually exclusive!")
1431  endif ()
1432  if (${PREFIX}_${FLAG})
1433    set (${FLAG} TRUE)
1434  elseif (${PREFIX}_NO${FLAG})
1435    set (${FLAG} FALSE)
1436  else ()
1437    set (${FLAG} ${DEFAULT})
1438  endif ()
1439endmacro ()
1440
1441# ----------------------------------------------------------------------------
1442## @brief Determine if cache entry exists.
1443#
1444# @param [out] VAR   Name of boolean result variable.
1445# @param [in]  ENTRY Name of cache entry.
1446macro (basis_is_cached VAR ENTRY)
1447  if (DEFINED ${ENTRY})
1448    get_property (${VAR} CACHE ${ENTRY} PROPERTY TYPE SET)
1449  else ()
1450    set (${VAR} FALSE)
1451  endif ()
1452endmacro ()
1453
1454# ----------------------------------------------------------------------------
1455## @brief Set type of variable.
1456#
1457# If the variable is cached, the type is updated, otherwise, a cache entry
1458# of the given type with the current value of the variable is added.
1459#
1460# @param [in] VAR  Name of variable.
1461# @param [in] TYPE Desired type of variable.
1462# @param [in] ARGN Optional DOC string used if variable was not cached before.
1463macro (basis_set_or_update_type VAR TYPE)
1464  basis_is_cached (_CACHED ${VAR})
1465  if (_CACHED)
1466    set_property (CACHE ${VAR} PROPERTY TYPE ${TYPE})
1467  else ()
1468    set (${VAR} "${${VAR}}" CACHE ${TYPE} "${ARGN}" FORCE)
1469  endif ()
1470  unset (_CACHED)
1471endmacro ()
1472
1473# ----------------------------------------------------------------------------
1474## @brief Change type of cached variable.
1475#
1476# If the variable is not cached, nothing is done.
1477macro (basis_update_type_of_variable VAR TYPE)
1478  basis_is_cached (_CACHED ${VAR})
1479  if (_CACHED)
1480    set_property (CACHE ${VAR} PROPERTY TYPE ${TYPE})
1481  endif ()
1482  unset (_CACHED)
1483endmacro ()
1484
1485# ----------------------------------------------------------------------------
1486## @brief Set variable value.
1487#
1488# If the variable is cached, this function will update the cache value,
1489# otherwise, it simply sets the CMake variable uncached to the given value(s).
1490macro (basis_set_or_update_value VAR)
1491  basis_is_cached (_CACHED ${VAR})
1492  if (_CACHED)
1493    if (ARGC GREATER 1)
1494      set_property (CACHE ${VAR} PROPERTY VALUE ${ARGN})
1495    else ()
1496      set (${VAR} "" CACHE INTERNAL "" FORCE)
1497    endif ()
1498  else ()
1499    set (${VAR} ${ARGN})
1500  endif ()
1501  unset (_CACHED)
1502endmacro ()
1503
1504# ----------------------------------------------------------------------------
1505## @brief Update cache variable.
1506macro (basis_update_value VAR)
1507  basis_is_cached (_CACHED ${VAR})
1508  if (_CACHED)
1509    set_property (CACHE ${VAR} PROPERTY VALUE ${ARGN})
1510  endif ()
1511  unset (_CACHED)
1512endmacro ()
1513
1514# ----------------------------------------------------------------------------
1515## @brief Set value of variable only if variable is not set already.
1516#
1517# @param [out] VAR  Name of variable.
1518# @param [in]  ARGN Arguments to set() command excluding variable name.
1519#
1520# @returns Sets @p VAR if its value was not valid before.
1521macro (basis_set_if_empty VAR)
1522  if (NOT ${VAR})
1523    set (${VAR} ${ARGN})
1524  endif ()
1525endmacro ()
1526
1527# ----------------------------------------------------------------------------
1528## @brief Set value of variable only if variable is not defined yet.
1529#
1530# @param [out] VAR  Name of variable.
1531# @param [in]  ARGN Arguments to set() command excluding variable name.
1532#
1533# @returns Sets @p VAR if it was not defined before.
1534macro (basis_set_if_not_set VAR)
1535  if (NOT DEFINED "${VAR}")
1536    set ("${VAR}" ${ARGN})
1537  endif ()
1538endmacro ()
1539
1540# ----------------------------------------------------------------------------
1541## @brief Set value of variable to either 0 or 1 based on option value
1542#
1543# This function can be used to convert option values from TRUE/ON to 1 and
1544# FALSE/OFF to 0 such that they can be used to configure a config.h.in header.
1545#
1546# @param [out] VAR Name of configuration header variable.
1547# @param [in]  OPT Value of CMake option.
1548macro (basis_set_config_option VAR OPT)
1549  if (${OPT})
1550    set ("${VAR}" 1)
1551  else ()
1552    set ("${VAR}" 0)
1553  endif ()
1554endmacro ()
1555
1556# ----------------------------------------------------------------------------
1557## @brief Set path relative to script file.
1558#
1559# This function can be used in script configurations. It takes a variable
1560# name and a path as input arguments. If the given path is relative, it makes
1561# it first absolute using @c PROJECT_SOURCE_DIR. Then the path is made
1562# relative to the directory of the built script file. A CMake variable of the
1563# given name is set to the specified relative path. Optionally, a third
1564# argument, the path used for building the script for the install tree
1565# can be passed as well. If a relative path is given as this argument,
1566# it is made absolute by prefixing it with @c CMAKE_INSTALL_PREFIX instead.
1567#
1568# @note This function may only be used in script configurations such as
1569#       in particular the ScriptConfig.cmake.in file. It requires that the
1570#       variables @c __DIR__ and @c BUILD_INSTALL_SCRIPT are set properly.
1571#       These variables are set by the basis_configure_script() function.
1572#       Moreover, it makes use of the global @c CMAKE_INSTALL_PREFIX and
1573#       @c PROJECT_SOURCE_DIR variables.
1574#
1575# @param [out] VAR   Name of the variable.
1576# @param [in]  PATH  Path to directory or file.
1577# @param [in]  ARGV3 Path to directory or file inside install tree.
1578#                    If this argument is not given, PATH is used for both
1579#                    the build and install tree version of the script.
1580#
1581# @ingroup CMakeAPI
1582function (basis_set_script_path VAR PATH)
1583  if (NOT __DIR__)
1584    message (FATAL_ERROR "__DIR__ not set! Note that basis_set_script_path() may"
1585                         " only be used in script configurations (e.g., ScriptConfig.cmake.in).")
1586  endif ()
1587  if (ARGC GREATER 3)
1588    message (FATAL_ERROR "Too many arguments given for function basis_set_script_path()")
1589  endif ()
1590  if (ARGC EQUAL 3 AND BUILD_INSTALL_SCRIPT)
1591    set (PREFIX "${CMAKE_INSTALL_PREFIX}")
1592    set (PATH   "${ARGV2}")
1593  else ()
1594    set (PREFIX "${PROJECT_SOURCE_DIR}")
1595  endif ()
1596  if (NOT IS_ABSOLUTE "${PATH}")
1597    set (PATH "${PREFIX}/${PATH}")
1598  endif ()
1599  basis_get_relative_path (PATH "${__DIR__}" "${PATH}")
1600  if (NOT PATH)
1601    set (${VAR} "." PARENT_SCOPE)
1602  else ()
1603    string (REGEX REPLACE "/+$" "" PATH "${PATH}")
1604    set (${VAR} "${PATH}" PARENT_SCOPE)
1605  endif ()
1606endfunction ()
1607
1608# ============================================================================
1609# set/get any property
1610# ============================================================================
1611
1612# ----------------------------------------------------------------------------
1613## @brief Convert list into regular expression.
1614#
1615# This function is in particular used to convert a list of property names
1616# such as &lt;CONFIG&gt;_OUTPUT_NAME, e.g., the list @c BASIS_PROPERTIES_ON_TARGETS,
1617# into a regular expression which can be used in pattern matches.
1618#
1619# @param [out] REGEX Name of variable for resulting regular expression.
1620# @param [in]  ARGN  List of patterns which may contain placeholders in the
1621#                    form of "<this is a placeholder>". These are replaced
1622#                    by the regular expression "[^ ]+".
1623macro (basis_list_to_regex REGEX)
1624  string (REGEX REPLACE "<[^>]+>" "[^ ]+" ${REGEX} "${ARGN}")
1625  string (REGEX REPLACE ";" "|" ${REGEX} "${${REGEX}}")
1626  set (${REGEX} "^(${${REGEX}})$")
1627endmacro ()
1628
1629# ----------------------------------------------------------------------------
1630## @brief Output current CMake variables to file.
1631function (basis_dump_variables RESULT_FILE)
1632  set (DUMP)
1633  get_cmake_property (VARIABLE_NAMES VARIABLES)
1634  foreach (V IN LISTS VARIABLE_NAMES)
1635    if (NOT V MATCHES "^_|^RESULT_FILE$|^ARGC$|^ARGV[0-9]?$|^ARGN_")
1636      set (VALUE "${${V}}")
1637      # sanitize value for use in set() command
1638      string (REPLACE "\\" "\\\\" VALUE "${VALUE}") # escape backspaces
1639      string (REPLACE "\"" "\\\"" VALUE "${VALUE}") # escape double quotes
1640      # Escape ${VAR} by \${VAR} such that CMake does not evaluate it.
1641      # Escape $STR{VAR} by \$STR{VAR} such that CMake does not report a
1642      # syntax error b/c it expects either ${VAR}, $ENV{VAR}, or $CACHE{VAR}.
1643      # Escape @VAR@ by \@VAR\@ such that CMake does not evaluate it.
1644      string (REGEX REPLACE "([^\\])\\\$([^ ]*){" "\\1\\\\\$\\2{" VALUE "${VALUE}")
1645      string (REGEX REPLACE "([^\\])\\\@([^ ]*)\@" "\\1\\\\\@\\2\\\\\@" VALUE "${VALUE}")
1646      # append variable to output file
1647      set (DUMP "${DUMP}set (${V} \"${VALUE}\")\n")
1648    endif ()
1649  endforeach ()
1650  file (WRITE "${RESULT_FILE}" "# CMake variables dump created by BASIS\n${DUMP}")
1651endfunction ()
1652
1653# ----------------------------------------------------------------------------
1654## @brief Write CMake script file which sets the named variable to the
1655#         specified (list of) values.
1656function (basis_write_list FILENAME VARIABLE)
1657  file (WRITE "${FILENAME}" "# Automatically generated. Do not edit this file!\nset (${VARIABLE}\n")
1658  foreach (V IN LISTS ARGN)
1659    file (APPEND "${FILENAME}" "  \"${V}\"\n")
1660  endforeach ()
1661  file (APPEND "${FILENAME}" ")\n")
1662endfunction ()
1663
1664# ----------------------------------------------------------------------------
1665## @brief Set a named property in a given scope.
1666#
1667# This function replaces CMake's
1668# <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property">
1669# set_property()</a> command.
1670#
1671# @param [in] SCOPE The argument for the @p SCOPE parameter of
1672#                   <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property">
1673#                   set_property()</a>.
1674# @param [in] ARGN  Arguments as accepted by.
1675#                   <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property">
1676#                   set_property()</a>.
1677#
1678# @returns Sets the specified property.
1679#
1680# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property
1681#
1682# @ingroup CMakeAPI
1683if (BASIS_USE_TARGET_UIDS)
1684  function (basis_set_property SCOPE)
1685    if (SCOPE MATCHES "^TARGET$|^TEST$")
1686      # map target/test names to UIDs
1687      list (LENGTH ARGN ARGN_LENGTH)
1688      if (ARGN_LENGTH EQUAL 0)
1689        message (FATAL_ERROR "basis_set_property(${SCOPE}): Expected arguments after SCOPE!")
1690      endif ()
1691      set (IDX 0)
1692      set (ARG)
1693      while (IDX LESS ARGN_LENGTH)
1694        list (GET ARGN ${IDX} ARG)
1695        if (ARG MATCHES "^APPEND$")
1696          math (EXPR IDX "${IDX} + 1")
1697          list (GET ARGN ${IDX} ARG)
1698          if (NOT ARG MATCHES "^PROPERTY$")
1699            message (FATAL_ERROR "basis_set_properties(${SCOPE}): Expected PROPERTY keyword after APPEND!")
1700          endif ()
1701          break ()
1702        elseif (ARG MATCHES "^PROPERTY$")
1703          break ()
1704        else ()
1705          if (SCOPE MATCHES "^TEST$")
1706            basis_get_test_uid (UID "${ARG}")
1707          else ()
1708            basis_get_target_uid (UID "${ARG}")
1709          endif ()
1710          list (INSERT ARGN ${IDX} "${UID}")
1711          math (EXPR IDX "${IDX} + 1")
1712          list (REMOVE_AT ARGN ${IDX}) # after insert to avoid index out of range
1713        endif ()
1714      endwhile ()
1715      if (IDX EQUAL ARGN_LENGTH)
1716        message (FATAL_ERROR "basis_set_properties(${SCOPE}): Missing PROPERTY keyword!")
1717      endif ()
1718      math (EXPR IDX "${IDX} + 1")
1719      list (GET ARGN ${IDX} ARG)
1720      # property name matches DEPENDS
1721      if (ARG MATCHES "DEPENDS")
1722        math (EXPR IDX "${IDX} + 1")
1723        while (IDX LESS ARGN_LENGTH)
1724          list (GET ARGN ${IDX} ARG)
1725          if (SCOPE MATCHES "^TEST$")
1726            basis_get_test_uid (UID "${ARG}")
1727          else ()
1728            basis_get_target_uid (UID "${ARG}")
1729          endif ()
1730          list (INSERT ARGN ${IDX} "${UID}")
1731          math (EXPR IDX "${IDX} + 1")
1732          list (REMOVE_AT ARGN ${IDX}) # after insert to avoid index out of range
1733        endwhile ()
1734      endif ()
1735    endif ()
1736    if (BASIS_DEBUG)
1737      message ("** basis_set_property():")
1738      message ("**   Scope:     ${SCOPE}")
1739      message ("**   Arguments: [${ARGN}]")
1740    endif ()
1741    set_property (${SCOPE} ${ARGN})
1742  endfunction ()
1743else ()
1744  macro (basis_set_property)
1745    set_property (${ARGV})
1746  endmacro ()
1747endif ()
1748
1749# ----------------------------------------------------------------------------
1750## @brief Get a property.
1751#
1752# This function replaces CMake's
1753# <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
1754# get_property()</a> command.
1755#
1756# @param [out] VAR     Property value.
1757# @param [in]  SCOPE   The argument for the @p SCOPE argument of
1758#                      <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
1759#                      get_property()</a>.
1760# @param [in]  ELEMENT The argument for the @p ELEMENT argument of
1761#                      <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
1762#                      get_property()</a>.
1763# @param [in]  ARGN    Arguments as accepted by
1764#                      <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
1765#                      get_property()</a>.
1766#
1767# @returns Sets @p VAR to the value of the requested property.
1768#
1769# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property
1770#
1771# @ingroup CMakeAPI
1772if (BASIS_USE_TARGET_UIDS)
1773  function (basis_get_property VAR SCOPE ELEMENT)
1774    if (SCOPE MATCHES "^TARGET$")
1775      basis_get_target_uid (ELEMENT "${ELEMENT}")
1776    elseif (SCOPE MATCHES "^TEST$")
1777      basis_get_test_uid (ELEMENT "${ELEMENT}")
1778    endif ()
1779    get_property (VALUE ${SCOPE} ${ELEMENT} ${ARGN})
1780    set ("${VAR}" "${VALUE}" PARENT_SCOPE)
1781  endfunction ()
1782else ()
1783  macro (basis_get_property)
1784    get_property (${ARGV})
1785  endmacro ()
1786endif ()
1787
1788# ----------------------------------------------------------------------------
1789## @brief Set project-global property.
1790#
1791# Set property associated with current project/module. The property is in
1792# fact just a cached variable whose name is prefixed by the project's name.
1793function (basis_set_project_property)
1794  CMAKE_PARSE_ARGUMENTS (
1795    ARGN
1796      "APPEND"
1797      "PROJECT"
1798      "PROPERTY"
1799    ${ARGN}
1800  )
1801
1802  if (NOT ARGN_PROJECT)
1803    set (ARGN_PROJECT "${PROJECT_NAME}")
1804  endif ()
1805  if (NOT ARGN_PROPERTY)
1806    message (FATAL_ERROR "Missing PROPERTY argument!")
1807  endif ()
1808
1809  list (GET ARGN_PROPERTY 0 PROPERTY_NAME)
1810  list (REMOVE_AT ARGN_PROPERTY 0) # remove property name from values
1811
1812  if (ARGN_APPEND)
1813    basis_get_project_property (CURRENT ${ARGN_PROJECT} ${PROPERTY_NAME})
1814    if (NOT "${CURRENT}" STREQUAL "")
1815      list (INSERT ARGN_PROPERTY 0 "${CURRENT}")
1816    endif ()
1817  endif ()
1818
1819  set (
1820    ${ARGN_PROJECT}_${PROPERTY_NAME}
1821      "${ARGN_PROPERTY}"
1822    CACHE INTERNAL
1823      "Property ${PROPERTY_NAME} of project ${ARGN_PROJECT}."
1824    FORCE
1825  )
1826endfunction ()
1827
1828# ----------------------------------------------------------------------------
1829## @brief Get project-global property value.
1830#
1831# Example:
1832# @code
1833# basis_get_project_property(TARGETS)
1834# basis_get_project_property(TARGETS ${PROJECT_NAME})
1835# basis_get_project_property(TARGETS ${PROJECT_NAME} TARGETS)
1836# basis_get_project_property(TARGETS PROPERTY TARGETS)
1837# @endcode
1838#
1839# @param [out] VARIABLE Name of result variable.
1840# @param [in]  ARGN     See the example uses. The optional second argument
1841#                       is either the name of the project similar to CMake's
1842#                       get_target_property() command or the keyword PROPERTY
1843#                       followed by the name of the property.
1844function (basis_get_project_property VARIABLE)
1845  if (ARGC GREATER 3)
1846    message (FATAL_ERROR "Too many arguments!")
1847  endif ()
1848  if (ARGC EQUAL 1)
1849    set (ARGN_PROJECT "${PROJECT_NAME}")
1850    set (ARGN_PROPERTY "${VARIABLE}")
1851  elseif (ARGC EQUAL 2)
1852    if (ARGV1 MATCHES "^PROPERTY$")
1853      message (FATAL_ERROR "Expected argument after PROPERTY keyword!")
1854    endif ()
1855    set (ARGN_PROJECT  "${ARGV1}")
1856    set (ARGN_PROPERTY "${VARIABLE}")
1857  else ()
1858    if (ARGV1 MATCHES "^PROPERTY$")
1859      set (ARGN_PROJECT "${PROJECT_NAME}")
1860    else ()
1861      set (ARGN_PROJECT  "${ARGV1}")
1862    endif ()
1863    set (ARGN_PROPERTY "${ARGV2}")
1864  endif ()
1865  set (${VARIABLE} "${${ARGN_PROJECT}_${ARGN_PROPERTY}}" PARENT_SCOPE)
1866endfunction ()
1867
1868# ============================================================================
1869# list / string manipulations
1870# ============================================================================
1871
1872# ----------------------------------------------------------------------------
1873## @brief Sanitize string variable for use in regular expression.
1874#
1875# @note This function may not work for all cases, but is used in particular
1876#       to sanitize project names, target names, namespace identifiers,...
1877#
1878#       This takes all of the dollar signs, and other special characters and
1879#       adds escape characters such as backslash as necessary.
1880#
1881# @param [out] OUT String that can be used in regular expression.
1882# @param [in]  STR String to sanitize.
1883macro (basis_sanitize_for_regex OUT STR)
1884  string (REGEX REPLACE "([.+*?^$])" "\\\\\\1" ${OUT} "${STR}")
1885endmacro ()
1886
1887# ----------------------------------------------------------------------------
1888## @brief Concatenates all list elements into a single string.
1889#
1890# The list elements are concatenated without any delimiter in between.
1891# Use basis_list_to_delimited_string() to specify a delimiter such as a
1892# whitespace character or comma (,) as delimiter.
1893#
1894# @param [out] STR  Output string.
1895# @param [in]  ARGN Input list.
1896#
1897# @returns Sets @p STR to the resulting string.
1898#
1899# @sa basis_list_to_delimited_string()
1900function (basis_list_to_string STR)
1901  set (OUT)
1902  foreach (ELEM IN LISTS ARGN)
1903    set (OUT "${OUT}${ELEM}")
1904  endforeach ()
1905  set ("${STR}" "${OUT}" PARENT_SCOPE)
1906endfunction ()
1907
1908# ----------------------------------------------------------------------------
1909## @brief Concatenates all list elements into a single delimited string.
1910#
1911# @param [out] STR   Output string.
1912# @param [in]  DELIM Delimiter used to separate list elements.
1913#                    Each element which contains the delimiter as substring
1914#                    is surrounded by double quotes (") in the output string.
1915# @param [in]  ARGN  Input list. If this list starts with the argument
1916#                    @c NOAUTOQUOTE, the automatic quoting of list elements
1917#                    which contain the delimiter is disabled.
1918#
1919# @returns Sets @p STR to the resulting string.
1920#
1921# @see basis_join
1922#
1923# @todo consider replacing basis_list_to_delimited_string with basis_join
1924function (basis_list_to_delimited_string STR DELIM)
1925  set (OUT)
1926  set (AUTOQUOTE TRUE)
1927  if (ARGN)
1928    list (GET ARGN 0 FIRST)
1929    if (FIRST MATCHES "^NOAUTOQUOTE$")
1930      list (REMOVE_AT ARGN 0)
1931      set (AUTOQUOTE FALSE)
1932    endif ()
1933  endif ()
1934  basis_sanitize_for_regex (DELIM_RE "${DELIM}")
1935  foreach (ELEM ${ARGN})
1936    if (OUT)
1937      set (OUT "${OUT}${DELIM}")
1938    endif ()
1939    if (AUTOQUOTE AND ELEM MATCHES "${DELIM_RE}")
1940      set (OUT "${OUT}\"${ELEM}\"")
1941    else ()
1942      set (OUT "${OUT}${ELEM}")
1943    endif ()
1944  endforeach ()
1945  set ("${STR}" "${OUT}" PARENT_SCOPE)
1946endfunction ()
1947
1948# ----------------------------------------------------------------------------
1949## @brief Concatenates all list elements into a single delimited string.
1950#
1951# @param [in]  VALUES       Input list string.
1952# @param [in]  DELIMITER    Delimiter glue used to separate list elements.
1953#                           Each element which contains the delimiter as substring
1954#                           is surrounded by double quotes (") in the output string.
1955# @param [out] OUTPUT       Output string variable name.
1956#
1957# @code
1958#   set( letters "" "\;a" b c "d\;d" )
1959#   basis_join("${letters}" ":" output)
1960#   message("${output}") # :;a:b:c:d;d
1961# @endcode
1962#
1963# @returns Sets @p OUTPUT to the resulting string.
1964#
1965# @see basis_list_to_delimited_string
1966function(basis_join VALUES DELIMITER OUTPUT)
1967  string (REGEX REPLACE "([^\\]|^);" "\\1${DELIMITER}" _TMP_STR "${VALUES}")
1968  string (REGEX REPLACE "[\\](.)" "\\1" _TMP_STR "${_TMP_STR}") #fixes escaping
1969  set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE)
1970endfunction()
1971
1972# ----------------------------------------------------------------------------
1973## @brief Splits a string at space characters into a list.
1974#
1975# @todo Probably this can be done in a better way...
1976#       Difficulty is, that string(REPLACE) does always replace all
1977#       occurrences. Therefore, we need a regular expression which matches
1978#       the entire string. More sophisticated regular expressions should do
1979#       a better job, though.
1980#
1981# @param [out] LST  Output list.
1982# @param [in]  STR  Input string.
1983#
1984# @returns Sets @p LST to the resulting CMake list.
1985function (basis_string_to_list LST STR)
1986  set (TMP "${STR}")
1987  set (OUT)
1988  # 1. extract elements such as "a string with spaces"
1989  while (TMP MATCHES "\"[^\"]*\"")
1990    string (REGEX REPLACE "^(.*)\"([^\"]*)\"(.*)$" "\\1\\3" TMP "${TMP}")
1991    if (OUT)
1992      set (OUT "${CMAKE_MATCH_2};${OUT}")
1993    else (OUT)
1994      set (OUT "${CMAKE_MATCH_2}")
1995    endif ()
1996  endwhile ()
1997  # 2. extract other elements separated by spaces (excluding first and last)
1998  while (TMP MATCHES " [^\" ]+ ")
1999    string (REGEX REPLACE "^(.*) ([^\" ]+) (.*)$" "\\1\\3" TMP "${TMP}")
2000    if (OUT)
2001      set (OUT "${CMAKE_MATCH_2};${OUT}")
2002    else (OUT)
2003      set (OUT "${CMAKE_MATCH_2}")
2004    endif ()
2005  endwhile ()
2006  # 3. extract first and last elements (if not done yet)
2007  if (TMP MATCHES "^[^\" ]+")
2008    set (OUT "${CMAKE_MATCH_0};${OUT}")
2009  endif ()
2010  if (NOT "${CMAKE_MATCH_0}" STREQUAL "${TMP}" AND TMP MATCHES "[^\" ]+$")
2011    set (OUT "${OUT};${CMAKE_MATCH_0}")
2012  endif ()
2013  # return resulting list
2014  set (${LST} "${OUT}" PARENT_SCOPE)
2015endfunction ()
2016
2017# ----------------------------------------------------------------------------
2018## @brief Compare two lists.
2019#
2020# @param [out] RESULT Result of comparison.
2021# @param [in]  LIST1  Name of variable holding the first list.
2022# @param [in]  LIST2  Name of varaible holding the second list.
2023#
2024# @retval 0 The two lists are not identical.
2025# @retval 1 Both lists have identical elements (not necessarily in the same order).
2026macro (basis_compare_lists RESULT LIST1 LIST2)
2027  set (_L1 "${${LIST1}}")
2028  set (_L2 "${${LIST2}}")
2029  list (SORT _L1)
2030  list (SORT _L2)
2031  if ("${_L1}" STREQUAL "${_L2}")
2032    set (RESULT TRUE)
2033  else ()
2034    set (RESULT FALSE)
2035  endif ()
2036  unset (_L1)
2037  unset (_L2)
2038endmacro ()
2039
2040# ============================================================================
2041# name <=> UID
2042# ============================================================================
2043
2044# ----------------------------------------------------------------------------
2045## @brief Derive target name from source file name.
2046#
2047# @param [out] TARGET_NAME Target name.
2048# @param [in]  SOURCE_FILE Source file.
2049# @param [in]  ARGN        Third argument to get_filename_component().
2050#                          If not specified, the given path is only sanitized.
2051#
2052# @returns Target name derived from @p SOURCE_FILE.
2053function (basis_get_source_target_name TARGET_NAME SOURCE_FILE)
2054  # remove ".in" suffix from file name
2055  string (REGEX REPLACE "\\.in$" "" OUT "${SOURCE_FILE}")
2056  # get name component
2057  if (ARGC GREATER 2)
2058    get_filename_component (OUT "${OUT}" ${ARGV2})
2059  endif ()
2060  # replace special characters
2061  string (REGEX REPLACE "[./\\]" "_" OUT "${OUT}")
2062  # return
2063  set (${TARGET_NAME} "${OUT}" PARENT_SCOPE)
2064endfunction ()
2065
2066# ----------------------------------------------------------------------------
2067## @brief Strip of top-level package name from target UID if present.
2068#
2069# If @c BASIS_USE_FULLY_QUALIFIED_TARGET_UID is @c ON, the top-level package
2070# name is always preserved and this operation does nothing.
2071#
2072# @param[in,out] TARGET_UID "Global" target name, i.e., actual CMake target name.
2073#
2074# @returns Sets @p TARGET_UID to the (stripped) UID.
2075function (basis_strip_target_uid TARGET_UID)
2076  if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
2077    basis_sanitize_for_regex (RE "${TOPLEVEL_PROJECT_NAMESPACE_CMAKE}")
2078    string (REGEX REPLACE "^\\.*${RE}\\." "" UID "${${TARGET_UID}}")
2079    set ("${TARGET_UID}" "${UID}" PARENT_SCOPE)
2080  endif ()
2081endfunction ()
2082
2083# ----------------------------------------------------------------------------
2084## @brief Make target UID from given target name.
2085#
2086# This function is intended for internal use only.
2087#
2088# @param [out] TARGET_UID  "Global" target name, i.e., actual CMake target name.
2089# @param [in]  TARGET_NAME Target name used as argument to BASIS CMake functions.
2090#
2091# @returns Sets @p TARGET_UID to the UID of the build target @p TARGET_NAME.
2092#
2093# @sa basis_make_target_uid()
2094function (_basis_make_target_uid TARGET_UID TARGET_NAME)
2095  if (TARGET_NAME MATCHES "^\\.+(.*)$")
2096    set (${TARGET_UID} "${CMAKE_MATCH_1}" PARENT_SCOPE)
2097  else ()
2098    set (UID "${PROJECT_NAMESPACE_CMAKE}.${TARGET_NAME}")
2099    basis_strip_target_uid (UID)
2100    set (${TARGET_UID} "${UID}" PARENT_SCOPE)
2101  endif ()
2102endfunction ()
2103
2104# ----------------------------------------------------------------------------
2105## @brief Make target UID from given target name.
2106#
2107# This function is intended for use by the basis_add_*() functions only.
2108#
2109# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2110# always just sets the @p TARGET_UID to the given @p TARGET_NAME.
2111#
2112# @param [out] TARGET_UID  "Global" target name, i.e., actual CMake target name.
2113# @param [in]  TARGET_NAME Target name used as argument to BASIS CMake functions.
2114#
2115# @returns Sets @p TARGET_UID to the UID of the build target @p TARGET_NAME.
2116#
2117# @sa basis_get_target_uid()
2118if (BASIS_USE_TARGET_UIDS)
2119  function (basis_make_target_uid TARGET_UID TARGET_NAME)
2120    _basis_make_target_uid (UID "${TARGET_NAME}")
2121    set ("${TARGET_UID}" "${UID}" PARENT_SCOPE)
2122  endfunction ()
2123else ()
2124  function (basis_make_target_uid TARGET_UID TARGET_NAME)
2125    if (TARGET_NAME MATCHES "^\\.+(.*)$")
2126      set ("${TARGET_UID}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
2127    else ()
2128      set ("${TARGET_UID}" "${TARGET_NAME}" PARENT_SCOPE)
2129    endif ()
2130  endfunction ()
2131endif ()
2132
2133# ----------------------------------------------------------------------------
2134## @brief Get "global" target name, i.e., actual CMake target name.
2135#
2136# In order to ensure that CMake target names are unique across modules of
2137# a BASIS project, the target name given to the BASIS CMake functions is
2138# converted by basis_make_target_uid() into a so-called target UID which is
2139# used as actual CMake target name. This function can be used to get for a
2140# given target name or UID the closest match of a known target UID.
2141#
2142# The individual parts of the target UID, i.e, package name,
2143# module name, and target name are separated by a dot (.).
2144# If @c BASIS_USE_FULLY_QUALIFIED_UIDS is set to @c OFF, the common part of
2145# all target UIDs is removed by this function from the target UID.
2146# When the target is exported, however, this common part will be
2147# prefixed again. This is done by the basis_export_targets() function.
2148#
2149# Note that names of imported targets are not prefixed in any case.
2150#
2151# The counterpart basis_get_target_name() can be used to convert the target UID
2152# back to the target name without namespace prefix.
2153#
2154# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2155# always just sets the @p TARGET_UID to the given @p TARGET_NAME.
2156#
2157# @note At the moment, BASIS does not support modules which themselves have
2158#       modules again. This would require a more nested namespace hierarchy
2159#       and makes things unnecessarily complicated.
2160#
2161# @param [out] TARGET_UID  "Global" target name, i.e., actual CMake target name.
2162# @param [in]  TARGET_NAME Target name used as argument to BASIS CMake functions.
2163#
2164# @returns Sets @p TARGET_UID to the UID of the build target @p TARGET_NAME.
2165#
2166# @sa basis_get_target_name()
2167if (BASIS_USE_TARGET_UIDS)
2168  function (basis_get_target_uid TARGET_UID TARGET_NAME)
2169    # in case of a leading namespace separator, do not modify target name
2170    if (TARGET_NAME MATCHES "^\\.+(.*)$")
2171      set (UID "${CMAKE_MATCH_1}")
2172    # otherwise,
2173    else ()
2174      set (UID "${TARGET_NAME}")
2175      basis_sanitize_for_regex (BASE_RE "${TOPLEVEL_PROJECT_NAMESPACE_CMAKE}")
2176      # try prepending namespace or parts of it until target is known,
2177      # first assuming the simplified UIDs without the common prefix
2178      # of this package which applies to targets of this package
2179      if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS AND NOT TARGET "${UID}")
2180        string (REGEX REPLACE "^${BASE_RE}\\." "" PREFIX "${PROJECT_NAMESPACE_CMAKE}")
2181        while (PREFIX)
2182          if (TARGET "${PREFIX}.${TARGET_NAME}")
2183            set (UID "${PREFIX}.${TARGET_NAME}")
2184            break ()
2185          else ()
2186            if (PREFIX MATCHES "(.*)\\.[^.]+")
2187              set (PREFIX "${CMAKE_MATCH_1}")
2188            else ()
2189              break ()
2190            endif ()
2191          endif ()
2192        endwhile ()
2193      endif ()
2194      # and then with the fully qualified UIDs for imported targets
2195      if (NOT TARGET "${UID}")
2196        set (PREFIX "${PROJECT_NAMESPACE_CMAKE}")
2197        while (PREFIX)
2198          if (TARGET "${PREFIX}.${TARGET_NAME}")
2199            set (UID "${PREFIX}.${TARGET_NAME}")
2200            break ()
2201          else ()
2202            if (PREFIX MATCHES "(.*)\\.[^.]+")
2203              set (PREFIX "${CMAKE_MATCH_1}")
2204            else ()
2205              break ()
2206            endif ()
2207          endif ()
2208        endwhile ()
2209      endif ()
2210    endif ()
2211    # strip off top-level namespace part (optional)
2212    if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
2213      string (REGEX REPLACE "^${BASE_RE}\\." "" UID "${UID}")
2214    endif ()
2215    # return
2216    set ("${TARGET_UID}" "${UID}" PARENT_SCOPE)
2217  endfunction ()
2218else ()
2219  function (basis_get_target_uid TARGET_UID TARGET_NAME)
2220    if (TARGET_NAME MATCHES "^\\.+(.*)$")
2221      set (${TARGET_UID} "${CMAKE_MATCH_1}" PARENT_SCOPE)
2222    else ()
2223      set ("${TARGET_UID}" "${TARGET_NAME}" PARENT_SCOPE)
2224    endif ()
2225  endfunction ()
2226endif ()
2227
2228# ----------------------------------------------------------------------------
2229## @brief Get fully-qualified target name.
2230#
2231# This function always returns a fully-qualified target UID, no matter if
2232# the option @c BASIS_USE_TARGET_UIDS or @c BASIS_USE_FULLY_QUALIFIED_UIDS
2233# is @c OFF. Note that if @c BASIS_USE_FULLY_QUALIFIED_UIDS is @c ON, the
2234# returned target UID may not be the actual name of a CMake target.
2235#
2236# @param [out] TARGET_UID  Fully-qualified target UID.
2237# @param [in]  TARGET_NAME Target name used as argument to BASIS CMake functions.
2238#
2239# @sa basis_get_target_uid()
2240function (basis_get_fully_qualified_target_uid TARGET_UID TARGET_NAME)
2241  if (BASIS_USE_TARGET_UIDS)
2242    basis_get_target_uid (UID "${TARGET_NAME}")
2243    if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
2244      if (TARGET "${UID}")
2245        get_target_property (IMPORTED "${UID}" IMPORTED)
2246        if (NOT IMPORTED)
2247          set (UID "${TOPLEVEL_PROJECT_NAMESPACE_CMAKE}.${UID}")
2248        endif ()
2249      else ()
2250        set (UID "${TOPLEVEL_PROJECT_NAMESPACE_CMAKE}.${UID}")
2251      endif ()
2252    endif ()
2253  else ()
2254    if (TARGET "${TARGET_NAME}")
2255      get_target_property (IMPORTED "${TARGET_NAME}" IMPORTED)
2256    else ()
2257      set (IMPORTED FALSE)
2258    endif ()
2259    if (IMPORTED)
2260      set (UID "${TARGET_NAME}")
2261    else ()
2262      set (UID "${PROJECT_NAMESPACE_CMAKE}.${TARGET_NAME}")
2263    endif ()
2264  endif ()
2265  set (${TARGET_UID} "${UID}" PARENT_SCOPE)
2266endfunction ()
2267
2268# ----------------------------------------------------------------------------
2269## @brief Get namespace of build target without check of UID.
2270#
2271# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2272# always just sets the @p TARGET_NS to an empty string.
2273#
2274# @param [out] TARGET_NS  Namespace part of target UID.
2275# @param [in]  TARGET_UID Target UID.
2276if (BASIS_USE_TARGET_UIDS)
2277  function (_basis_get_target_namespace TARGET_NS TARGET_UID)
2278    if (UID MATCHES "^(.*)\\.")
2279      set ("${TARGET_NS}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
2280    else ()
2281      set ("${TARGET_NS}" "" PARENT_SCOPE)
2282    endif ()
2283  endfunction ()
2284else ()
2285  function (_basis_get_target_namespace TARGET_NS TARGET_UID)
2286    set ("${TARGET_NS}" "" PARENT_SCOPE)
2287  endfunction ()
2288endif ()
2289
2290# ----------------------------------------------------------------------------
2291## @brief Get namespace of build target.
2292#
2293# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2294# always just sets the @p TARGET_NS to an empty string.
2295#
2296# @param [out] TARGET_NS  Namespace part of target UID.
2297# @param [in]  TARGET_UID Target UID/name.
2298if (BASIS_USE_TARGET_UIDS)
2299  function (basis_get_target_namespace TARGET_NS TARGET_UID)
2300    basis_get_fully_qualified_target_uid (UID "${TARGET_UID}")
2301    _basis_get_target_namespace (NS "${UID}")
2302    set ("${TARGET_NS}" "${NS}" PARENT_SCOPE)
2303  endfunction ()
2304else ()
2305  function (basis_get_target_namespace TARGET_NS TARGET_UID)
2306    set ("${TARGET_NS}" "" PARENT_SCOPE)
2307  endfunction ()
2308endif ()
2309
2310# ----------------------------------------------------------------------------
2311## @brief Get "local" target name, i.e., BASIS target name without check of UID.
2312#
2313# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2314# always just sets the @p TARGET_NAME to the given @p TARGET_UID.
2315#
2316# @param [out] TARGET_NAME Target name used as argument to BASIS functions.
2317# @param [in]  TARGET_UID  "Global" target name, i.e., actual CMake target name.
2318#
2319# @returns Sets @p TARGET_NAME to the name of the build target with UID @p TARGET_UID.
2320#
2321# @sa basis_get_target_name(), basis_get_target_uid()
2322if (BASIS_USE_TARGET_UIDS)
2323  function (_basis_get_target_name TARGET_NAME TARGET_UID)
2324    # strip off namespace of current project
2325    basis_sanitize_for_regex (RE "${PROJECT_NAMESPACE_CMAKE}")
2326    string (REGEX REPLACE "^${RE}\\." "" NAME "${UID}")
2327    # return
2328    set (${TARGET_NAME} "${NAME}" PARENT_SCOPE)
2329  endfunction ()
2330else ()
2331  function (_basis_get_target_name TARGET_NAME TARGET_UID)
2332    set (${TARGET_NAME} "${TARGET_UID}" PARENT_SCOPE)
2333  endfunction ()
2334endif ()
2335
2336# ----------------------------------------------------------------------------
2337## @brief Get "local" target name, i.e., BASIS target name.
2338#
2339# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2340# always just sets the @p TARGET_NAME to the given @p TARGET_UID.
2341#
2342# @param [out] TARGET_NAME Target name used as argument to BASIS functions.
2343# @param [in]  TARGET_UID  "Global" target name, i.e., actual CMake target name.
2344#
2345# @returns Sets @p TARGET_NAME to the name of the build target with UID @p TARGET_UID.
2346#
2347# @sa basis_get_target_uid()
2348if (BASIS_USE_TARGET_UIDS)
2349  function (basis_get_target_name TARGET_NAME TARGET_UID)
2350    basis_get_fully_qualified_target_uid (UID "${TARGET_UID}")
2351    _basis_get_target_name (NAME "${UID}")
2352    set (${TARGET_NAME} "${NAME}" PARENT_SCOPE)
2353  endfunction ()
2354else ()
2355  function (basis_get_target_name TARGET_NAME TARGET_UID)
2356    set (${TARGET_NAME} "${TARGET_UID}" PARENT_SCOPE)
2357  endfunction ()
2358endif ()
2359
2360# ----------------------------------------------------------------------------
2361## @brief Checks whether a given name is a valid target name.
2362#
2363# Displays fatal error message when target name is invalid.
2364#
2365# @param [in] TARGET_NAME Desired target name.
2366#
2367# @returns Nothing.
2368function (basis_check_target_name TARGET_NAME)
2369  # reserved target name ?
2370  foreach (PATTERN IN LISTS BASIS_RESERVED_TARGET_NAMES)
2371    if ("^${TARGET_NAME}$" STREQUAL "^${PATTERN}$")
2372      message (FATAL_ERROR "Target name \"${TARGET_NAME}\" is reserved and cannot be used.")
2373    endif ()
2374  endforeach ()
2375  # invalid target name ?
2376  if (NOT TARGET_NAME MATCHES "^\\.?[a-zA-Z]([a-zA-Z0-9_+.]|-)*$|^__init__(_py)?$")
2377    message (FATAL_ERROR "Target name '${TARGET_NAME}' is invalid.\nChoose a target name"
2378                         " which only contains alphanumeric characters,"
2379                         " '_', '-', or '+', and starts with a letter."
2380                         " The only exception from this rule is __init__[_py] for"
2381                         " a __init__.py script.\n")
2382  endif ()
2383
2384  # unique ?
2385  basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
2386  if (TARGET "${TARGET_UID}")
2387    message (FATAL_ERROR "There exists already a target named ${TARGET_UID}."
2388                         " Target names must be unique.")
2389  endif ()
2390endfunction ()
2391
2392# ----------------------------------------------------------------------------
2393## @brief Make test UID from given test name.
2394#
2395# This function is intended for use by the basis_add_test() only.
2396#
2397# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2398# always just sets the @p TEST_UID to the given @p TEST_NAME.
2399#
2400# @param [out] TEST_UID  "Global" test name, i.e., actual CTest test name.
2401# @param [in]  TEST_NAME Test name used as argument to BASIS CMake functions.
2402#
2403# @returns Sets @p TEST_UID to the UID of the test @p TEST_NAME.
2404#
2405# @sa basis_get_test_uid()
2406function (basis_make_test_uid TEST_UID TEST_NAME)
2407  basis_make_target_uid (UID "${TEST_NAME}")
2408  set ("${TEST_UID}" "${UID}" PARENT_SCOPE)
2409endfunction ()
2410
2411# ----------------------------------------------------------------------------
2412## @brief Get "global" test name, i.e., actual CTest test name.
2413#
2414# In order to ensure that CTest test names are unique across BASIS projects,
2415# the test name used by a developer of a BASIS project is converted by this
2416# function into another test name which is used as actual CTest test name.
2417#
2418# The function basis_get_test_name() can be used to convert the unique test
2419# name, the test UID, back to the original test name passed to this function.
2420#
2421# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2422# always just sets the @p TEST_UID to the given @p TEST_NAME.
2423#
2424# @param [out] TEST_UID  "Global" test name, i.e., actual CTest test name.
2425# @param [in]  TEST_NAME Test name used as argument to BASIS CMake functions.
2426#
2427# @returns Sets @p TEST_UID to the UID of the test @p TEST_NAME.
2428#
2429# @sa basis_get_test_name()
2430if (BASIS_USE_TARGET_UIDS)
2431  function (basis_get_test_uid TEST_UID TEST_NAME)
2432    if (TEST_NAME MATCHES "^\\.+(.*)$")
2433      set ("${TEST_UID}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
2434    else ()
2435      set (UID "${PROJECT_NAMESPACE_CMAKE}.${TEST_NAME}")
2436      basis_strip_target_uid (UID ${UID})
2437      set ("${TEST_UID}" "${UID}" PARENT_SCOPE)
2438    endif ()
2439  endfunction ()
2440else ()
2441  function (basis_get_test_uid TEST_UID TEST_NAME)
2442    if (TEST_NAME MATCHES "^\\.+(.*)$")
2443      set ("${TEST_UID}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
2444    else ()
2445      set ("${TEST_UID}" "${TEST_NAME}" PARENT_SCOPE)
2446    endif ()
2447  endfunction ()
2448endif ()
2449
2450# ----------------------------------------------------------------------------
2451## @brief Get "global" test name, i.e., actual CTest test name.
2452#
2453# This function always returns a fully-qualified test UID, no matter if
2454# the option @c BASIS_USE_FULLY_QUALIFIED_UIDS is @c OFF. Note that
2455# if this option is @c ON, the returned test UID may not be the
2456# actual name of a CMake test.
2457#
2458# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2459# always just sets the @p TEST_UID to the given @p TEST_NAME.
2460#
2461# @param [out] TEST_UID  Fully-qualified test UID.
2462# @param [in]  TEST_NAME Test name used as argument to BASIS CMake functions.
2463#
2464# @sa basis_get_test_uid()
2465if (BASIS_USE_TARGET_UIDS)
2466  function (basis_get_fully_qualified_test_uid TEST_UID TEST_NAME)
2467    if (TEST_NAME MATCHES "^\\.+(.*)$")
2468      set ("${TEST_UID}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
2469    else ()
2470      set ("${TEST_UID}" "${TOPLEVEL_PROJECT_NAMESPACE_CMAKE}.${TEST_NAME}" PARENT_SCOPE)
2471    endif ()
2472  endfunction ()
2473else ()
2474  function (basis_get_fully_qualified_test_uid TEST_UID TEST_NAME)
2475    if (TEST_NAME MATCHES "^\\.+(.*)$")
2476      set ("${TEST_UID}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
2477    else ()
2478      set ("${TEST_UID}" "${TEST_NAME}" PARENT_SCOPE)
2479    endif ()
2480  endfunction ()
2481endif ()
2482
2483# ----------------------------------------------------------------------------
2484## @brief Get namespace of test.
2485#
2486# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2487# always just sets the @p TEST_NS to an empty string.
2488#
2489# @param [out] TEST_NS  Namespace part of test UID. If @p TEST_UID is
2490#                       no UID, i.e., does not contain a namespace part,
2491#                       the namespace of this project is returned.
2492# @param [in]  TEST_UID Test UID/name.
2493if (BASIS_USE_TARGET_UIDS)
2494  function (basis_get_test_namespace TEST_NS TEST_UID)
2495    if (TEST_UID MATCHES "^(.*)\\.")
2496      set (${TEST_NS} "${CMAKE_MATCH_1}" PARENT_SCOPE)
2497    else ()
2498      set (${TEST_NS} "" PARENT_SCOPE)
2499    endif ()
2500  endfunction ()
2501else ()
2502  function (basis_get_test_namespace TEST_NS TEST_UID)
2503    set ("${TEST_UID}" "" PARENT_SCOPE)
2504  endfunction ()
2505endif ()
2506
2507# ----------------------------------------------------------------------------
2508## @brief Get "local" test name, i.e., BASIS test name.
2509#
2510# If @c BASIS_USE_TARGET_UIDS is set to @c OFF, this operation
2511# always just sets the @p TEST_NAME to the given @p TEST_UID.
2512#
2513# @param [out] TEST_NAME Test name used as argument to BASIS functions.
2514# @param [in]  TEST_UID  "Global" test name, i.e., actual CTest test name.
2515#
2516# @returns Sets @p TEST_NAME to the name of the test with UID @p TEST_UID.
2517#
2518# @sa basis_get_test_uid()
2519if (BASIS_USE_TARGET_UIDS)
2520  function (basis_get_test_name TEST_NAME TEST_UID)
2521    if (TEST_UID MATCHES "([^.]+)$")
2522      set (${TEST_NAME} "${CMAKE_MATCH_1}" PARENT_SCOPE)
2523    else ()
2524      set (${TEST_NAME} "" PARENT_SCOPE)
2525    endif ()
2526  endfunction ()
2527else ()
2528  function (basis_get_test_name TEST_NAME TEST_UID)
2529    set ("${TEST_NAME}" "${TEST_UID}" PARENT_SCOPE)
2530  endfunction ()
2531endif ()
2532
2533# ----------------------------------------------------------------------------
2534## @brief Checks whether a given name is a valid test name.
2535#
2536# Displays fatal error message when test name is invalid.
2537#
2538# @param [in] TEST_NAME Desired test name.
2539#
2540# @returns Nothing.
2541function (basis_check_test_name TEST_NAME)
2542  # reserved test name ?
2543  foreach (PATTERN IN LISTS BASIS_RESERVED_TARGET_NAMES)
2544    if ("^${TARGET_NAME}$" STREQUAL "^${PATTERN}$")
2545      message (FATAL_ERROR "Test name \"${TARGET_NAME}\" is reserved and cannot be used.")
2546    endif ()
2547  endforeach ()
2548  # invalid test name ?
2549  if (NOT TEST_NAME MATCHES "^\\.?[a-zA-Z]([a-zA-Z0-9_+.]|-)*$")
2550    message (FATAL_ERROR "Test name ${TEST_NAME} is invalid.\nChoose a test name "
2551                         " which only contains alphanumeric characters,"
2552                         " '_', '-', or '+', and starts with a letter.\n")
2553  endif ()
2554endfunction ()
2555
2556# ============================================================================
2557# common target tools
2558# ============================================================================
2559
2560# ----------------------------------------------------------------------------
2561## @brief Whether a given target exists.
2562#
2563# This function should be used instead of the if(TARGET) command of CMake
2564# because target names are mapped by BASIS to target UIDs.
2565#
2566# @param [out] RESULT_VARIABLE Boolean result variable.
2567# @param [in]  TARGET_NAME     Name which to check whether it is a target.
2568#
2569# @sa basis_make_target_uid()
2570# @sa basis_get_target_uid()
2571macro (basis_exists_target RESULT_VARIABLE TARGET_NAME)
2572  basis_get_target_uid (_UID "${TARGET_NAME}")
2573  if (TARGET ${_UID})
2574    set (${RESULT_VARIABLE} TRUE)
2575  else ()
2576    set (${RESULT_VARIABLE} FALSE)
2577  endif ()
2578  unset (_UID)
2579endmacro ()
2580
2581# ----------------------------------------------------------------------------
2582## @brief Get default subdirectory prefix of scripted library modules.
2583#
2584# @param [out] PREFIX   Name of variable which is set to the default library
2585#                       prefix, i.e., subdirectory relative to the library
2586#                       root directory as used for the @c PREFIX property of
2587#                       scripted module libraries (see basis_add_script_library())
2588#                       or relative to the include directory in case of C++.
2589#                       Note that this prefix includes a trailing slash to
2590#                       indicate that the prefix is a subdirectory.
2591# @param [in]  LANGUAGE Programming language (case-insenitive), e.g.,
2592#                       @c CXX, @c Python, @c Matlab...
2593macro (basis_library_prefix PREFIX LANGUAGE)
2594  string (TOUPPER "${LANGUAGE}" _LANGUAGE_U)
2595  if (PROJECT_NAMESPACE_${_LANGUAGE_U})
2596    basis_sanitize_for_regex (_RE "${BASIS_NAMESPACE_DELIMITER_${_LANGUAGE_U}}")
2597    string (REGEX REPLACE "${_RE}" "/" ${PREFIX} "${PROJECT_NAMESPACE_${_LANGUAGE_U}}")
2598    set (${PREFIX} "${${PREFIX}}/")
2599    unset (_RE)
2600  else ()
2601    message (FATAL_ERROR "basis_library_prefix(): PROJECT_NAMESPACE_${_LANGUAGE_U} not set!"
2602                         " Make sure that the LANGUAGE argument is supported and in"
2603                         " uppercase letters only.")
2604  endif ()
2605  unset (_LANGUAGE_U)
2606endmacro ()
2607
2608# ----------------------------------------------------------------------------
2609## @brief Get file name of compiled script.
2610#
2611# @param [out] CFILE  File path of compiled script file.
2612# @param [in]  SOURCE Script source file.
2613# @param [in]  ARGV2  Language of script file. If not specified, the language
2614#                     is derived from the file name extension and shebang of
2615#                     the script source file.
2616function (basis_get_compiled_file CFILE SOURCE)
2617  if (ARGC GREATER 2)
2618    set (LANGUAGE "${ARGV2}")
2619  else ()
2620    basis_get_source_language (LANGUAGE "${SOURCE}")
2621  endif ()
2622  set (${CFILE} "" PARENT_SCOPE)
2623  if (SOURCE)
2624    if (LANGUAGE MATCHES "PYTHON")
2625      set (${CFILE} "${SOURCE}c" PARENT_SCOPE)
2626    elseif (LANGUAGE MATCHES "JYTHON")
2627      if (SOURCE MATCHES "(.*)\\.([^.]+)$")
2628        set (${CFILE} "${CMAKE_MATCH_1}$${CMAKE_MATCH_2}.class" PARENT_SCOPE)
2629      endif ()
2630    endif ()
2631  endif ()
2632endfunction ()
2633
2634# ----------------------------------------------------------------------------
2635## @brief Get file path of Jython file compiled from the given Python module.
2636#
2637# Python modules are also compiled using Jython. This macro returns the file
2638# path of the compiled Jython file in the build tree which corresponds to the
2639# specified Python module.
2640#
2641# @param [out] CFILE  Path of corresponding compiled Jython file.
2642# @param [in]  MODULE Path of input Python module in build tree.
2643macro (basis_get_compiled_jython_file_of_python_module CFILE MODULE)
2644  if (BINARY_PYTHON_LIBRARY_DIR AND BINARY_JYTHON_LIBRARY_DIR)
2645    file (RELATIVE_PATH _GCJFOPM_REL "${BINARY_PYTHON_LIBRARY_DIR}" "${MODULE}")
2646  else ()
2647    set (_GCJFOPM_REL)
2648  endif ()
2649  if (NOT _GCJFOPM_REL MATCHES "^$|^\\.\\./")
2650    basis_get_compiled_file (${CFILE} "${BINARY_JYTHON_LIBRARY_DIR}/${_GCJFOPM_REL}" JYTHON)
2651  else ()
2652    basis_get_compiled_file (${CFILE} "${MODULE}" JYTHON)
2653  endif ()
2654  unset (_GCJFOPM_REL)
2655endmacro ()
2656
2657# ----------------------------------------------------------------------------
2658## @brief Whether to compile Python modules for Jython interpreter.
2659#
2660# This macro returns a boolean value stating whether Python modules shall also
2661# be compiled for use by Jython interpreter if BASIS_COMPILE_SCRIPTS is ON.
2662#
2663# @param [out] FLAG Set to either TRUE or FALSE depending on whether Python
2664#                   modules shall be compiled using Jython or not.
2665macro (basis_compile_python_modules_for_jython FLAG)
2666  if (BASIS_COMPILE_SCRIPTS AND JYTHON_EXECUTABLE)
2667    set (${FLAG} TRUE)
2668  else ()
2669    set (${FLAG} FALSE)
2670  endif ()
2671  if (DEFINED USE_JythonInterp AND NOT USE_JythonInterp)
2672    set (${FLAG} FALSE)
2673  endif ()
2674endmacro ()
2675
2676# ----------------------------------------------------------------------------
2677## @brief Glob source files.
2678#
2679# This function gets a list of source files and globbing expressions, evaluates
2680# the globbing expression, and replaces these by the found source files such
2681# that the resulting list of source files contains only absolute paths of
2682# source files. It is used by basis_add_executable() and basis_add_library()
2683# to get a list of all source files. The syntax for the glob expressions
2684# corresponds to the one used by CMake's
2685# <a href="http://www.cmake.org/cmake/help/v2.8.8/cmake.html#command:file">
2686# file(GLOB)</a> command. Additionally, if the pattern <tt>**</tt> is found
2687# in a glob expression, it is replaced by a single <tt>*</tt> and the
2688# recursive version, i.e., <tt>file(GLOB_RECURSE)</tt>, is used instead.
2689#
2690# @param [in]  TARGET_UID UID of build target which builds the globbed source files.
2691#                         The custom target which re-globs the source files
2692#                         before each build of this target is named after this
2693#                         build target with two leading underscores (__).
2694# @param [out] SOURCES    List of absolute source paths.
2695# @param [in]  ARGN       Input file paths and/or globbing expressions.
2696#
2697# @sa basis_add_executable()
2698# @sa basis_add_library()
2699function (basis_add_glob_target TARGET_UID SOURCES)
2700  # prepare globbing expressions
2701  # make paths absolute and turn directories into recursive globbing expressions
2702  set (EXPRESSIONS)
2703  foreach (EXPRESSION IN LISTS ARGN)
2704    if (NOT EXPRESSION MATCHES "^\\$<") # preserve generator expressions
2705      if (NOT IS_ABSOLUTE "${EXPRESSION}")
2706        # prefer configured/generated files in the build tree, but disallow
2707        # globbing within the build tree; glob only files in source tree
2708        if (NOT EXPRESSION MATCHES "[*?]|\\[[0-9]+-[0-9]+\\]"            AND
2709            EXISTS           "${CMAKE_CURRENT_BINARY_DIR}/${EXPRESSION}" AND
2710            NOT IS_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${EXPRESSION}")
2711          set (EXPRESSION "${CMAKE_CURRENT_BINARY_DIR}/${EXPRESSION}")
2712        else ()
2713          set (EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/${EXPRESSION}")
2714        endif ()
2715      endif ()
2716      if (IS_DIRECTORY "${EXPRESSION}")
2717        set (EXPRESSION "${EXPRESSION}/**")
2718      endif ()
2719    endif ()
2720    list (APPEND EXPRESSIONS "${EXPRESSION}")
2721  endforeach ()
2722  # only if at least one globbing expression is given we need to go through this hassle
2723  if (EXPRESSIONS MATCHES "[*?]|\\[[0-9]+-[0-9]+\\]")
2724    set (BUILD_DIR    "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TARGET_UID}.dir")
2725    set (SOURCES_FILE "${BUILD_DIR}/sources.txt")
2726    # get initial list of source files
2727    execute_process (
2728      COMMAND "${CMAKE_COMMAND}"
2729                  "-DEXPRESSIONS:STRING=${EXPRESSIONS}"
2730                  "-DINIT:BOOLEAN=TRUE"
2731                  "-DSOURCES_FILE:FILEPATH=${SOURCES_FILE}"
2732                  -P "${BASIS_MODULE_PATH}/glob.cmake"
2733      RESULT_VARIABLE RETVAL
2734    )
2735    if (NOT RETVAL EQUAL 0 OR NOT EXISTS "${SOURCES_FILE}")
2736      message (FATAL_ERROR "Target ${TARGET_UID}: Failed to glob source files!")
2737    endif ()
2738    # note that including this file here, which is modified whenever a
2739    # source file is added or removed, triggers a re-configuration of the
2740    # build system which is required to re-execute this function.
2741    include ("${SOURCES_FILE}")
2742    set (${SOURCES} "${INITIAL_SOURCES}" PARENT_SCOPE)
2743    # add custom target to re-glob files before each build
2744    set (ERRORMSG "You have either added, removed, or renamed a source file which"
2745                  " matches one of the globbing expressions specified for the"
2746                  " list of source files from which to build the ${TARGET_UID} target."
2747                  " Therefore, the build system must be re-configured. Either try to"
2748                  " build again which should trigger CMake and re-configure the build"
2749                  " system or run CMake manually.")
2750    basis_list_to_string (ERRORMSG ${ERRORMSG})
2751    add_custom_target (
2752      __${TARGET_UID}
2753      COMMAND "${CMAKE_COMMAND}"
2754                  "-DEXPRESSIONS:STRING=${EXPRESSIONS}"
2755                  "-DINIT:BOOLEAN=FALSE"
2756                  "-DSOURCES_FILE:FILEPATH=${SOURCES_FILE}"
2757                  "-DERRORMSG:STRING=${ERRORMSG}"
2758                  -P "${BASIS_MODULE_PATH}/glob.cmake"
2759      COMMENT "Checking if source files for target ${TARGET_UID} were added or removed"
2760      VERBATIM
2761    )
2762  # otherwise, just return the given absolute source file paths
2763  else ()
2764    set (${SOURCES} "${EXPRESSIONS}" PARENT_SCOPE)
2765  endif ()
2766endfunction ()
2767
2768# ----------------------------------------------------------------------------
2769## @brief Detect programming language of given source code files.
2770#
2771# This function determines the programming language in which the given source
2772# code files are written. If no common programming language could be determined,
2773# "AMBIGUOUS" is returned. If none of the following programming languages
2774# could be determined, "UNKNOWN" is returned: CXX (i.e., C++), JAVA, MATLAB,
2775# PYTHON, JYTHON, PERL, BASH, BATCH.
2776#
2777# @param [out] LANGUAGE Detected programming language.
2778# @param [in]  ARGN     List of source code files.
2779function (basis_get_source_language LANGUAGE)
2780  set (LANGUAGE_OUT)
2781  # iterate over source files
2782  foreach (SOURCE_FILE ${ARGN})
2783    # skip generator expressions
2784    if (NOT SOURCE_FILE MATCHES "^\\$<")
2785      get_filename_component (SOURCE_FILE "${SOURCE_FILE}" ABSOLUTE)
2786
2787      if (IS_DIRECTORY "${SOURCE_FILE}")
2788
2789        file (GLOB_RECURSE SOURCE_FILES "${SOURCE_FILE}/*")
2790        list (APPEND ARGN ${SOURCE_FILES})
2791
2792      else ()
2793
2794        # ------------------------------------------------------------------------
2795        # determine language based on extension for those without shebang
2796        set (LANG)
2797        # C++
2798        if (SOURCE_FILE MATCHES "\\.(c|cc|cpp|cxx|h|hh|hpp|hxx|txx|inl)(\\.in)?$")
2799          set (LANG "CXX")
2800        # Java
2801        elseif (SOURCE_FILE MATCHES "\\.java(\\.in)?$")
2802          set (LANG "JAVA")
2803        # MATLAB
2804        elseif (SOURCE_FILE MATCHES "\\.m(\\.in)?$")
2805          set (LANG "MATLAB")
2806        endif ()
2807
2808        # ------------------------------------------------------------------------
2809        # determine language from shebang directive
2810        #
2811        # Note that some scripting languages may use identical file name extensions.
2812        # This is in particular the case for Python and Jython. The only way we
2813        # can distinguish these two is by looking at the shebang directive.
2814        if (NOT LANG)
2815
2816          if (NOT EXISTS "${SOURCE_FILE}" AND EXISTS "${SOURCE_FILE}.in")
2817            set (SOURCE_FILE "${SOURCE_FILE}.in")
2818          endif ()
2819          if (EXISTS "${SOURCE_FILE}")
2820            file (STRINGS "${SOURCE_FILE}" FIRST_LINE LIMIT_COUNT 1)
2821            if (FIRST_LINE MATCHES "^#!")
2822              if (FIRST_LINE MATCHES "^#! */usr/bin/env +([^ ]+)")
2823                set (INTERPRETER "${CMAKE_MATCH_1}")
2824              elseif (FIRST_LINE MATCHES "^#! *([^ ]+)")
2825                set (INTERPRETER "${CMAKE_MATCH_1}")
2826                get_filename_component (INTERPRETER "${INTERPRETER}" NAME)
2827              else ()
2828                set (INTERPRETER)
2829              endif ()
2830              if (INTERPRETER MATCHES "^(python|jython|perl|bash)$")
2831                string (TOUPPER "${INTERPRETER}" LANG)
2832              endif ()
2833            endif ()
2834          endif ()
2835        endif ()
2836
2837        # ------------------------------------------------------------------------
2838        # determine language from further known extensions
2839        if (NOT LANG)
2840          # Python
2841          if (SOURCE_FILE MATCHES "\\.py(\\.in)?$")
2842            set (LANG "PYTHON")
2843          # Perl
2844          elseif (SOURCE_FILE MATCHES "\\.(pl|pm|t)(\\.in)?$")
2845            set (LANG "PERL")
2846          # BASH
2847          elseif (SOURCE_FILE MATCHES "\\.sh(\\.in)?$")
2848            set (LANG "BASH")
2849          # Batch
2850          elseif (SOURCE_FILE MATCHES "\\.bat(\\.in)?$")
2851            set (LANG "BATCH")
2852          # unknown
2853          else ()
2854            set (LANGUAGE_OUT "UNKNOWN")
2855            break ()
2856          endif ()
2857        endif ()
2858
2859        # ------------------------------------------------------------------------
2860        # detect ambiguity
2861        if (LANGUAGE_OUT AND NOT "^${LANG}$" STREQUAL "^${LANGUAGE_OUT}$")
2862          if (LANGUAGE_OUT MATCHES "CXX" AND LANG MATCHES "MATLAB")
2863            # MATLAB Compiler can handle this...
2864          elseif (LANGUAGE_OUT MATCHES "MATLAB" AND LANG MATCHES "CXX")
2865            set (LANG "MATLAB") # language stays MATLAB
2866          elseif (LANGUAGE_OUT MATCHES "PYTHON" AND LANG MATCHES "JYTHON")
2867            # Jython can deal with Python scripts/modules
2868          elseif (LANGUAGE_OUT MATCHES "JYTHON" AND LANG MATCHES "PYTHON")
2869            set (LANG "JYTHON") # language stays JYTHON
2870          else ()
2871            # ambiguity
2872            set (LANGUAGE_OUT "AMBIGUOUS")
2873            break ()
2874          endif ()
2875        endif ()
2876
2877        # update current language
2878        set (LANGUAGE_OUT "${LANG}")
2879      endif ()
2880    endif ()
2881  endforeach ()
2882  # return
2883  if (LANGUAGE_OUT)
2884    set (${LANGUAGE} "${LANGUAGE_OUT}" PARENT_SCOPE)
2885  else ()
2886    message (FATAL_ERROR "basis_get_source_language called without arguments!")
2887  endif ()
2888endfunction ()
2889
2890# ----------------------------------------------------------------------------
2891## @brief Configure .in source files.
2892#
2893# This function configures each source file in the given argument list with
2894# a .in file name suffix and stores the configured file in the build tree
2895# with the same relative directory as the template source file itself.
2896# The first argument names the CMake variable of the list of configured
2897# source files where each list item is the absolute file path of the
2898# corresponding (configured) source file.
2899#
2900# @param [out] LIST_NAME Name of output list.
2901# @param [in]  ARGN      These arguments are parsed and the following
2902#                        options recognized. All remaining arguments are
2903#                        considered to be source file paths.
2904# @par
2905# <table border="0">
2906#   <tr>
2907#     @tp @b BINARY_DIRECTORY @endtp
2908#     <td>Explicitly specify directory in build tree where configured
2909#         source files should be written to.</td>
2910#   </tr>
2911#   <tr>
2912#     @tp @b KEEP_DOT_IN_SUFFIX @endtp
2913#     <td>By default, after a source file with the .in extension has been
2914#         configured, the .in suffix is removed from the file name.
2915#         This can be omitted by giving this option.</td>
2916#   </tr>
2917# </table>
2918#
2919# @returns Nothing.
2920function (basis_configure_sources LIST_NAME)
2921  # parse arguments
2922  CMAKE_PARSE_ARGUMENTS (ARGN "KEEP_DOT_IN_SUFFIX" "BINARY_DIRECTORY" "" ${ARGN})
2923
2924  # ensure that specified BINARY_DIRECTORY is inside build tree of project
2925  if (ARGN_BINARY_DIRECTORY)
2926    get_filename_component (_binpath "${ARGN_BINARY_DIRECTORY}" ABSOLUTE)
2927    file (RELATIVE_PATH _relpath "${PROJECT_BINARY_DIR}" "${_binpath}")
2928    if (_relpath MATCHES "^\\.\\./")
2929      message (FATAL_ERROR "Specified BINARY_DIRECTORY must be inside the build tree!")
2930    endif ()
2931    unset (_binpath)
2932    unset (_relpath)
2933  endif ()
2934
2935  # configure source files
2936  set (CONFIGURED_SOURCES)
2937  foreach (SOURCE ${ARGN_UNPARSED_ARGUMENTS})
2938    # The .in suffix is optional, add it here if a .in file exists for this
2939    # source file, but only if the source file itself does not name an actually
2940    # existing source file.
2941    #
2942    # If the source file path is relative, prefer possibly already configured
2943    # sources in build tree such as the test driver source file created by
2944    # create_test_sourcelist() or a manual use of configure_file().
2945    #
2946    # Note: Make path absolute, otherwise EXISTS check will not work!
2947    if (NOT IS_ABSOLUTE "${SOURCE}")
2948      if (EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}")
2949        set (SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}")
2950      elseif (EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}.in")
2951        set (SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}.in")
2952      elseif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}")
2953        set (SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}")
2954      elseif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.in")
2955        set (SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.in")
2956      endif ()
2957    else ()
2958      if (NOT EXISTS "${SOURCE}" AND EXISTS "${SOURCE}.in")
2959        set (SOURCE "${SOURCE}.in")
2960      endif ()
2961    endif ()
2962    # configure source file if filename ends in .in suffix
2963    if (SOURCE MATCHES "\\.in$")
2964      # if binary directory was given explicitly, use it
2965      if (ARGN_BINARY_DIRECTORY)
2966        get_filename_component (SOURCE_NAME "${SOURCE}" NAME)
2967        if (NOT ARGN_KEEP_DOT_IN_SUFFIX)
2968          string (REGEX REPLACE "\\.in$" "" SOURCE_NAME "${SOURCE_NAME}")
2969        endif ()
2970        set (CONFIGURED_SOURCE "${ARGN_BINARY_DIRECTORY}/${SOURCE_NAME}")
2971      # otherwise,
2972      else ()
2973        # if source is in project's source tree use relative binary directory
2974        if ("^${SOURCE}$" STREQUAL "^${PROJECT_SOURCE_DIR}$")
2975          basis_get_relative_path (CONFIGURED_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}" "${SOURCE}")
2976          get_filename_component (CONFIGURED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${CONFIGURED_SOURCE}" ABSOLUTE)
2977          if (NOT ARGN_KEEP_DOT_IN_SUFFIX)
2978            string (REGEX REPLACE "\\.in$" "" CONFIGURED_SOURCE "${CONFIGURED_SOURCE}")
2979          endif ()
2980        # otherwise, use current binary directory
2981        else ()
2982          get_filename_component (SOURCE_NAME "${SOURCE}" NAME)
2983          if (NOT ARGN_KEEP_DOT_IN_SUFFIX)
2984            string (REGEX REPLACE "\\.in$" "" SOURCE_NAME "${SOURCE_NAME}")
2985          endif ()
2986          set (CONFIGURED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE_NAME}")
2987        endif ()
2988      endif ()
2989      # configure source file
2990      configure_file ("${SOURCE}" "${CONFIGURED_SOURCE}" @ONLY)
2991      if (BASIS_DEBUG)
2992        message ("** Configured source file with .in extension")
2993      endif ()
2994    # otherwise, skip configuration of this source file
2995    else ()
2996      set (CONFIGURED_SOURCE "${SOURCE}")
2997      if (BASIS_DEBUG)
2998        message ("** Skipped configuration of source file")
2999      endif ()
3000    endif ()
3001    if (BASIS_DEBUG)
3002      message ("**     Source:            ${SOURCE}")
3003      message ("**     Configured source: ${CONFIGURED_SOURCE}")
3004    endif ()
3005    list (APPEND CONFIGURED_SOURCES "${CONFIGURED_SOURCE}")
3006  endforeach ()
3007  # return
3008  set (${LIST_NAME} "${CONFIGURED_SOURCES}" PARENT_SCOPE)
3009endfunction ()
3010
3011# ----------------------------------------------------------------------------
3012## @brief Remove one blank line from top of string
3013macro (basis_remove_blank_line STRVAR)
3014  if (${STRVAR} MATCHES "(^|(.*)\n)[ \t]*\n(.*)")
3015    set (${STRVAR} "${CMAKE_MATCH_1}${CMAKE_MATCH_3}")
3016  endif ()
3017endmacro ()
3018
3019# ----------------------------------------------------------------------------
3020## @brief Configure and optionally compile script file.
3021#
3022# This function is used to configure script files during the build. It is
3023# called by the build script generated by basis_add_script_target() for each script
3024# target. It is also used to configure the modules of the packages
3025# implemented in supported scripting languages which are located in the
3026# @c PROJECT_LIBRARY_DIR of the source tree.
3027#
3028# In case of executable scripts, this function automatically prepends the
3029# module search paths such that the modules of this software package are found
3030# (and preferred in case of potential name conflicts with other packages).
3031# Moreover, it adds (or replaces) the shebang directive on Unix such that the
3032# configured interpreter version is used. On Windows, it converts the executable
3033# script into a Windows Command instead which executes the proper interpreter
3034# with the code section of the input script.
3035#
3036# @param [in] INPUT  Input script file.
3037# @param [in] OUTPUT Configured output script file.
3038# @param [in] ARGN   Optional arguments:
3039# @par
3040# <table border=0>
3041#   <tr>
3042#     @tp @b COMPILE @endtp
3043#     <td>Whether to compile module scripts if suitable, i.e., an intermediate
3044#         format exists for the specific scripting language. For example,
3045#         Python modules can be compiled.</td>
3046#   </tr>
3047#     @tp @b CONFIGURATION name @endtp
3048#     <td>Name of build configuration.</td>
3049#   </tr>
3050#   <tr>
3051#     @tp @b COPYONLY @endtp
3052#     <td>Whether to only copy the script file without replacing CMake variables
3053#         within the file. This option is passed on to CMake's configure_file()
3054#         command used to configure the script file. By default, the option
3055#         \@ONLY is used instead.</td>
3056#   </tr>
3057#   <tr>
3058#     @tp @b EXECUTABLE @endtp
3059#     <td>Specifies that the given script file is an executable script and not a
3060#         module script. Otherwise, if this option is not given and the output
3061#         file name contains a file name extension, the given script file is
3062#         configured as module script. A script file with an output file name
3063#         that has no extension, is always considered to be an executable.</td>
3064#   </tr>
3065#   <tr>
3066#     @tp @b DIRECTORY dir @endtp
3067#     <td>Build tree directory of configured script. By default, the directory
3068#         of the output script file is used to set the @c __DIR__ variable
3069#         used to make absolute file paths relative to the configured build
3070#         tree script file in the script configuration file
3071#         (see basis_set_script_path()).
3072#   </tr>
3073#   <tr>
3074#     @tp @b DESTINATION dir @endtp
3075#     <td>Installation directory for configured script. If this option is given,
3076#         the @c BUILD_INSTALL_SCRIPT variable is set to @c TRUE before including
3077#         any specified script configuration files (see @p CONFIG_FILE option).
3078#         Moreover, the @c __DIR__ variable is set to the specified directory.
3079#         Otherwise, if this option is omitted, the @c BUILD_INSTALL_SCRIPT variable
3080#         is set to @c FALSE instead and @c __DIR__ is set to the directory of
3081#         the configured @p OUTPUT file. Note that the @c BUILD_INSTALL_SCRIPT and
3082#         @c __DIR__ variables are in particular used by basis_set_script_path()
3083#         to convert the given paths to paths relative to the location of the
3084#         configured/installed script.</td>
3085#   </tr>
3086#   <tr>
3087#     @tp @b CACHE_FILE file1 [file2...] @endtp
3088#     <td>List of CMake files with dump of variables which should be included
3089#         before configuring the script. The cache files can be generated using
3090#         the basis_dump_variables() function.</td>
3091#   </tr>
3092#   <tr>
3093#     @tp @b CONFIG_FILE file1 [file2...] @endtp
3094#     <td>List of script configuration files to include before the configuration
3095#         of the script. See also the documentation of the @p DESTINATION option.</td>
3096#   </tr>
3097#   <tr>
3098#     @tp @b LINK_DEPENDS dep1 [dep2...] @endtp
3099#     <td>List of "link" dependencies, i.e., modules and script/module libraries
3100#         required by this script. For executable scripts, the paths to these
3101#         modules/packages is added to the module search path. If the prefix
3102#         "relative " is given before a file path, it is made relative to the
3103#         output/installation directory of the script file. All given input paths
3104#         must be absolute, however, as the relative location depends on
3105#         whether the script will be installed, i.e., the @c DESTINATION
3106#         is specified, or not.</td>
3107#   </tr>
3108# </table>
3109function (basis_configure_script INPUT OUTPUT)
3110  # rename arguments to avoid conflict with script configuration
3111  set (_INPUT_FILE  "${INPUT}")
3112  set (_OUTPUT_FILE "${OUTPUT}")
3113  # --------------------------------------------------------------------------
3114  # parse arguments
3115  CMAKE_PARSE_ARGUMENTS (
3116    ARGN
3117      "COMPILE;COPYONLY;EXECUTABLE"
3118      "DIRECTORY;DESTINATION;LANGUAGE;CONFIGURATION"
3119      "CACHE_FILE;CONFIG_FILE;LINK_DEPENDS"
3120    ${ARGN}
3121  )
3122  if (ARGN_UNPARSED_ARGUMENTS)
3123    message (FATAL_ERROR "Unrecognized arguments given: ${ARGN_UNPARSED_ARGUMENTS}")
3124  endif ()
3125  if (NOT ARGN_LANGUAGE)
3126    basis_get_source_language (ARGN_LANGUAGE "${_INPUT_FILE}")
3127  endif ()
3128  # --------------------------------------------------------------------------
3129  # include cache files
3130  foreach (_F IN LISTS ARGN_CACHE_FILE)
3131    get_filename_component (_F "${_F}" ABSOLUTE)
3132    if (NOT EXISTS "${_F}")
3133      message (FATAL_ERROR "Cache file ${_F} does not exist!")
3134    endif ()
3135    include ("${_F}")
3136  endforeach ()
3137  # --------------------------------------------------------------------------
3138  # general variables for use in scripts and those intended for use in script
3139  # configurations. The __DIR__ is in particular used by basis_set_script_path()
3140  # to make absolute paths relative to the script file location
3141  if (ARGN_DESTINATION)
3142    if (NOT IS_ABSOLUTE "${ARGN_DESTINATION}")
3143      set (ARGN_DESTINATION "${CMAKE_INSTALL_PREFIX}/${ARGN_DESTINATION}")
3144    endif ()
3145    set (BUILD_INSTALL_SCRIPT TRUE)
3146    set (__DIR__ "${ARGN_DESTINATION}")
3147  else ()
3148    set (BUILD_INSTALL_SCRIPT FALSE)
3149    if (ARGN_DIRECTORY)
3150      if (NOT IS_ABSOLUTE "${ARGN_DIRECTORY}")
3151        message (FATAL_ERROR "Build tree script DIRECTORY must be an absolute path")
3152      endif ()
3153      set (__DIR__ "${ARGN_DIRECTORY}")
3154      string (REPLACE "$<${BASIS_GE_CONFIG}>" "${ARGN_CONFIGURATION}" __DIR__ "${__DIR__}")
3155    else ()
3156      get_filename_component (__DIR__ "${_OUTPUT_FILE}" PATH)
3157    endif ()
3158  endif ()
3159  get_filename_component (__NAME__ "${_OUTPUT_FILE}" NAME)
3160  set (__FILE__   "${__DIR__}/${__NAME__}")
3161  set (__CONFIG__ "${ARGN_CONFIGURATION}")
3162  # --------------------------------------------------------------------------
3163  # replace $<CONFIG> (deprecated $<CONFIGURATION>) in paths of link dependencies
3164  string (REPLACE "$<${BASIS_GE_CONFIG}>" "${ARGN_CONFIGURATION}" ARGN_LINK_DEPENDS "${ARGN_LINK_DEPENDS}")
3165  # --------------------------------------------------------------------------
3166  # include script configuration files
3167  foreach (_F IN LISTS ARGN_CONFIG_FILE)
3168    get_filename_component (_F "${_F}" ABSOLUTE)
3169    if (NOT EXISTS "${_F}")
3170      message (FATAL_ERROR "Script configuration file ${_F} does not exist!")
3171    endif ()
3172    include ("${_F}")
3173  endforeach ()
3174  # --------------------------------------------------------------------------
3175  # configure executable script
3176  if (ARGN_EXECUTABLE)
3177    # Attention: Every line of code added/removed will introduce a mismatch
3178    #            between error messages of the interpreter and the original
3179    #            source file. To not confuse/mislead developers too much,
3180    #            keep number of lines added/removed at a minimum or at least
3181    #            try to balance the number of lines added and removed.
3182    #            Moreover, blank lines can be used to insert code without
3183    #            changing the number of source code lines.
3184    file (READ "${_INPUT_FILE}" SCRIPT)
3185    # (temporarily) remove existing shebang directive
3186    file (STRINGS "${_INPUT_FILE}" FIRST_LINE LIMIT_COUNT 1)
3187    if (FIRST_LINE MATCHES "^#!")
3188      basis_sanitize_for_regex (FIRST_LINE_RE "${FIRST_LINE}")
3189      string (REGEX REPLACE "^${FIRST_LINE_RE}\n?" "" SCRIPT "${SCRIPT}")
3190      set (SHEBANG "${FIRST_LINE}")
3191    endif ()
3192    # replace CMake variables used in script
3193    if (NOT ARGN_COPYONLY)
3194      string (CONFIGURE "${SCRIPT}" SCRIPT @ONLY)
3195    endif ()
3196    # add code to set module search path
3197    if (ARGN_LANGUAGE MATCHES "[JP]YTHON")
3198      if (ARGN_LINK_DEPENDS)
3199        set (PYTHON_CODE "import sys; import os.path; __dir__ = os.path.dirname(os.path.realpath(__file__))")
3200        list (REVERSE ARGN_LINK_DEPENDS)
3201        foreach (DIR ${ARGN_LINK_DEPENDS})
3202          if (DIR MATCHES "^relative +(.*)$")
3203            basis_get_relative_path (DIR "${__DIR__}" "${CMAKE_MATCH_1}")
3204          endif ()
3205          if (DIR MATCHES "\\.(py|class)$")
3206            get_filename_component (DIR "${DIR}" PATH)
3207          endif ()
3208          if (IS_ABSOLUTE "${DIR}")
3209            set (PYTHON_CODE "${PYTHON_CODE}; sys.path.insert(0, os.path.realpath('${DIR}'))")
3210          else ()
3211            set (PYTHON_CODE "${PYTHON_CODE}; sys.path.insert(0, os.path.realpath(os.path.join(__dir__, '${DIR}')))")
3212          endif ()
3213        endforeach ()
3214        # insert extra Python code near top, but after any future statement
3215        # (http://docs.python.org/2/reference/simple_stmts.html#future)
3216        set (FUTURE_STATEMENTS)
3217        if (SCRIPT MATCHES "^(.*from[ \t]+__future__[ \t]+import[ \t]+[a-z_]+[^\n]*\n)(.*)$")
3218          set (FUTURE_STATEMENTS "${CMAKE_MATCH_1}")
3219          set (SCRIPT            "${CMAKE_MATCH_2}")
3220        endif ()
3221        basis_remove_blank_line (SCRIPT) # remove a blank line therefore
3222        set (SCRIPT "${FUTURE_STATEMENTS}${PYTHON_CODE} # <-- added by BASIS\n${SCRIPT}")
3223      endif ()
3224    elseif (ARGN_LANGUAGE MATCHES "PERL")
3225      if (ARGN_LINK_DEPENDS)
3226        set (PERL_CODE "use Cwd qw(realpath); use File::Basename;")
3227        foreach (DIR ${ARGN_LINK_DEPENDS})
3228          if (DIR MATCHES "^relative +(.*)$")
3229            basis_get_relative_path (DIR "${__DIR__}" "${CMAKE_MATCH_1}")
3230          endif ()
3231          if (DIR MATCHES "\\.pm$")
3232            get_filename_component (DIR "${DIR}" PATH)
3233          endif ()
3234          if (IS_ABSOLUTE "${DIR}")
3235            set (PERL_CODE "${PERL_CODE} use lib '${DIR}';")
3236          else ()
3237            set (PERL_CODE "${PERL_CODE} use lib dirname(realpath(__FILE__)) . '/${DIR}';")
3238          endif ()
3239        endforeach ()
3240        basis_remove_blank_line (SCRIPT) # remove a blank line therefore
3241        set (SCRIPT "${PERL_CODE} # <-- added by BASIS\n${SCRIPT}")
3242      endif ()
3243    elseif (ARGN_LANGUAGE MATCHES "BASH")
3244      basis_library_prefix (PREFIX BASH)
3245      # In case of Bash, set BASIS_BASH_UTILITIES which is required to first source the
3246      # BASIS utilities modules (in particular core.sh). This variable should be set to
3247      # the utilities.sh module of BASIS by default as part of the BASIS installation
3248      # (environment variable) and is here set to the project-specific basis.sh module.
3249      #
3250      # Note that folks at SBIA may submit a Bash script directly to a batch queuing
3251      # system such as the Oracle Grid Engine (SGE) instead of writing a separate submit
3252      # script. To avoid not finding the BASIS utilities in this case only because the
3253      # Bash file was copied by SGE to a temporary file, consider the <PROJECT>_DIR
3254      # environment variable as an alternative.
3255      set (BASH_CODE
3256# Note: Code formatted such that it can be on single line. Use no comments within!
3257"__FILE__=\"$(cd -P -- \"$(dirname -- \"$BASH_SOURCE\")\" && pwd -P)/$(basename -- \"$BASH_SOURCE\")\"
3258if [[ -n \"$SGE_ROOT\" ]] && [[ $__FILE__ =~ $SGE_ROOT/.* ]] && [[ -n \"\${${PROJECT_NAME}_DIR}\" ]] && [[ -f \"\${${PROJECT_NAME}_DIR}/bin/${__NAME__}\" ]]
3259then __FILE__=\"\${${PROJECT_NAME}_DIR}/bin/${__NAME__}\"
3260fi
3261i=0
3262lnk=\"$__FILE__\"
3263while [[ -h \"$lnk\" ]] && [[ $i -lt 100 ]]
3264do dir=`dirname -- \"$lnk\"`
3265lnk=`readlink -- \"$lnk\"`
3266lnk=`cd \"$dir\" && cd $(dirname -- \"$lnk\") && pwd`/`basename -- \"$lnk\"`
3267let i++
3268done
3269[[ $i -lt 100 ]] && __FILE__=\"$lnk\"
3270unset -v i dir lnk
3271__DIR__=\"$(dirname -- \"$__FILE__\")\"
3272BASIS_BASH_UTILITIES=\"$__DIR__/${BASH_LIBRARY_DIR}/${PREFIX}basis.sh\""
3273      )
3274      string (REPLACE "\n" "; " BASH_CODE "${BASH_CODE}")
3275      # set BASHPATH which is used by import() function provided by core.sh module of BASIS
3276      set (BASHPATH)
3277      foreach (DIR ${ARGN_LINK_DEPENDS})
3278        if (DIR MATCHES "^relative +(.*)$")
3279          basis_get_relative_path (DIR "${__DIR__}" "${CMAKE_MATCH_1}")
3280        endif ()
3281        if (DIR MATCHES "\\.sh$")
3282          get_filename_component (DIR "${DIR}" PATH)
3283        endif ()
3284        if (IS_ABSOLUTE "${DIR}")
3285          list (APPEND BASHPATH "${DIR}")
3286        else ()
3287          list (APPEND BASHPATH "$__DIR__/${DIR}")
3288        endif ()
3289      endforeach ()
3290      if (BASHPATH)
3291        list (REMOVE_DUPLICATES BASHPATH)
3292        list (APPEND BASHPATH "$BASHPATH")
3293        basis_list_to_delimited_string (BASHPATH ":" NOAUTOQUOTE ${BASHPATH})
3294        set (BASH_CODE "${BASH_CODE}; BASHPATH=\"${BASHPATH}\"")
3295      endif ()
3296      basis_remove_blank_line (SCRIPT) # remove a blank line therefore
3297      set (SCRIPT "${BASH_CODE} # <-- added by BASIS\n${SCRIPT}")
3298    endif ()
3299    # replace shebang directive
3300    if (PYTHON_EXECUTABLE AND ARGN_LANGUAGE MATCHES "PYTHON")
3301      if (WIN32)
3302        set (SHEBANG "@setlocal enableextensions & \"${PYTHON_EXECUTABLE}\" -x \"%~f0\" %* & goto :EOF")
3303      else ()
3304        set (SHEBANG "#! ${PYTHON_EXECUTABLE}")
3305      endif ()
3306    elseif (JYTHON_EXECUTABLE AND ARGN_LANGUAGE MATCHES "JYTHON")
3307      if (WIN32)
3308        set (SHEBANG "@setlocal enableextensions & \"${JYTHON_EXECUTABLE}\" -x \"%~f0\" %* & goto :EOF")
3309      else ()
3310        # Attention: It is IMPORTANT to not use "#! <interpreter>" even if the <interpreter>
3311        #            is given as full path in case of jython. Otherwise, the Jython executable
3312        #            fails to execute from within a Python script using the os.system(),
3313        #            subprocess.popen(), subprocess.call() or similar function!
3314        #            Don't ask me for an explanation, but possibly the used shell otherwise does
3315        #            not recognize the shebang as being valid. Using /usr/bin/env helps out here,
3316        #            -schuha
3317        set (SHEBANG "#! /usr/bin/env ${JYTHON_EXECUTABLE}")
3318      endif ()
3319    elseif (PERL_EXECUTABLE AND ARGN_LANGUAGE MATCHES "PERL")
3320      if (WIN32)
3321        set (SHEBANG "@goto = \"START_OF_BATCH\" ;\n@goto = ();")
3322        set (SCRIPT "${SCRIPT}\n\n__END__\n\n:\"START_OF_BATCH\"\n@\"${PERL_EXECUTABLE}\" -w -S \"%~f0\" %*")
3323      else ()
3324        set (SHEBANG "#! ${PERL_EXECUTABLE} -w")
3325      endif ()
3326    elseif (BASH_EXECUTABLE AND ARGN_LANGUAGE MATCHES "BASH")
3327      set (SHEBANG "#! ${BASH_EXECUTABLE}")
3328    endif ()
3329    # add (modified) shebang directive again
3330    if (SHEBANG)
3331      set (SCRIPT "${SHEBANG}\n${SCRIPT}")
3332    endif ()
3333    # write configured script
3334    file (WRITE "${_OUTPUT_FILE}" "${SCRIPT}")
3335    # make script executable on Unix
3336    if (UNIX AND NOT ARGN_DESTINATION)
3337      execute_process (COMMAND /bin/chmod +x "${_OUTPUT_FILE}")
3338    endif ()
3339  # --------------------------------------------------------------------------
3340  # configure module script
3341  else ()
3342    # configure module - do not use configure_file() as it will not update the
3343    #                    file if nothing has changed. the update of the modification
3344    #                    time is however in particular required for the
3345    #                    configure_script.cmake build command which uses this
3346    #                    function to build script targets. otherwise, the custom
3347    #                    build command is reexecuted only because the output files
3348    #                    never appear to be more recent than the dependencies
3349    file (READ "${_INPUT_FILE}" SCRIPT)
3350    if (NOT ARGN_COPYONLY)
3351      string (CONFIGURE "${SCRIPT}" SCRIPT @ONLY)
3352    endif ()
3353    file (WRITE "${_OUTPUT_FILE}" "${SCRIPT}")
3354    # compile module if requested
3355    if (ARGN_COMPILE)
3356      if (ARGN_LANGUAGE MATCHES "PYTHON" AND PYTHON_EXECUTABLE)
3357        basis_get_compiled_file (CFILE "${_OUTPUT_FILE}" PYTHON)
3358        execute_process (COMMAND "${PYTHON_EXECUTABLE}" -E -c "import py_compile; py_compile.compile('${_OUTPUT_FILE}', '${CFILE}')")
3359        basis_compile_python_modules_for_jython (RV)
3360        if (RV)
3361          basis_get_compiled_jython_file_of_python_module (CFILE "${_OUTPUT_FILE}")
3362          get_filename_component (CDIR "${CFILE}" PATH)
3363          file (MAKE_DIRECTORY "${CDIR}")
3364          execute_process (COMMAND "${JYTHON_EXECUTABLE}" -c "import py_compile; py_compile.compile('${_OUTPUT_FILE}', '${CFILE}')")
3365        endif ()
3366      elseif (ARGN_LANGUAGE MATCHES "JYTHON" AND JYTHON_EXECUTABLE)
3367        basis_get_compiled_file (CFILE "${_OUTPUT_FILE}" JYTHON)
3368        execute_process (COMMAND "${JYTHON_EXECUTABLE}" -c "import py_compile; py_compile.compile('${_OUTPUT_FILE}', '${CFILE}')")
3369      endif ()
3370    endif ()
3371  endif ()
3372endfunction ()
3373
3374# ----------------------------------------------------------------------------
3375## @brief Get type name of target.
3376#
3377# @param [out] TYPE       The target's type name or NOTFOUND.
3378# @param [in]  TARGET_UID The UID of the target.
3379function (_basis_get_target_type TYPE TARGET_UID)
3380  get_target_property (IMPORTED ${TARGET_UID} IMPORTED)
3381  if (IMPORTED)
3382    get_target_property (TYPE_OUT ${TARGET_UID} TYPE)
3383  else ()
3384    get_target_property (TYPE_OUT ${TARGET_UID} BASIS_TYPE)
3385    if (NOT TYPE_OUT)
3386      get_target_property (TYPE_OUT ${TARGET_UID} TYPE)
3387    endif ()
3388  endif ()
3389  set ("${TYPE}" "${TYPE_OUT}" PARENT_SCOPE)
3390endfunction ()
3391
3392# ----------------------------------------------------------------------------
3393## @brief Get type name of target.
3394#
3395# @param [out] TYPE        The target's type name or NOTFOUND.
3396# @param [in]  TARGET_NAME The name of the target.
3397function (basis_get_target_type TYPE TARGET_NAME)
3398  basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
3399  if (TARGET ${TARGET_UID})
3400    _basis_get_target_type(TYPE_OUT ${TARGET_UID})
3401  else ()
3402    set (TYPE_OUT "NOTFOUND")
3403  endif ()
3404  set ("${TYPE}" "${TYPE_OUT}" PARENT_SCOPE)
3405endfunction ()
3406
3407# ----------------------------------------------------------------------------
3408## @brief Get location of build target output file(s).
3409#
3410# This convenience function can be used to get the full path of the output
3411# file(s) generated by a given build target. It is similar to the read-only
3412# @c LOCATION property of CMake targets and should be used instead of
3413# reading this porperty. In case of scripted libraries, this function returns
3414# the path of the root directory of the library that has to be added to the
3415# module search path.
3416#
3417# @note If the target is a binary built from C++ source files and the CMake
3418#       generator is an IDE such as Visual Studio or Xcode, the absolute
3419#       directory of the target location ends with the generator expression
3420#       "/$<${BASIS_GE_CONFIG}>" which is to be substituted by the respective
3421#       build configuration.
3422#
3423# @param [out] VAR         Path of build target output file.
3424# @param [in]  TARGET_NAME Name of build target.
3425# @param [in]  PART        Which file name component of the @c LOCATION
3426#                          property to return. See get_filename_component().
3427#                          If POST_INSTALL_RELATIVE is given as argument,
3428#                          @p VAR is set to the path of the installed file
3429#                          relative to the installation prefix. Similarly,
3430#                          POST_INSTALL sets @p VAR to the absolute path
3431#                          of the installed file post installation.
3432#
3433# @returns Path of output file similar to @c LOCATION property of CMake targets.
3434#
3435# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#prop_tgt:LOCATION
3436function (basis_get_target_location VAR TARGET_NAME PART)
3437  basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
3438  if (TARGET "${TARGET_UID}")
3439    _basis_get_target_name (TARGET_NAME "${TARGET_UID}")
3440    _basis_get_target_type (TYPE        "${TARGET_UID}")
3441    get_target_property (IMPORTED ${TARGET_UID} IMPORTED)
3442    # ------------------------------------------------------------------------
3443    # imported targets
3444    #
3445    # Note: This might not be required though as even custom executable
3446    #       and library targets can be imported using CMake's
3447    #       add_executable(<NAME> IMPORTED) and add_library(<NAME> <TYPE> IMPORTED)
3448    #       commands. Such executable can, for example, also be a BASH
3449    #       script built by basis_add_script().
3450    if (IMPORTED)
3451      # 1. Try IMPORTED_LOCATION_<CMAKE_BUILD_TYPE>
3452      if (CMAKE_BUILD_TYPE)
3453        string (TOUPPER "${CMAKE_BUILD_TYPE}" U)
3454      else ()
3455        set (U "NOCONFIG")
3456      endif ()
3457      get_target_property (LOCATION ${TARGET_UID} IMPORTED_LOCATION_${U})
3458      # 2. Try IMPORTED_LOCATION
3459      if (NOT LOCATION)
3460        get_target_property (LOCATION ${TARGET_UID} IMPORTED_LOCATION)
3461      endif ()
3462      # 3. Prefer Release over all other configurations
3463      if (NOT LOCATION)
3464        get_target_property (LOCATION ${TARGET_UID} IMPORTED_LOCATION_RELEASE)
3465      endif ()
3466      # 4. Just use any of the imported configurations
3467      if (NOT LOCATION)
3468        get_property (CONFIGS TARGET ${TARGET_UID} PROPERTY IMPORTED_CONFIGURATIONS)
3469        foreach (C IN LISTS CONFIGS)
3470          string (TOUPPER "${C}" C)
3471          get_target_property (LOCATION ${TARGET_UID} IMPORTED_LOCATION_${C})
3472          if (LOCATION)
3473            break ()
3474          endif ()
3475        endforeach ()
3476      endif ()
3477      # make path relative to CMAKE_INSTALL_PREFIX if POST_INSTALL_RELATIVE given
3478      if (LOCATION AND ARGV2 MATCHES "POST_INSTALL_RELATIVE")
3479        file (RELATIVE_PATH LOCATION "${CMAKE_INSTALL_PREFIX}" "${LOCATION}")
3480      endif ()
3481    # ------------------------------------------------------------------------
3482    # non-imported targets
3483    else ()
3484      # Attention: The order of the matches/if cases matters here!
3485      # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3486      # scripts
3487      if (TYPE MATCHES "^SCRIPT_(EXECUTABLE|MODULE)$")
3488        if (PART MATCHES "POST_INSTALL")
3489          get_target_property (DIRECTORY ${TARGET_UID} INSTALL_DIRECTORY)
3490        else ()
3491          get_target_property (DIRECTORY ${TARGET_UID} OUTPUT_DIRECTORY)
3492          if (DIRECTORY AND CMAKE_GENERATOR MATCHES "Visual Studio|Xcode")
3493            set (DIRECTORY "${DIRECTORY}/$<${BASIS_GE_CONFIG}>")
3494          endif ()
3495        endif ()
3496        get_target_property (FNAME ${TARGET_UID} OUTPUT_NAME)
3497      elseif (TYPE STREQUAL "^SCRIPT_MODULE$")
3498        if (PART MATCHES "POST_INSTALL")
3499          get_target_property (DIRECTORY ${TARGET_UID} INSTALL_DIRECTORY)
3500        else ()
3501          get_target_property (COMPILE   ${TARGET_UID} COMPILE)
3502          get_target_property (DIRECTORY ${TARGET_UID} OUTPUT_DIRECTORY)
3503          if (DIRECTORY AND (COMPILE OR CMAKE_GENERATOR MATCHES "Visual Studio|Xcode"))
3504            set (DIRECTORY "${DIRECTORY}/$<${BASIS_GE_CONFIG}>")
3505          endif ()
3506        endif ()
3507        get_target_property (FNAME ${TARGET_UID} OUTPUT_NAME)
3508      # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3509      # libraries
3510      elseif (TYPE MATCHES "LIBRARY|MODULE|MEX")
3511        if (TYPE MATCHES "STATIC")
3512          if (PART MATCHES "POST_INSTALL")
3513            get_target_property (DIRECTORY ${TARGET_UID} ARCHIVE_INSTALL_DIRECTORY)
3514          else ()
3515            get_target_property (DIRECTORY ${TARGET_UID} ARCHIVE_OUTPUT_DIRECTORY)
3516          endif ()
3517          get_target_property (FNAME ${TARGET_UID} ARCHIVE_OUTPUT_NAME)
3518        else ()
3519          if (PART MATCHES "POST_INSTALL")
3520            get_target_property (DIRECTORY ${TARGET_UID} LIBRARY_INSTALL_DIRECTORY)
3521          else ()
3522            get_target_property (DIRECTORY ${TARGET_UID} LIBRARY_OUTPUT_DIRECTORY)
3523          endif ()
3524          get_target_property (FNAME ${TARGET_UID} LIBRARY_OUTPUT_NAME)
3525        endif ()
3526      # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3527      # executables
3528      else ()
3529        if (PART MATCHES "POST_INSTALL")
3530          get_target_property (DIRECTORY ${TARGET_UID} RUNTIME_INSTALL_DIRECTORY)
3531        else ()
3532          get_target_property (DIRECTORY ${TARGET_UID} RUNTIME_OUTPUT_DIRECTORY)
3533        endif ()
3534        get_target_property (FNAME ${TARGET_UID} RUNTIME_OUTPUT_NAME)
3535      endif ()
3536      if (DIRECTORY MATCHES "NOTFOUND")
3537        message (FATAL_ERROR "Failed to get directory of ${TYPE} ${TARGET_UID}!"
3538                             " Check implementation of basis_get_target_location()"
3539                             " and make sure that the required *INSTALL_DIRECTORY"
3540                             " property is set on the target!")
3541      endif ()
3542      if (DIRECTORY)
3543        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3544        # get output name of built file (if applicable)
3545        if (NOT FNAME)
3546          get_target_property (FNAME ${TARGET_UID} OUTPUT_NAME)
3547        endif ()
3548        if (NOT "^${TYPE}$" STREQUAL "^SCRIPT_LIBRARY$")
3549          get_target_property (PREFIX ${TARGET_UID} PREFIX)
3550          get_target_property (SUFFIX ${TARGET_UID} SUFFIX)
3551          if (FNAME)
3552            set (TARGET_FILE "${FNAME}")
3553          else ()
3554            set (TARGET_FILE "${TARGET_NAME}")
3555          endif ()
3556          if (PREFIX)
3557            set (TARGET_FILE "${PREFIX}${TARGET_FILE}")
3558          endif ()
3559          if (SUFFIX)
3560            set (TARGET_FILE "${TARGET_FILE}${SUFFIX}")
3561          elseif (WIN32 AND "^${TYPE}$" STREQUAL "^EXECUTABLE$")
3562            set (TARGET_FILE "${TARGET_FILE}.exe")
3563          endif ()
3564        endif ()
3565        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3566        # prepend $<CONFIG> "generator expression" for non-custom binaries
3567        # when built with an IDE such as Visual Studio or Xcode
3568        if ("^${TYPE}$" STREQUAL "^EXECUTABLE$" OR "^${TYPE}$" STREQUAL "^LIBRARY$")
3569          if (NOT PART MATCHES "INSTALL")
3570            if (CMAKE_GENERATOR MATCHES "Visual Studio|Xcode")
3571              set (DIRECTORY "${DIRECTORY}/$<${BASIS_GE_CONFIG}>")
3572            endif ()
3573          endif ()
3574        endif ()
3575        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3576        # assemble final path
3577        if (PART MATCHES "POST_INSTALL_RELATIVE")
3578          if (IS_ABSOLUTE "${DIRECTORY}")
3579            file (RELATIVE_PATH DIRECTORY "${CMAKE_INSTALL_PREFIX}" "${DIRECTORY}")
3580            if (NOT DIRECTORY)
3581              set (DIRECTORY ".")
3582            endif ()
3583          endif ()
3584        elseif (PART MATCHES "POST_INSTALL")
3585          if (NOT IS_ABSOLUTE "${DIRECTORY}")
3586            set (DIRECTORY "${CMAKE_INSTALL_PREFIX}/${DIRECTORY}")
3587          endif ()
3588        endif ()
3589        if (TARGET_FILE)
3590          set (LOCATION "${DIRECTORY}/${TARGET_FILE}")
3591        else ()
3592          set (LOCATION "${DIRECTORY}")
3593        endif ()
3594      else ()
3595        set (LOCATION "${DIRECTORY}")
3596      endif ()
3597    endif ()
3598    # get filename component
3599    if (LOCATION AND PART MATCHES "(^|_)(PATH|NAME|NAME_WE)$")
3600      get_filename_component (LOCATION "${LOCATION}" "${CMAKE_MATCH_2}")
3601    endif ()
3602  else ()
3603    message (FATAL_ERROR "basis_get_target_location(): Unknown target ${TARGET_UID}")
3604  endif ()
3605  # return
3606  set ("${VAR}" "${LOCATION}" PARENT_SCOPE)
3607endfunction ()
3608
3609# ----------------------------------------------------------------------------
3610## @brief Get link libraries/dependencies of (imported) target.
3611#
3612# This function recursively adds the dependencies of the dependencies as well
3613# and returns them together with the list of the direct link dependencies.
3614# Moreover, for script targets, if any of the dependencies uses the BASIS
3615# utilities for the given language (@c BASIS_UTILITIES property), the
3616# corresponding utilities library is added to the list of dependencies.
3617# Note that therefore the BASIS utilities targets have to be added already,
3618# which is only the case during the finalization of script targets.
3619#
3620# @param [out] LINK_DEPENDS List of all link dependencies. In case of scripts,
3621#                           the dependencies are the required modules or
3622#                           paths to required packages, respectively.
3623# @param [in]  TARGET_NAME  Name of the target.
3624function (basis_get_target_link_libraries LINK_DEPENDS TARGET_NAME)
3625  basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
3626  if (NOT TARGET "${TARGET_UID}")
3627    message (FATAL_ERROR "basis_get_target_link_libraries(): Unknown target: ${TARGET_UID}")
3628  endif ()
3629  if (BASIS_DEBUG AND BASIS_VERBOSE)
3630    message ("** basis_get_target_link_libraries():")
3631    message ("**   TARGET_NAME:     ${TARGET_NAME}")
3632    message ("**   CURRENT_DEPENDS: ${ARGN}")
3633  endif ()
3634  # get type of target
3635  get_target_property (BASIS_TYPE ${TARGET_UID} BASIS_TYPE)
3636  # get direct link dependencies of target
3637  get_target_property (IMPORTED ${TARGET_UID} IMPORTED)
3638  if (IMPORTED)
3639    # 1. Try IMPORTED_LINK_INTERFACE_LIBRARIES_<CMAKE_BUILD_TYPE>
3640    if (CMAKE_BUILD_TYPE)
3641      string (TOUPPER "${CMAKE_BUILD_TYPE}" U)
3642    else ()
3643      set (U "NOCONFIG")
3644    endif ()
3645    get_target_property (DEPENDS ${TARGET_UID} "IMPORTED_LINK_INTERFACE_LIBRARIES_${U}")
3646    # 2. Try IMPORTED_LINK_INTERFACE_LIBRARIES
3647    if (NOT DEPENDS)
3648      get_target_property (DEPENDS ${TARGET_UID} "IMPORTED_LINK_INTERFACE_LIBRARIES")
3649    endif ()
3650    # 3. Prefer Release over all other configurations
3651    if (NOT DEPENDS)
3652      get_target_property (DEPENDS ${TARGET_UID} "IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE")
3653    endif ()
3654    # 4. Just use any of the imported configurations
3655    if (NOT DEPENDS)
3656      get_property (CONFIGS TARGET "${TARGET_UID}" PROPERTY IMPORTED_CONFIGURATIONS)
3657      foreach (C IN LISTS CONFIGS)
3658        get_target_property (DEPENDS ${TARGET_UID} "IMPORTED_LINK_INTERFACE_LIBRARIES_${C}")
3659        if (DEPENDS)
3660          break ()
3661        endif ()
3662      endforeach ()
3663    endif ()
3664  # otherwise, get LINK_DEPENDS property value
3665  elseif (BASIS_TYPE MATCHES "^EXECUTABLE$|^(SHARED|STATIC|MODULE)_LIBRARY$")
3666    get_target_property (DEPENDS ${TARGET_UID} BASIS_LINK_DEPENDS)
3667  else ()
3668    get_target_property (DEPENDS ${TARGET_UID} LINK_DEPENDS)
3669  endif ()
3670  if (NOT DEPENDS)
3671    set (DEPENDS)
3672  endif ()
3673  # prepend BASIS utilities if used (and added)
3674  if (BASIS_TYPE MATCHES "SCRIPT")
3675    set (BASIS_UTILITIES_TARGETS)
3676    foreach (UID IN ITEMS ${TARGET_UID} ${DEPENDS})
3677      if (TARGET "${UID}")
3678        get_target_property (BASIS_UTILITIES ${UID} BASIS_UTILITIES)
3679        get_target_property (LANGUAGE        ${UID} LANGUAGE)
3680        if (BASIS_UTILITIES)
3681          set (BASIS_UTILITIES_TARGET)
3682          if (LANGUAGE MATCHES "[JP]YTHON")
3683            basis_get_source_target_name (BASIS_UTILITIES_TARGET "basis.py" NAME)
3684          elseif (LANGUAGE MATCHES "PERL")
3685            basis_get_source_target_name (BASIS_UTILITIES_TARGET "Basis.pm" NAME)
3686          elseif (LANGUAGE MATCHES "BASH")
3687            basis_get_source_target_name (BASIS_UTILITIES_TARGET "basis.sh" NAME)
3688          endif ()
3689          if (BASIS_UTILITIES_TARGET)
3690            basis_get_target_uid (BASIS_UTILITIES_TARGET ${BASIS_UTILITIES_TARGET})
3691          endif ()
3692          if (TARGET ${BASIS_UTILITIES_TARGET})
3693            list (APPEND BASIS_UTILITIES_TARGETS ${BASIS_UTILITIES_TARGET})
3694          endif ()
3695        endif ()
3696      endif ()
3697    endforeach ()
3698    if (BASIS_UTILITIES_TARGETS)
3699      list (INSERT DEPENDS 0 ${BASIS_UTILITIES_TARGETS})
3700    endif ()
3701  endif ()
3702  # convert target names to UIDs
3703  set (_DEPENDS)
3704  foreach (LIB IN LISTS DEPENDS)
3705    basis_get_target_uid (UID "${LIB}")
3706    if (TARGET ${UID})
3707      list (APPEND _DEPENDS "${UID}")
3708    else ()
3709      list (APPEND _DEPENDS "${LIB}")
3710    endif ()
3711  endforeach ()
3712  set (DEPENDS "${_DEPENDS}")
3713  unset (_DEPENDS)
3714  # recursively add link dependencies of dependencies
3715  # TODO implement it non-recursively for better performance
3716  foreach (LIB IN LISTS DEPENDS)
3717    if (TARGET ${LIB})
3718      list (FIND ARGN "${LIB}" IDX) # avoid recursive loop
3719      if (IDX EQUAL -1)
3720        basis_get_target_link_libraries (LIB_DEPENDS ${LIB} ${ARGN} ${DEPENDS})
3721        list (APPEND DEPENDS ${LIB_DEPENDS})
3722      endif ()
3723    endif ()
3724  endforeach ()
3725  # remove duplicate entries
3726  if (DEPENDS)
3727    list (REMOVE_DUPLICATES DEPENDS)
3728  endif ()
3729  # return
3730  set (${LINK_DEPENDS} "${DEPENDS}" PARENT_SCOPE)
3731endfunction ()
3732
3733# ============================================================================
3734# generator expressions
3735# ============================================================================
3736
3737# ----------------------------------------------------------------------------
3738## @brief Process generator expressions in arguments.
3739#
3740# This command evaluates the $&lt;TARGET_FILE:tgt&gt; and related generator
3741# expressions also for custom targets such as scripts and MATLAB Compiler
3742# targets. For other generator expressions whose argument is a target name,
3743# this function replaces the target name by the target UID, i.e., the actual
3744# CMake target name such that the expression can be evaluated by CMake.
3745# The following generator expressions are directly evaluated by this function:
3746# <table border=0>
3747#   <tr>
3748#     @tp <b><tt>$&lt;TARGET_FILE:tgt&gt;</tt></b> @endtp
3749#     <td>Absolute file path of built target.</td>
3750#   </tr>
3751#   <tr>
3752#     @tp <b><tt>$&lt;TARGET_FILE_POST_INSTALL:tgt&gt;</tt></b> @endtp
3753#     <td>Absolute path of target file after installation using the
3754#         current @c CMAKE_INSTALL_PREFIX.</td>
3755#   </tr>
3756#   <tr>
3757#     @tp <b><tt>$&lt;TARGET_FILE_POST_INSTALL_RELATIVE:tgt&gt;</tt></b> @endtp
3758#     <td>Path of target file after installation relative to @c CMAKE_INSTALL_PREFIX.</td>
3759#   </tr>
3760# </table>
3761# Additionally, the suffix <tt>_NAME</tt> or <tt>_DIR</tt> can be appended
3762# to the name of each of these generator expressions to get only the basename
3763# of the target file including the extension or the corresponding directory
3764# path, respectively.
3765#
3766# Generator expressions are in particular supported by basis_add_test().
3767#
3768# @param [out] ARGS Name of output list variable.
3769# @param [in]  ARGN List of arguments to process.
3770#
3771# @sa basis_add_test()
3772# @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:add_test
3773function (basis_process_generator_expressions ARGS)
3774  set (ARGS_OUT)
3775  foreach (ARG IN LISTS ARGN)
3776    string (REGEX MATCHALL "\\$<.*TARGET.*:.*>" EXPRS "${ARG}")
3777    foreach (EXPR IN LISTS EXPRS)
3778      if (EXPR MATCHES "\\$<(.*):(.*)>")
3779        set (EXPR_NAME   "${CMAKE_MATCH_1}")
3780        set (TARGET_NAME "${CMAKE_MATCH_2}")
3781        # TARGET_FILE* expression, including custom targets
3782        if (EXPR_NAME MATCHES "^TARGET_FILE(.*)")
3783          if (NOT CMAKE_MATCH_1)
3784            set (CMAKE_MATCH_1 "ABSOLUTE")
3785          endif ()
3786          string (REGEX REPLACE "^_" "" PART "${CMAKE_MATCH_1}")
3787          basis_get_target_location (ARG "${TARGET_NAME}" ${PART})
3788        # other generator expression supported by CMake
3789        # only replace target name, but do not evaluate expression
3790        else ()
3791          basis_get_target_uid (TARGET_UID "${CMAKE_MATCH_2}")
3792          string (REPLACE "${EXPR}" "$<${CMAKE_MATCH_1}:${TARGET_UID}>" ARG "${ARG}")
3793        endif ()
3794        if (BASIS_DEBUG AND BASIS_VERBOSE)
3795          message ("** basis_process_generator_expressions():")
3796          message ("**   Expression:  ${EXPR}")
3797          message ("**   Keyword:     ${EXPR_NAME}")
3798          message ("**   Argument:    ${TARGET_NAME}")
3799          message ("**   Replaced by: ${ARG}")
3800        endif ()
3801      endif ()
3802    endforeach ()
3803    list (APPEND ARGS_OUT "${ARG}")
3804  endforeach ()
3805  set (${ARGS} "${ARGS_OUT}" PARENT_SCOPE)
3806endfunction ()
3807
3808
3809##
3810#  @brief basis_append_to_each takes an input list and appends a single element to each item in that list and appends it to the output list.
3811#                For example, this is useful for adding relative paths to the end of a list of paths.
3812#
3813#  @param OUTPUT_LIST Name of list that will be filled with appended names.
3814#  @param INPUT_LIST  Name of list that contains items to have text appended.
3815#  @param ITEM_TO_APPEND text to append to each item in the input list.
3816#
3817function(basis_append_to_each OUTPUT_LIST INPUT_LIST ITEM_TO_APPEND)
3818  foreach(PATH IN LISTS ${INPUT_LIST})
3819    list(APPEND ${OUTPUT_LIST} ${PATH}${ITEM_TO_APPEND} )
3820  endforeach()
3821
3822  if(${OUTPUT_LIST})
3823    set(${OUTPUT_LIST} ${${OUTPUT_LIST}} PARENT_SCOPE)
3824  endif()
3825endfunction()
3826
3827
3828## @}
3829# end of Doxygen group
3830