BASIS  r3148
CommonTools.cmake
Go to the documentation of this file.
00001 ##############################################################################
00002 # @file  CommonTools.cmake
00003 # @brief Definition of common CMake functions.
00004 #
00005 # Copyright (c) 2011, 2012, 2013 University of Pennsylvania. All rights reserved.<br />
00006 # See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.
00007 #
00008 # Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00009 #
00010 # @ingroup CMakeTools
00011 ##############################################################################
00012 
00013 if (__BASIS_COMMONTOOLS_INCLUDED)
00014   return ()
00015 else ()
00016   set (__BASIS_COMMONTOOLS_INCLUDED TRUE)
00017 endif ()
00018 
00019 
00020 include (CMakeParseArguments)
00021 
00022 
00023 ## @addtogroup CMakeUtilities
00024 #  @{
00025 
00026 
00027 # ============================================================================
00028 # find other packages
00029 # ============================================================================
00030 
00031 # ----------------------------------------------------------------------------
00032 ## @brief Overloaded find_package() command.
00033 #
00034 # This macro calls CMake's
00035 # <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package">
00036 # find_package()</a> command and converts obsolete all uppercase "<PKG>_<VAR>"
00037 # variables to case-sensitive "<Pkg>_<VAR>" variables.
00038 # It further ensures that the global variables CMAKE_FIND_LIBRARY_SUFFIXES
00039 # and CMAKE_FIND_EXECUTABLE_SUFFIX are reset to the values they had before
00040 # the call to find_package(). This is required if the "Find<Pkg>.cmake" module
00041 # has modified these variables, but not restored their initial value.
00042 macro (find_package)
00043   if (BASIS_DEBUG)
00044     message ("find_package(${ARGV})")
00045   endif ()
00046   # attention: find_package() can be recursive. Hence, use "stack" to keep
00047   #            track of library suffixes. Further note that we need to
00048   #            maintain a list of lists, which is not supported by CMake.
00049   list (APPEND _BASIS_FIND_LIBRARY_SUFFIXES "{${CMAKE_FIND_LIBRARY_SUFFIXES}}")
00050   list (APPEND _BASIS_FIND_EXECUTABLE_SUFFIX "${CMAKE_FIND_EXECUTABLE_SUFFIX}")
00051   _find_package(${ARGV})
00052   # map obsolete <PKG>_* variables to case-sensitive <Pkg>_*
00053   string (TOUPPER "${ARGV0}" _FP_ARGV0_U)
00054   foreach (_FP_VAR IN ITEMS FOUND DIR USE_FILE
00055                             VERSION VERSION_STRING VERSION_MAJOR VERSION_MINOR VERSION_PATCH
00056                             INCLUDE_DIR INCLUDE_DIRS INCLUDE_PATH
00057                             LIBRARY_DIR LIBRARY_DIRS LIBRARY_PATH)
00058     if (NOT DEFINED ${ARGV0}_${_FP_VAR} AND DEFINED ${_FP_ARGV0_U}_${_FP_VAR})
00059       set (${ARGV0}_${_FP_VAR} "${${_FP_ARGV0_U}_${_FP_VAR}}")
00060     endif ()
00061   endforeach ()
00062   unset (_FP_VAR)
00063   unset (_FP_ARGV0_U)
00064   # restore CMAKE_FIND_LIBRARY_SUFFIXES
00065   string (REGEX REPLACE ";?{([^}]*)}$" "" _BASIS_FIND_LIBRARY_SUFFIXES "${_BASIS_FIND_LIBRARY_SUFFIXES}")
00066   set (CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_MATCH_1}")
00067   # restore CMAKE_FIND_EXECUTABLE_SUFFIX
00068   list (LENGTH _BASIS_FIND_EXECUTABLE_SUFFIX _FP_LAST)
00069   if (_FP_LAST GREATER 0)
00070     math (EXPR _FP_LAST "${_FP_LAST} - 1")
00071     list (REMOVE_AT _BASIS_FIND_EXECUTABLE_SUFFIX ${_FP_LAST})
00072   endif ()
00073   unset (_FP_LAST)
00074 endmacro ()
00075 
00076 # ----------------------------------------------------------------------------
00077 ## @brief Tokenize dependency specification.
00078 #
00079 # This function parses a dependency specification such as
00080 # "ITK-4.1{TestKernel,IO}" into the package name, i.e., ITK, the requested
00081 # (minimum) package version, i.e., 4.1, and a list of package components, i.e.,
00082 # TestKernel and IO. A valid dependency specification must specify the package
00083 # name of the dependency (case-sensitive). The version and components
00084 # specification are optional. Note that the components specification may
00085 # be separated by an arbitrary number of whitespace characters including
00086 # newlines. The same applies to the specification of the components themselves.
00087 # This allows one to format the dependency specification as follows, for example:
00088 # @code
00089 # ITK {
00090 #   TestKernel,
00091 #   IO
00092 # }
00093 # @endcode
00094 #
00095 # @param [in]  DEP Dependency specification, i.e., "<Pkg>[-<version>][{<Component1>[,...]}]".
00096 # @param [out] PKG Package name.
00097 # @param [out] VER Package version.
00098 # @param [out] CMP List of package components.
00099 function (basis_tokenize_dependency DEP PKG VER CMP)
00100   set (CMPS)
00101   if (DEP MATCHES "^([^ ]+)[ \\n\\t]*{([^}]*)}$")
00102     set (DEP "${CMAKE_MATCH_1}")
00103     string (REPLACE "," ";" COMPONENTS "${CMAKE_MATCH_2}")
00104     foreach (C IN LISTS COMPONENTS)
00105       string (STRIP "${C}" C)
00106       list (APPEND CMPS ${C})
00107     endforeach ()
00108   endif ()
00109   if (DEP MATCHES "^(.*)-([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(\\.[0-9]+)?$")
00110     set (${PKG} "${CMAKE_MATCH_1}" PARENT_SCOPE)
00111     set (${VER} "${CMAKE_MATCH_2}${CMAKE_MATCH_3}${CMAKE_MATCH_4}${CMAKE_MATCH_5}" PARENT_SCOPE)
00112   else ()
00113     set (${PKG} "${DEP}" PARENT_SCOPE)
00114     set (${VER} ""       PARENT_SCOPE)
00115   endif ()
00116   set (${CMP} "${CMPS}" PARENT_SCOPE)
00117 endfunction ()
00118 
00119 # ----------------------------------------------------------------------------
00120 ## @brief Find external software package or other project module.
00121 #
00122 # This function replaces CMake's
00123 # <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package">
00124 # find_package()</a> command and extends its functionality.
00125 # In particular, if the given package name is the name of another module
00126 # of this project (the top-level project), it ensures that this module is
00127 # found instead of an external package.
00128 #
00129 # If the package is found, but only optionally used, i.e., the @c REQUIRED
00130 # argument was not given to this macro, a <tt>USE_&lt;Pkg&gt;</tt> option is
00131 # added by this macro which is by default @c ON. This option can be set to
00132 # @c OFF by the user in order to force the <tt>&lt;Pkg&gt;_FOUND</tt> variable
00133 # to be set to @c FALSE again even if the package was found. This allows the
00134 # user to specify which of the optional dependencies should actually not be
00135 # used for the build of the software even though these packages are installed
00136 # on their system.
00137 #
00138 # @param [in] PACKAGE Name of other package. Optionally, the package name
00139 #                     can include a version specification as suffix which
00140 #                     is separated by the package name using a dash (-), i.e.,
00141 #                     &lt;Package&gt;[-major[.minor[.patch[.tweak]]]].
00142 #                     If a version specification is given, it is passed on as
00143 #                     @c version argument to CMake's
00144 #                     <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package">
00145 #                     find_package()</a> command.
00146 # @param [in] ARGN    Advanced arguments for
00147 #                     <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package">
00148 #                     find_package()</a>.
00149 #
00150 # @retval <PACKAGE>_FOUND Whether the given package was found.
00151 #
00152 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package
00153 #
00154 # @ingroup CMakeAPI
00155 macro (basis_find_package PACKAGE)
00156   # parse arguments
00157   CMAKE_PARSE_ARGUMENTS (
00158     ARGN
00159     "EXACT;QUIET;REQUIRED"
00160     ""
00161     "COMPONENTS"
00162     ${ARGN}
00163   )
00164   # --------------------------------------------------------------------------
00165   # tokenize dependency specification
00166   basis_tokenize_dependency ("${PACKAGE}" PKG VER CMPS)
00167   list (APPEND ARGN_COMPONENTS ${CMPS})
00168   unset (CMPS)
00169   if (ARGN_UNPARSED_ARGUMENTS MATCHES "^[0-9]+(\\.[0-9]+)*$" AND VER)
00170     message (FATAL_ERROR "Cannot use both version specification as part of "
00171                          "package name and explicit version argument.")
00172   else ()
00173     set (VER "${CMAKE_MATCH_0}")
00174   endif ()
00175   # --------------------------------------------------------------------------
00176   # preserve <PKG>_DIR variable which might get reset if different versions
00177   # of the package are searched or if package is optional and deselected
00178   set (PKG_DIR "${${PKG}_DIR}")
00179   # --------------------------------------------------------------------------
00180   # some debugging output
00181   if (BASIS_DEBUG)
00182     message ("** basis_find_package()")
00183     message ("**     Package:    ${PKG}")
00184     if (VER)
00185     message ("**     Version:    ${VER}")
00186     endif ()
00187     if (ARGN_COMPONENTS)
00188     message ("**     Components: [${ARGN_COMPONENTS}]")
00189     endif ()
00190   endif ()
00191   # --------------------------------------------------------------------------
00192   # find other modules of same project
00193   set (PKG_IS_MODULE FALSE)
00194   if (PROJECT_IS_MODULE)
00195     # allow modules to specify top-level project as dependency
00196     if (PKG MATCHES "^${BASIS_PROJECT_NAME}$")
00197       if (BASIS_DEBUG)
00198         message ("**     This is the top-level project.")
00199       endif ()
00200       set (${PKG}_FOUND TRUE)
00201     # look for other module of top-level project
00202     elseif (PROJECT_MODULES MATCHES "(^|;)${PKG}(;|$)")
00203       set (PKG_IS_MODULE TRUE)
00204       if (PROJECT_MODULES_ENABLED MATCHES "(^|;)${PKG}(;|$)")
00205         if (BASIS_DEBUG)
00206           message ("**     Identified it as other module of this project.")
00207         endif ()
00208         include ("${${PKG}_DIR}/${BASIS_PROJECT_PACKAGE_CONFIG_PREFIX}${PKG}Config.cmake")
00209         set (${PKG}_FOUND TRUE)
00210       else ()
00211         set (${PKG}_FOUND FALSE)
00212       endif ()
00213     endif ()
00214   # --------------------------------------------------------------------------
00215   # find bundled packages
00216   elseif (BUNDLE_PROJECTS MATCHES "(^|;)${PKG}(;|$)")
00217     if  (EXISTS "${CMAKE_INSTALL_PREFIX}/${INSTALL_CONFIG_DIR}/${PKG}Config.cmake")
00218       set (PKG_CONFIG_FILE "${CMAKE_INSTALL_PREFIX}/${INSTALL_CONFIG_DIR}/${PKG}Config.cmake")
00219     else ()
00220       string (TOLOWER "${PKG}" PKG_L)
00221       if (EXISTS "${CMAKE_INSTALL_PREFIX}/${INSTALL_CONFIG_DIR}/${PKG_L}-config.cmake")
00222         set (PKG_CONFIG_FILE "${CMAKE_INSTALL_PREFIX}/${INSTALL_CONFIG_DIR}/${PKG_L}-config.cmake")
00223       else ()
00224         set (PKG_CONFIG_FILE)
00225       endif ()
00226       unset (PKG_L)
00227     endif ()
00228     if (PKG_CONFIG_FILE)
00229       if (BASIS_DEBUG)
00230         message ("**     Identified it as other package of this bundle.")
00231       endif ()
00232       get_filename_component (PKG_CONFIG_DIR "${PKG_CONFIG_FILE}" PATH)
00233       basis_set_or_update_value (${PKG}_DIR "${PKG_CONFIG_DIR}")
00234       include ("${PKG_CONFIG_FILE}")
00235       set (${PKG}_FOUND TRUE)
00236       unset (PKG_CONFIG_DIR)
00237     endif ()
00238     unset (PKG_CONFIG_FILE)
00239   endif ()
00240   # --------------------------------------------------------------------------
00241   # otherwise, look for external package
00242   if (NOT PKG_IS_MODULE)
00243     # ------------------------------------------------------------------------
00244     # make <PKG>_DIR variable visible in GUI by caching it if not done yet
00245     basis_is_cached (_BFP_CACHED ${PKG}_DIR)
00246     if (DEFINED ${PKG}_DIR AND NOT _BFP_CACHED)
00247       set (${PKG}_DIR "${${PKG}_DIR}" CACHE PATH "Installation directory of ${PKG}.")
00248     endif ()
00249     unset (_BFP_CACHED)
00250     # ------------------------------------------------------------------------
00251     # determine if additional components of found package should be discovered
00252     set (FIND_ADDITIONAL_COMPONENTS FALSE)
00253     if (${PKG}_FOUND)
00254       if (${PKG}_FOUND_COMPONENTS AND ARGN_COMPONENTS)
00255         foreach (_C ${ARGN_COMPONENTS})
00256           list (FIND ${PKG}_FOUND_COMPONENTS "${_C}" _IDX)
00257           if (_IDX EQUAL -1)
00258             set (FIND_ADDITIONAL_COMPONENTS TRUE)
00259             break ()
00260           endif ()
00261         endforeach ()
00262       elseif (${PKG}_FOUND_COMPONENTS OR ARGN_COMPONENTS)
00263         set (FIND_ADDITIONAL_COMPONENTS TRUE)
00264       endif ()
00265     endif ()
00266     # ------------------------------------------------------------------------
00267     # look for external package if not found or additional components needed
00268     if (NOT ${PKG}_FOUND OR FIND_ADDITIONAL_COMPONENTS)
00269       set (_${PKG}_FOUND "${${PKG}_FOUND}") # used to decide what the intersection of
00270                                             # of multiple find invocations for the same
00271                                             # package with different components will be
00272       # ----------------------------------------------------------------------
00273       # reset other <PKG>_* variables if <PKG>_DIR changed
00274       if (_${PKG}_DIR AND ${PKG}_DIR) # internal _<PKG>_DIR cache entry set below
00275         basis_sanitize_for_regex (_BFP_RE "${${PKG}_DIR}")
00276         if (NOT _${PKG}_DIR MATCHES "^${_BFP_RE}$")
00277           get_cmake_property (_BFP_VARS VARIABLES)
00278           foreach (_BFP_VAR IN LISTS _BFP_VARS)
00279             if (_BFP_VAR MATCHES "^${PKG}_" AND NOT _BFP_VAR MATCHES "^${PKG}_DIR$")
00280               basis_is_cached (_BFP_CACHED ${_BFP_VAR})
00281               if (_BFP_CACHED)
00282                 get_property (_BFP_TYPE CACHE ${_BFP_VAR} PROPERTY TYPE)
00283                 if (NOT _BFP_TYPE MATCHES INTERNAL)
00284                   set_property (CACHE ${_BFP_VAR} PROPERTY VALUE "${_BFP_VAR}-NOTFOUND")
00285                   set_property (CACHE ${_BFP_VAR} PROPERTY TYPE  INTERNAL)
00286                 endif ()
00287               endif ()
00288             endif ()
00289           endforeach ()
00290           unset (_BFP_VAR)
00291           unset (_BFP_VARS)
00292           unset (_BFP_CACHED)
00293           unset (_BFP_TYPE)
00294         endif ()
00295         unset (_BFP_RE)
00296       endif ()
00297       # ----------------------------------------------------------------------
00298       # hide or show already defined <PKG>_DIR cache entry
00299       if (DEFINED ${PKG}_DIR AND DEFINED USE_${PKG})
00300         if (USE_${PKG})
00301           mark_as_advanced (CLEAR ${PKG}_DIR)
00302         else ()
00303           mark_as_advanced (FORCE ${PKG}_DIR)
00304         endif ()
00305       endif ()
00306       # ----------------------------------------------------------------------
00307       # find external packages
00308       if (DEFINED USE_${PKG} AND NOT USE_${PKG})
00309         set (${PKG}_FOUND FALSE)
00310       else ()
00311         # circumvent issue with CMake's find_package() interpreting these variables
00312         # relative to the current binary directory instead of the top-level directory
00313         if (${PKG}_DIR AND NOT IS_ABSOLUTE "${${PKG}_DIR}")
00314           set (${PKG}_DIR "${CMAKE_BINARY_DIR}/${${PKG}_DIR}")
00315           get_filename_component (${PKG}_DIR "${${PKG}_DIR}" ABSOLUTE)
00316         endif ()
00317         # moreover, users tend to specify the installation prefix instead of the
00318         # actual directory containing the package configuration file
00319         if (IS_DIRECTORY "${${PKG}_DIR}")
00320           list (INSERT CMAKE_PREFIX_PATH 0 "${${PKG}_DIR}")
00321         endif ()
00322         # now look for the package
00323         set (FIND_ARGN)
00324         if (ARGN_EXACT)
00325           list (APPEND FIND_ARGN "EXACT")
00326         endif ()
00327         if (ARGN_QUIET)
00328           list (APPEND FIND_ARGN "QUIET")
00329         endif ()
00330         if (ARGN_COMPONENTS)
00331           list (APPEND FIND_ARGN "COMPONENTS" ${ARGN_COMPONENTS})
00332         elseif (ARGN_REQUIRED)
00333           list (APPEND FIND_ARGN "REQUIRED")
00334         endif ()
00335         if ("${PKG}" MATCHES "^(MFC|wxWidgets)$")
00336           # if Find<Pkg>.cmake prints status message, don't do it here
00337           find_package (${PKG} ${VER} ${FIND_ARGN})
00338         else ()
00339           set (_STATUS "Looking for ${PKG}")
00340           if (VER)
00341             set (_STATUS "${_STATUS} ${VER}")
00342           endif ()
00343           if (ARGN_COMPONENTS)
00344             set (_STATUS "${_STATUS} [${ARGN_COMPONENTS}]")
00345           endif ()
00346           if (NOT ARGN_REQUIRED)
00347             set (_STATUS "${_STATUS} (optional)")
00348           endif ()
00349           set (_STATUS "${_STATUS}...")
00350           message (STATUS "${_STATUS}")
00351           find_package (${PKG} ${VER} ${FIND_ARGN})
00352           # set common <Pkg>_VERSION_STRING variable if possible and not set
00353           if (NOT DEFINED ${PKG}_VERSION_STRING)
00354             if (PKG MATCHES "^PythonInterp$")
00355               set (${PKG}_VERSION_STRING ${PYTHON_VERSION_STRING})
00356             elseif (PKG MATCHES "^JythonInterp$")
00357               set (${PKG}_VERSION_STRING ${JYTHON_VERSION_STRING})
00358             elseif (DEFINED ${PKG}_VERSION_MAJOR)
00359               set (${PKG}_VERSION_STRING ${${PKG}_VERSION_MAJOR})
00360               if (DEFINED ${PKG}_VERSION_MINOR)
00361                 set (${PKG}_VERSION_STRING ${${PKG}_VERSION_STRING}.${${PKG}_VERSION_MINOR})
00362                 if (DEFINED ${PKG}_VERSION_PATCH)
00363                   set (${PKG}_VERSION_STRING ${${PKG}_VERSION_STRING}.${${PKG}_VERSION_PATCH})
00364                 endif ()
00365               endif ()
00366             elseif (DEFINED ${PKG}_VERSION)
00367               set (${PKG}_VERSION_STRING ${${PKG}_VERSION})
00368             endif ()
00369           endif ()
00370           # verbose output of information about found package
00371           if (${PKG}_FOUND)
00372             set (_STATUS "${_STATUS} - found")
00373             if (BASIS_VERBOSE)
00374               if (DEFINED ${PKG}_VERSION_STRING AND NOT ${PKG}_VERSION_STRING MATCHES "^0.0.0$")
00375                 set (_STATUS "${_STATUS} v${${PKG}_VERSION_STRING}")
00376               endif ()
00377               if (${PKG}_DIR)
00378                 set (_STATUS "${_STATUS} at ${${PKG}_DIR}")
00379               endif ()
00380             endif ()
00381           else ()
00382             set (_STATUS "${_STATUS} - not found")
00383           endif ()
00384           message (STATUS "${_STATUS}")
00385         endif ()
00386         # remember which components where found already
00387         if (${PKG}_FOUND AND ARGN_COMPONENTS)
00388           if (${PKG}_FOUND_COMPONENTS)
00389           list (APPEND ARGN_COMPONENTS ${${PKG}_FOUND_COMPONENTS})
00390           list (REMOVE_DUPLICATES ARGN_COMPONENTS)
00391         endif ()
00392           set (${PKG}_FOUND_COMPONENTS "${ARGN_COMPONENTS}")
00393         endif ()
00394         # if previously components of this package where found and the additional
00395         # components are only optional, set <PKG>_FOUND to TRUE again
00396         if (_${PKG}_FOUND AND NOT ARGN_REQUIRED)
00397           set (${PKG}_FOUND TRUE)
00398         endif ()
00399         # provide option which allows users to disable use of not required packages
00400         if (${PKG}_FOUND AND NOT ARGN_REQUIRED)
00401           option (USE_${PKG} "Enable/disable use of package ${PKG}." ON)
00402           mark_as_advanced (USE_${PKG})
00403           if (NOT USE_${PKG})
00404             set (${PKG}_FOUND FALSE)
00405           endif ()
00406         endif ()
00407       endif ()
00408       # ----------------------------------------------------------------------
00409       # reset <PKG>_DIR variable for possible search of different package version
00410       if (PKG_DIR AND NOT ${PKG}_DIR)
00411         basis_set_or_update_value (${PKG}_DIR "${PKG_DIR}")
00412       endif ()
00413       # ----------------------------------------------------------------------
00414       # remember current/previous <PKG>_DIR
00415       # (used above to reset other <PKG>_* variables whenever <PKG>_DIR changed)
00416       if (DEFINED ${PKG}_DIR)
00417         set (_${PKG}_DIR "${${PKG}_DIR}" CACHE INTERNAL "(Previous) Installation directory of ${PKG}." FORCE)
00418       endif ()
00419     endif ()
00420   endif ()
00421   # --------------------------------------------------------------------------
00422   # unset locally used variables
00423   unset (PACKAGE_DIR)
00424   unset (PKG)
00425   unset (VER)
00426   unset (USE_PKG_OPTION)
00427   unset (PKG_IS_MODULE)
00428   unset (FIND_ADDITIONAL_COMPONENTS)
00429 endmacro ()
00430 
00431 # ----------------------------------------------------------------------------
00432 ## @brief Use found package.
00433 #
00434 # This macro includes the package's use file if the variable @c &lt;Pkg&gt;_USE_FILE
00435 # is defined. Otherwise, it adds the include directories to the search path
00436 # for include paths if possible. Therefore, the corresponding package
00437 # configuration file has to set the proper CMake variables, i.e.,
00438 # either @c &lt;Pkg&gt;_INCLUDES, @c &lt;Pkg&gt;_INCLUDE_DIRS, or @c &lt;Pkg&gt;_INCLUDE_DIR.
00439 #
00440 # If the given package name is the name of another module of this project
00441 # (the top-level project), this function includes the use file of the specified
00442 # module.
00443 #
00444 # @note As some packages still use all captial variables instead of ones
00445 #       prefixed by a string that follows the same capitalization as the
00446 #       package's name, this function also considers these if defined instead.
00447 #       Hence, if @c &lt;PKG&gt;_INCLUDES is defined, but not @c &lt;Pkg&gt;_INCLUDES, it
00448 #       is used in place of the latter.
00449 #
00450 # @note According to an email on the CMake mailing list, it is not a good idea
00451 #       to use basis_link_directories() any more given that the arguments to
00452 #       basis_target_link_libraries() are absolute paths to the library files.
00453 #       Therefore, this code is commented and not used. It remains here as a
00454 #       reminder only.
00455 #
00456 # @param [in] PACKAGE Name of other package. Optionally, the package name
00457 #                     can include a version specification as suffix which
00458 #                     is separated by the package name using a dash (-), i.e.,
00459 #                     &lt;Package&gt;[-major[.minor[.patch[.tweak]]]].
00460 #                     A version specification is simply ignored by this macro.
00461 #
00462 # @ingroup CMakeAPI
00463 macro (basis_use_package PACKAGE)
00464   # tokenize package specification
00465   basis_tokenize_dependency ("${PACKAGE}" PKG VER CMPS)
00466   # use package
00467   foreach (A IN ITEMS "WORKAROUND FOR NOT BEING ABLE TO USE RETURN")
00468     if (BASIS_DEBUG)
00469       message ("** basis_use_package()")
00470       message ("**     Package: ${PKG}")
00471     endif ()
00472     if (PROJECT_IS_MODULE)
00473       # allow modules to specify top-level project as dependency
00474       if (PKG MATCHES "^${BASIS_PROJECT_NAME}$")
00475         if (BASIS_DEBUG)
00476           message ("**     This is the top-level project.")
00477         endif ()
00478         break () # instead of return()
00479       # use other module of top-level project
00480       elseif (PROJECT_MODULES MATCHES "(^|;)${PKG}(;|$)")
00481         if (${PKG}_FOUND)
00482           if (BASIS_DEBUG)
00483             message ("**     Include package use file of other module.")
00484           endif ()
00485           include ("${${PKG}_USE_FILE}")
00486           break () # instead of return()
00487         else ()
00488           message (FATAL_ERROR "Module ${PKG} not found! This must be a mistake of BASIS."
00489                                " Report this issue to the maintainer of this package.")
00490         endif ()
00491       endif ()
00492     endif ()
00493     # if this package is an external project, i.e., a project build as part
00494     # of the same superbuild as this project, set BUNDLE_PROJECT to TRUE.
00495     # it is used by (basis_)link_directories() and add_library() to mark
00496     # the imported link directories and target as belonging to the same
00497     # installation. this is in particular important for the RPATH settings.
00498     # whether this package is an external project or not, is decided by the
00499     # BUNDLE_PROJECTS variable which must be set using the -D option of
00500     # cmake to a list naming all the other packages which are part of the
00501     # superbuild.
00502     if (BUNDLE_PROJECTS)
00503       list (FIND BUNDLE_PROJECTS "${PKG}" IDX)
00504       if (IDX EQUAL -1)
00505         set (BUNDLE_PROJECT FALSE)
00506       else ()
00507         set (BUNDLE_PROJECT TRUE)
00508       endif ()
00509     endif ()
00510     # use external package
00511     if (${PKG}_FOUND)
00512       # use package only if basis_use_package() not invoked before
00513       if (BASIS_USE_${PKG}_INCLUDED)
00514         if (BASIS_DEBUG)
00515           message ("**     External package used before already.")
00516         endif ()
00517         break ()
00518       endif ()
00519       if (${PKG}_USE_FILE)
00520         if (BASIS_DEBUG)
00521           message ("**     Include package use file of external package.")
00522         endif ()
00523         if (PKG MATCHES "^BASIS$")
00524           include ("${${PKG}_USE_FILE}" NO_POLICY_SCOPE)
00525         else ()
00526           include ("${${PKG}_USE_FILE}")
00527         endif ()
00528       else ()
00529         if (BASIS_DEBUG)
00530           message ("**     Use variables which were set by basis_find_package().")
00531         endif ()
00532         # OpenCV
00533         if (PKG MATCHES "^OpenCV$")
00534           # the cv.h may be found as part of PerlLibs, the include path of
00535           # which is added at first by BASISConfig.cmake
00536           if (OpenCV_INCLUDE_DIRS)
00537             basis_include_directories (BEFORE ${OpenCV_INCLUDE_DIRS})
00538           elseif (OpenCV_INCLUDE_DIR)
00539             basis_include_directories (BEFORE ${OpenCV_INCLUDE_DIR})
00540           endif ()
00541         # generic
00542         else ()
00543           if (${PKG}_INCLUDE_DIRS)
00544             basis_include_directories (${${PKG}_INCLUDE_DIRS})
00545           elseif (${PKG}_INCLUDES)
00546             basis_include_directories (${${PKG}_INCLUDES})
00547           elseif (${PKG}_INCLUDE_PATH)
00548             basis_include_directories (${${PKG}_INCLUDE_PATH})
00549           elseif (${PKG}_INCLUDE_DIR)
00550             basis_include_directories (${${PKG}_INCLUDE_DIR})
00551           endif ()
00552         endif ()
00553       endif ()
00554       set (BASIS_USE_${PKG}_INCLUDED TRUE)
00555     elseif (ARGC GREATER 1 AND "${ARGV1}" MATCHES "^REQUIRED$")
00556       if (BASIS_DEBUG)
00557         basis_dump_variables ("${PROJECT_BINARY_DIR}/VariablesAfterFind${PKG}.cmake")
00558       endif ()
00559       message (FATAL_ERROR "Package ${PACKAGE} not found!")
00560     endif ()
00561     # reset switch that identifies currently imported targets and link directories
00562     # as belonging to an external project which is part of the same superbuild
00563     set (BUNDLE_PROJECT FALSE)
00564   endforeach ()
00565 endmacro ()
00566 
00567 # ============================================================================
00568 # basis_get_filename_component / basis_get_relative_path
00569 # ============================================================================
00570 
00571 # ----------------------------------------------------------------------------
00572 ## @brief Fixes CMake's
00573 #         <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_filename_component">
00574 #         get_filename_component()</a> command.
00575 #
00576 # The get_filename_component() command of CMake returns the entire portion
00577 # after the first period (.) [including the period] as extension. However,
00578 # only the component following the last period (.) [including the period]
00579 # should be considered to be the extension.
00580 #
00581 # @note Consider the use of the basis_get_filename_component() macro as
00582 #       an alias to emphasize that this function is different from CMake's
00583 #       <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_filename_component">
00584 #       get_filename_component()</a> command.
00585 #
00586 # @param [in,out] ARGN Arguments as accepted by get_filename_component().
00587 #
00588 # @returns Sets the variable named by the first argument to the requested
00589 #          component of the given file path.
00590 #
00591 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_filename_component
00592 # @sa basis_get_filename_component()
00593 function (get_filename_component)
00594   if (ARGC LESS 3)
00595     message (FATAL_ERROR "[basis_]get_filename_component(): At least three arguments required!")
00596   elseif (ARGC GREATER 4)
00597     message (FATAL_ERROR "[basis_]get_filename_component(): Too many arguments!")
00598   endif ()
00599   list (GET ARGN 0 VAR)
00600   list (GET ARGN 1 STR)
00601   list (GET ARGN 2 CMD)
00602   if (CMD MATCHES "^EXT")
00603     _get_filename_component (${VAR} "${STR}" ${CMD})
00604     string (REGEX MATCHALL "\\.[^.]*" PARTS "${${VAR}}")
00605     list (LENGTH PARTS LEN)
00606     if (LEN GREATER 1)
00607       math (EXPR LEN "${LEN} - 1")
00608       list (GET PARTS ${LEN} ${VAR})
00609     endif ()
00610   elseif (CMD MATCHES "NAME_WE")
00611     _get_filename_component (${VAR} "${STR}" NAME)
00612     string (REGEX REPLACE "\\.[^.]*$" "" ${VAR} ${${VAR}})
00613   else ()
00614     _get_filename_component (${VAR} "${STR}" ${CMD})
00615   endif ()
00616   if (ARGC EQUAL 4)
00617     if (NOT ARGV3 MATCHES "^CACHE$")
00618       message (FATAL_ERROR "[basis_]get_filename_component(): Invalid fourth argument: ${ARGV3}!")
00619     else ()
00620       set (${VAR} "${${VAR}}" CACHE STRING "")
00621     endif ()
00622   else ()
00623     set (${VAR} "${${VAR}}" PARENT_SCOPE)
00624   endif ()
00625 endfunction ()
00626 
00627 # ----------------------------------------------------------------------------
00628 ## @brief Alias for the overwritten get_filename_component() function.
00629 #
00630 # @sa get_filename_component()
00631 #
00632 # @ingroup CMakeAPI
00633 macro (basis_get_filename_component)
00634   get_filename_component (${ARGN})
00635 endmacro ()
00636 
00637 # ----------------------------------------------------------------------------
00638 ## @brief Get path relative to a given base directory.
00639 #
00640 # Unlike the file(RELATIVE_PATH ...) command of CMake which if @p PATH and
00641 # @p BASE are the same directory returns an empty string, this function
00642 # returns a dot (.) in this case instead.
00643 #
00644 # @param [out] REL  @c PATH relative to @c BASE.
00645 # @param [in]  BASE Path of base directory. If a relative path is given, it
00646 #                   is made absolute using basis_get_filename_component()
00647 #                   with ABSOLUTE as last argument.
00648 # @param [in]  PATH Absolute or relative path. If a relative path is given
00649 #                   it is made absolute using basis_get_filename_component()
00650 #                   with ABSOLUTE as last argument.
00651 #
00652 # @returns Sets the variable named by the first argument to the relative path.
00653 #
00654 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:file
00655 #
00656 # @ingroup CMakeAPI
00657 function (basis_get_relative_path REL BASE PATH)
00658   if (BASE MATCHES "^$")
00659     message (FATAL_ERROR "Empty string given where (absolute) base directory path expected!")
00660   endif ()
00661   if (PATH MATCHES "^$")
00662     set (PATH ".")
00663   endif ()
00664   basis_get_filename_component (PATH "${PATH}" ABSOLUTE)
00665   basis_get_filename_component (BASE "${BASE}" ABSOLUTE)
00666   if (NOT PATH)
00667     message (FATAL_ERROR "basis_get_relative_path(): No PATH given!")
00668   endif ()
00669   if (NOT BASE)
00670     message (FATAL_ERROR "basis_get_relative_path(): No BASE given!")
00671   endif ()
00672   file (RELATIVE_PATH P "${BASE}" "${PATH}")
00673   if ("${P}" STREQUAL "")
00674     set (P ".")
00675   endif ()
00676   set (${REL} "${P}" PARENT_SCOPE)
00677 endfunction ()
00678 
00679 # ============================================================================
00680 # name / version
00681 # ============================================================================
00682 
00683 # ----------------------------------------------------------------------------
00684 ## @brief Convert string to lowercase only or mixed case.
00685 #
00686 # Strings in all uppercase or all lowercase are converted to all lowercase
00687 # letters because these are usually used for acronymns. All other strings
00688 # are returned unmodified with the one exception that the first letter has
00689 # to be uppercase for mixed case strings.
00690 #
00691 # This function is in particular used to normalize the project name for use
00692 # in installation directory paths and namespaces.
00693 #
00694 # @param [out] OUT String in CamelCase.
00695 # @param [in]  STR String.
00696 function (basis_normalize_name OUT STR)
00697   # strings in all uppercase or all lowercase such as acronymns are an
00698   # exception and shall be converted to all lowercase instead
00699   string (TOLOWER "${STR}" L)
00700   string (TOUPPER "${STR}" U)
00701   if ("${STR}" STREQUAL "${L}" OR "${STR}" STREQUAL "${U}")
00702     set (${OUT} "${L}" PARENT_SCOPE)
00703   # change first letter to uppercase
00704   else ()
00705     string (SUBSTRING "${U}"   0  1 A)
00706     string (SUBSTRING "${STR}" 1 -1 B)
00707     set (${OUT} "${A}${B}" PARENT_SCOPE)
00708   endif ()
00709 endfunction ()
00710 
00711 # ----------------------------------------------------------------------------
00712 ## @brief Extract version numbers from version string.
00713 #
00714 # @param [in]  VERSION Version string in the format "MAJOR[.MINOR[.PATCH]]".
00715 # @param [out] MAJOR   Major version number if given or 0.
00716 # @param [out] MINOR   Minor version number if given or 0.
00717 # @param [out] PATCH   Patch number if given or 0.
00718 #
00719 # @returns See @c [out] parameters.
00720 function (basis_version_numbers VERSION MAJOR MINOR PATCH)
00721   if (VERSION MATCHES "([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(rc[1-9][0-9]*|[a-z]+)?")
00722     if (CMAKE_MATCH_1)
00723       set (VERSION_MAJOR ${CMAKE_MATCH_1})
00724     else ()
00725       set (VERSION_MAJOR 0)
00726     endif ()
00727     if (CMAKE_MATCH_2)
00728       set (VERSION_MINOR ${CMAKE_MATCH_2})
00729       string (REGEX REPLACE "^\\." "" VERSION_MINOR "${VERSION_MINOR}")
00730     else ()
00731       set (VERSION_MINOR 0)
00732     endif ()
00733     if (CMAKE_MATCH_3)
00734       set (VERSION_PATCH ${CMAKE_MATCH_3})
00735       string (REGEX REPLACE "^\\." "" VERSION_PATCH "${VERSION_PATCH}")
00736     else ()
00737       set (VERSION_PATCH 0)
00738     endif ()
00739   else ()
00740     set (VERSION_MAJOR 0)
00741     set (VERSION_MINOR 0)
00742     set (VERSION_PATCH 0)
00743   endif ()
00744   set ("${MAJOR}" "${VERSION_MAJOR}" PARENT_SCOPE)
00745   set ("${MINOR}" "${VERSION_MINOR}" PARENT_SCOPE)
00746   set ("${PATCH}" "${VERSION_PATCH}" PARENT_SCOPE)
00747 endfunction ()
00748 
00749 # ============================================================================
00750 # set
00751 # ============================================================================
00752 
00753 # ----------------------------------------------------------------------------
00754 ## @brief Set flag given mutually exclusive
00755 #         ARGN_&lt;FLAG&gt; and ARGN_NO&lt;FLAG&gt; function arguments.
00756 #
00757 # @param [in]  PREFIX  Prefix of function arguments. Set to the first argument
00758 #                      of the CMAKE_PARSE_ARGUMENTS() command.
00759 # @param [out] FLAG    Name of flag.
00760 # @param [in]  DEFAULT Default flag value if neither <tt>ARGN_&lt;FLAG;gt;</tt>
00761 #                      nor <tt>ARGN_NO&lt;FLAG;gt;</tt> evaluates to true.
00762 macro (basis_set_flag PREFIX FLAG DEFAULT)
00763   if (${PREFIX}_${FLAG} AND ${PREFIX}_NO${FLAG})
00764     message (FATAL_ERROR "Options ${FLAG} and NO${FLAG} are mutually exclusive!")
00765   endif ()
00766   if (${PREFIX}_${FLAG})
00767     set (${FLAG} TRUE)
00768   elseif (${PREFIX}_NO${FLAG})
00769     set (${FLAG} FALSE)
00770   else ()
00771     set (${FLAG} ${DEFAULT})
00772   endif ()
00773 endmacro ()
00774 
00775 # ----------------------------------------------------------------------------
00776 ## @brief Determine if cache entry exists.
00777 #
00778 # @param [out] VAR   Name of boolean result variable.
00779 # @param [in]  ENTRY Name of cache entry.
00780 macro (basis_is_cached VAR ENTRY)
00781   if (DEFINED ${ENTRY})
00782     get_property (${VAR} CACHE ${ENTRY} PROPERTY TYPE SET)
00783   else ()
00784     set (${VAR} FALSE)
00785   endif ()
00786 endmacro ()
00787 
00788 # ----------------------------------------------------------------------------
00789 ## @brief Set type of variable.
00790 #
00791 # If the variable is cached, the type is updated, otherwise, a cache entry
00792 # of the given type with the current value of the variable is added.
00793 #
00794 # @param [in] VAR  Name of variable.
00795 # @param [in] TYPE Desired type of variable.
00796 # @param [in] ARGN Optional DOC string used if variable was not cached before.
00797 macro (basis_set_or_update_type VAR TYPE)
00798   basis_is_cached (_CACHED ${VAR})
00799   if (_CACHED)
00800     set_property (CACHE ${VAR} PROPERTY TYPE ${TYPE})
00801   else ()
00802     set (${VAR} "${${VAR}}" CACHE ${TYPE} "${ARGN}" FORCE)
00803   endif ()
00804   unset (_CACHED)
00805 endmacro ()
00806 
00807 # ----------------------------------------------------------------------------
00808 ## @brief Change type of cached variable.
00809 #
00810 # If the variable is not cached, nothing is done.
00811 macro (basis_update_type_of_variable VAR TYPE)
00812   basis_is_cached (_CACHED ${VAR})
00813   if (_CACHED)
00814     set_property (CACHE ${VAR} PROPERTY TYPE ${TYPE})
00815   endif ()
00816   unset (_CACHED)
00817 endmacro ()
00818 
00819 # ----------------------------------------------------------------------------
00820 ## @brief Set variable value.
00821 #
00822 # If the variable is cached, this function will update the cache value,
00823 # otherwise, it simply sets the CMake variable uncached to the given value(s).
00824 macro (basis_set_or_update_value VAR)
00825   basis_is_cached (_CACHED ${VAR})
00826   if (_CACHED)
00827     if (ARGC GREATER 1)
00828       set_property (CACHE ${VAR} PROPERTY VALUE ${ARGN})
00829     else ()
00830       set (${VAR} "" CACHE INTERNAL "" FORCE)
00831     endif ()
00832   else ()
00833     set (${VAR} ${ARGN})
00834   endif ()
00835   unset (_CACHED)
00836 endmacro ()
00837 
00838 # ----------------------------------------------------------------------------
00839 ## @brief Update cache variable.
00840 macro (basis_update_value VAR)
00841   basis_is_cached (_CACHED ${VAR})
00842   if (_CACHED)
00843     set_property (CACHE ${VAR} PROPERTY VALUE ${ARGN})
00844   endif ()
00845   unset (_CACHED)
00846 endmacro ()
00847 
00848 # ----------------------------------------------------------------------------
00849 ## @brief Set value of variable only if variable is not set already.
00850 #
00851 # @param [out] VAR  Name of variable.
00852 # @param [in]  ARGN Arguments to set() command excluding variable name.
00853 #
00854 # @returns Sets @p VAR if its value was not valid before.
00855 macro (basis_set_if_empty VAR)
00856   if (NOT ${VAR})
00857     set (${VAR} ${ARGN})
00858   endif ()
00859 endmacro ()
00860 
00861 # ----------------------------------------------------------------------------
00862 ## @brief Set value of variable only if variable is not defined yet.
00863 #
00864 # @param [out] VAR  Name of variable.
00865 # @param [in]  ARGN Arguments to set() command excluding variable name.
00866 #
00867 # @returns Sets @p VAR if it was not defined before.
00868 macro (basis_set_if_not_set VAR)
00869   if (NOT DEFINED "${VAR}")
00870     set ("${VAR}" ${ARGN})
00871   endif ()
00872 endmacro ()
00873 
00874 # ----------------------------------------------------------------------------
00875 ## @brief Set path relative to script file.
00876 #
00877 # This function can be used in script configurations. It takes a variable
00878 # name and a path as input arguments. If the given path is relative, it makes
00879 # it first absolute using @c PROJECT_SOURCE_DIR. Then the path is made
00880 # relative to the directory of the built script file. A CMake variable of the
00881 # given name is set to the specified relative path. Optionally, a third
00882 # argument, the path used for building the script for the install tree
00883 # can be passed as well. If a relative path is given as this argument,
00884 # it is made absolute by prefixing it with @c CMAKE_INSTALL_PREFIX instead.
00885 #
00886 # @note This function may only be used in script configurations such as
00887 #       in particular the ScriptConfig.cmake.in file. It requires that the
00888 #       variables @c __DIR__ and @c BUILD_INSTALL_SCRIPT are set properly.
00889 #       These variables are set by the configure_script() function.
00890 #       Moreover, it makes use of the global @c CMAKE_INSTALL_PREFIX and
00891 #       @c PROJECT_SOURCE_DIR variables.
00892 #
00893 # @param [out] VAR   Name of the variable.
00894 # @param [in]  PATH  Path to directory or file.
00895 # @param [in]  ARGV3 Path to directory or file inside install tree.
00896 #                    If this argument is not given, PATH is used for both
00897 #                    the build and install tree version of the script.
00898 #
00899 # @ingroup CMakeAPI
00900 function (basis_set_script_path VAR PATH)
00901   if (NOT __DIR__)
00902     message (FATAL_ERROR "__DIR__ not set! Note that basis_set_script_path() may"
00903                          " only be used in script configurations (e.g., ScriptConfig.cmake.in).")
00904   endif ()
00905   if (ARGC GREATER 3)
00906     message (FATAL_ERROR "Too many arguments given for function basis_set_script_path()")
00907   endif ()
00908   if (ARGC EQUAL 3 AND BUILD_INSTALL_SCRIPT)
00909     set (PREFIX "${CMAKE_INSTALL_PREFIX}")
00910     set (PATH   "${ARGV2}")
00911   else ()
00912     set (PREFIX "${PROJECT_SOURCE_DIR}")
00913   endif ()
00914   if (NOT IS_ABSOLUTE "${PATH}")
00915     set (PATH "${PREFIX}/${PATH}")
00916   endif ()
00917   basis_get_relative_path (PATH "${__DIR__}" "${PATH}")
00918   if (NOT PATH)
00919     set (PATH ".")
00920   endif ()
00921   string (REGEX REPLACE "/$" "" PATH "${PATH}")
00922   set (${VAR} "${PATH}" PARENT_SCOPE)
00923 endfunction ()
00924 
00925 # ============================================================================
00926 # set/get any property
00927 # ============================================================================
00928 
00929 # ----------------------------------------------------------------------------
00930 ## @brief Convert list into regular expression.
00931 #
00932 # This function is in particular used to convert a list of property names
00933 # such as &lt;CONFIG&gt;_OUTPUT_NAME, e.g., the list @c BASIS_PROPERTIES_ON_TARGETS,
00934 # into a regular expression which can be used in pattern matches.
00935 #
00936 # @param [out] REGEX Name of variable for resulting regular expression.
00937 # @param [in]  ARGN  List of patterns which may contain placeholders in the
00938 #                    form of "<this is a placeholder>". These are replaced
00939 #                    by the regular expression "[^ ]+".
00940 macro (basis_list_to_regex REGEX)
00941   string (REGEX REPLACE "<[^>]+>" "[^ ]+" ${REGEX} "${ARGN}")
00942   string (REGEX REPLACE ";" "|" ${REGEX} "${${REGEX}}")
00943   set (${REGEX} "^(${${REGEX}})$")
00944 endmacro ()
00945 
00946 # ----------------------------------------------------------------------------
00947 ## @brief Output current CMake variables to file.
00948 function (basis_dump_variables RESULT_FILE)
00949   set (DUMP)
00950   get_cmake_property (VARIABLE_NAMES VARIABLES)
00951   foreach (V IN LISTS VARIABLE_NAMES)
00952     if (NOT V MATCHES "^_|^RESULT_FILE$|^ARGC$|^ARGV[0-9]?$|^ARGN_")
00953       set (VALUE "${${V}}")
00954       # sanitize value for use in set() command
00955       string (REPLACE "\\" "\\\\" VALUE "${VALUE}") # escape backspaces
00956       string (REPLACE "\"" "\\\"" VALUE "${VALUE}") # escape double quotes
00957       # Escape ${VAR} by \${VAR} such that CMake does not evaluate it.
00958       # Escape $STR{VAR} by \$STR{VAR} such that CMake does not report a
00959       # syntax error b/c it expects either ${VAR}, $ENV{VAR}, or $CACHE{VAR}.
00960       # Escape @VAR@ by \@VAR\@ such that CMake does not evaluate it.
00961       string (REGEX REPLACE "([^\\])\\\$([^ ]*){" "\\1\\\\\$\\2{" VALUE "${VALUE}")
00962       string (REGEX REPLACE "([^\\])\\\@([^ ]*)\@" "\\1\\\\\@\\2\\\\\@" VALUE "${VALUE}")
00963       # append variable to output file
00964       set (DUMP "${DUMP}set (${V} \"${VALUE}\")\n")
00965     endif ()
00966   endforeach ()
00967   file (WRITE "${RESULT_FILE}" "# CMake variables dump created by BASIS\n${DUMP}")
00968 endfunction ()
00969 
00970 # ----------------------------------------------------------------------------
00971 ## @brief Write CMake script file which sets the named variable to the
00972 #         specified (list of) values.
00973 function (basis_write_list FILENAME VARIABLE)
00974   file (WRITE "${FILENAME}" "# Automatically generated. Do not edit this file!\nset (${VARIABLE}\n")
00975   foreach (V IN LISTS ARGN)
00976     file (APPEND "${FILENAME}" "  \"${V}\"\n")
00977   endforeach ()
00978   file (APPEND "${FILENAME}" ")\n")
00979 endfunction ()
00980 
00981 # ----------------------------------------------------------------------------
00982 ## @brief Set a named property in a given scope.
00983 #
00984 # This function replaces CMake's
00985 # <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property">
00986 # set_property()</a> command.
00987 #
00988 # @param [in] SCOPE The argument for the @p SCOPE parameter of
00989 #                   <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property">
00990 #                   set_property()</a>.
00991 # @param [in] ARGN  Arguments as accepted by.
00992 #                   <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property">
00993 #                   set_property()</a>.
00994 #
00995 # @returns Sets the specified property.
00996 #
00997 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property
00998 #
00999 # @ingroup CMakeAPI
01000 function (basis_set_property SCOPE)
01001   if (SCOPE MATCHES "^TARGET$|^TEST$")
01002     # map target/test names to UIDs
01003     list (LENGTH ARGN ARGN_LENGTH)
01004     if (ARGN_LENGTH EQUAL 0)
01005       message (FATAL_ERROR "basis_set_property(${SCOPE}): Expected arguments after SCOPE!")
01006     endif ()
01007     set (IDX 0)
01008     set (ARG)
01009     while (IDX LESS ARGN_LENGTH)
01010       list (GET ARGN ${IDX} ARG)
01011       if (ARG MATCHES "^APPEND$")
01012         math (EXPR IDX "${IDX} + 1")
01013         list (GET ARGN ${IDX} ARG)
01014         if (NOT ARG MATCHES "^PROPERTY$")
01015           message (FATAL_ERROR "basis_set_properties(${SCOPE}): Expected PROPERTY keyword after APPEND!")
01016         endif ()
01017         break ()
01018       elseif (ARG MATCHES "^PROPERTY$")
01019         break ()
01020       else ()
01021         if (SCOPE MATCHES "^TEST$")
01022           basis_get_test_uid (UID "${ARG}")
01023         else ()
01024           basis_get_target_uid (UID "${ARG}")
01025         endif ()
01026         list (INSERT ARGN ${IDX} "${UID}")
01027         math (EXPR IDX "${IDX} + 1")
01028         list (REMOVE_AT ARGN ${IDX}) # after insert to avoid index out of range
01029       endif ()
01030     endwhile ()
01031     if (IDX EQUAL ARGN_LENGTH)
01032       message (FATAL_ERROR "basis_set_properties(${SCOPE}): Missing PROPERTY keyword!")
01033     endif ()
01034     math (EXPR IDX "${IDX} + 1")
01035     list (GET ARGN ${IDX} ARG)
01036     # property name matches DEPENDS
01037     if (ARG MATCHES "DEPENDS")
01038       math (EXPR IDX "${IDX} + 1")
01039       while (IDX LESS ARGN_LENGTH)
01040         list (GET ARGN ${IDX} ARG)
01041         if (SCOPE MATCHES "^TEST$")
01042           basis_get_test_uid (UID "${ARG}")
01043         else ()
01044           basis_get_target_uid (UID "${ARG}")
01045         endif ()
01046         list (INSERT ARGN ${IDX} "${UID}")
01047         math (EXPR IDX "${IDX} + 1")
01048         list (REMOVE_AT ARGN ${IDX}) # after insert ot avoid index out of range
01049       endwhile ()
01050     endif ()
01051   endif ()
01052   if (BASIS_DEBUG)
01053     message ("** basis_set_property():")
01054     message ("**   Scope:     ${SCOPE}")
01055     message ("**   Arguments: [${ARGN}]")
01056   endif ()
01057   set_property (${SCOPE} ${ARGN})
01058 endfunction ()
01059 
01060 # ----------------------------------------------------------------------------
01061 ## @brief Get a property.
01062 #
01063 # This function replaces CMake's
01064 # <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
01065 # get_property()</a> command.
01066 #
01067 # @param [out] VAR     Property value.
01068 # @param [in]  SCOPE   The argument for the @p SCOPE argument of
01069 #                      <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
01070 #                      get_property()</a>.
01071 # @param [in]  ELEMENT The argument for the @p ELEMENT argument of
01072 #                      <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
01073 #                      get_property()</a>.
01074 # @param [in]  ARGN    Arguments as accepted by
01075 #                      <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
01076 #                      get_property()</a>.
01077 #
01078 # @returns Sets @p VAR to the value of the requested property.
01079 #
01080 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property
01081 #
01082 # @ingroup CMakeAPI
01083 function (basis_get_property VAR SCOPE ELEMENT)
01084   if (SCOPE MATCHES "^TARGET$")
01085     basis_get_target_uid (ELEMENT "${ELEMENT}")
01086   elseif (SCOPE MATCHES "^TEST$")
01087     basis_get_test_uid (ELEMENT "${ELEMENT}")
01088   endif ()
01089   get_property (VALUE ${SCOPE} ${ELEMENT} ${ARGN})
01090   set ("${VAR}" "${VALUE}" PARENT_SCOPE)
01091 endfunction ()
01092 
01093 # ----------------------------------------------------------------------------
01094 ## @brief Set project-global property.
01095 #
01096 # Set property associated with current project/module. The property is in
01097 # fact just a cached variable whose name is prefixed by the project's name.
01098 function (basis_set_project_property)
01099   CMAKE_PARSE_ARGUMENTS (
01100     ARGN
01101       "APPEND"
01102       "PROJECT"
01103       "PROPERTY"
01104     ${ARGN}
01105   )
01106 
01107   if (NOT ARGN_PROJECT)
01108     set (ARGN_PROJECT "${PROJECT_NAME}")
01109   endif ()
01110   if (NOT ARGN_PROPERTY)
01111     message (FATAL_ERROR "Missing PROPERTY argument!")
01112   endif ()
01113 
01114   list (GET ARGN_PROPERTY 0 PROPERTY_NAME)
01115   list (REMOVE_AT ARGN_PROPERTY 0) # remove property name from values
01116 
01117   if (ARGN_APPEND)
01118     basis_get_project_property (CURRENT PROPERTY ${PROPERTY_NAME})
01119     if (NOT "${CURRENT}" STREQUAL "")
01120       list (INSERT ARGN_PROPERTY 0 "${CURRENT}")
01121     endif ()
01122   endif ()
01123 
01124   set (
01125     ${ARGN_PROJECT}_${PROPERTY_NAME}
01126       "${ARGN_PROPERTY}"
01127     CACHE INTERNAL
01128       "Property ${PROPERTY_NAME} of project ${ARGN_PROJECT}."
01129     FORCE
01130   )
01131 endfunction ()
01132 
01133 # ----------------------------------------------------------------------------
01134 ## @brief Get project-global property value.
01135 #
01136 # Example:
01137 # @code
01138 # basis_get_project_property(TARGETS)
01139 # basis_get_project_property(TARGETS ${PROJECT_NAME})
01140 # basis_get_project_property(TARGETS ${PROJECT_NAME} TARGETS)
01141 # basis_get_project_property(TARGETS PROPERTY TARGETS)
01142 # @endcode
01143 #
01144 # @param [out] VARIABLE Name of result variable.
01145 # @param [in]  ARGN     See the example uses. The optional second argument
01146 #                       is either the name of the project similar to CMake's
01147 #                       get_target_property() command or the keyword PROPERTY
01148 #                       followed by the name of the property.
01149 function (basis_get_project_property VARIABLE)
01150   if (ARGC GREATER 3)
01151     message (FATAL_ERROR "Too many arguments!")
01152   endif ()
01153   if (ARGC EQUAL 1)
01154     set (ARGN_PROJECT "${PROJECT_NAME}")
01155     set (ARGN_PROPERTY "${VARIABLE}")
01156   elseif (ARGC EQUAL 2)
01157     if (ARGV1 MATCHES "^PROPERTY$")
01158       message (FATAL_ERROR "Expected argument after PROPERTY keyword!")
01159     endif ()
01160     set (ARGN_PROJECT  "${ARGV1}")
01161     set (ARGN_PROPERTY "${VARIABLE}")
01162   else ()
01163     if (ARGV1 MATCHES "^PROPERTY$")
01164       set (ARGN_PROJECT "${PROJECT_NAME}")
01165     else ()
01166       set (ARGN_PROJECT  "${ARGV1}")
01167     endif ()
01168     set (ARGN_PROPERTY "${ARGV2}")
01169   endif ()
01170   set (${VARIABLE} "${${ARGN_PROJECT}_${ARGN_PROPERTY}}" PARENT_SCOPE)
01171 endfunction ()
01172 
01173 # ============================================================================
01174 # list / string manipulations
01175 # ============================================================================
01176 
01177 # ----------------------------------------------------------------------------
01178 ## @brief Sanitize string variable for use in regular expression.
01179 #
01180 # @note This function may not work for all cases, but is used in particular
01181 #       to sanitize project names, target names, namespace identifiers,...
01182 #
01183 # @param [out] OUT String that can be used in regular expression.
01184 # @param [in]  STR String to sanitize.
01185 macro (basis_sanitize_for_regex OUT STR)
01186   string (REGEX REPLACE "([.+*?^$])" "\\\\\\1" ${OUT} "${STR}")
01187 endmacro ()
01188 
01189 # ----------------------------------------------------------------------------
01190 ## @brief Concatenates all list elements into a single string.
01191 #
01192 # The list elements are concatenated without any delimiter in between.
01193 # Use basis_list_to_delimited_string() to specify a delimiter such as a
01194 # whitespace character or comma (,) as delimiter.
01195 #
01196 # @param [out] STR  Output string.
01197 # @param [in]  ARGN Input list.
01198 #
01199 # @returns Sets @p STR to the resulting string.
01200 #
01201 # @sa basis_list_to_delimited_string()
01202 function (basis_list_to_string STR)
01203   set (OUT)
01204   foreach (ELEM ${ARGN})
01205     set (OUT "${OUT}${ELEM}")
01206   endforeach ()
01207   set ("${STR}" "${OUT}" PARENT_SCOPE)
01208 endfunction ()
01209 
01210 # ----------------------------------------------------------------------------
01211 ## @brief Concatenates all list elements into a single delimited string.
01212 #
01213 # @param [out] STR   Output string.
01214 # @param [in]  DELIM Delimiter used to separate list elements.
01215 #                    Each element which contains the delimiter as substring
01216 #                    is surrounded by double quotes (") in the output string.
01217 # @param [in]  ARGN  Input list. If this list starts with the argument
01218 #                    @c NOAUTOQUOTE, the automatic quoting of list elements
01219 #                    which contain the delimiter is disabled.
01220 #
01221 # @returns Sets @p STR to the resulting string.
01222 function (basis_list_to_delimited_string STR DELIM)
01223   set (OUT)
01224   set (AUTOQUOTE TRUE)
01225   if (ARGN)
01226     list (GET ARGN 0 FIRST)
01227     if (FIRST MATCHES "^NOAUTOQUOTE$")
01228       list (REMOVE_AT ARGN 0)
01229       set (AUTOQUOTE FALSE)
01230     endif ()
01231   endif ()
01232   foreach (ELEM ${ARGN})
01233     if (OUT)
01234       set (OUT "${OUT}${DELIM}")
01235     endif ()
01236     if (AUTOQUOTE AND ELEM MATCHES "${DELIM}")
01237       set (OUT "${OUT}\"${ELEM}\"")
01238     else ()
01239       set (OUT "${OUT}${ELEM}")
01240     endif ()
01241   endforeach ()
01242   set ("${STR}" "${OUT}" PARENT_SCOPE)
01243 endfunction ()
01244 
01245 # ----------------------------------------------------------------------------
01246 ## @brief Splits a string at space characters into a list.
01247 #
01248 # @todo Probably this can be done in a better way...
01249 #       Difficulty is, that string(REPLACE) does always replace all
01250 #       occurrences. Therefore, we need a regular expression which matches
01251 #       the entire string. More sophisticated regular expressions should do
01252 #       a better job, though.
01253 #
01254 # @param [out] LST  Output list.
01255 # @param [in]  STR  Input string.
01256 #
01257 # @returns Sets @p LST to the resulting CMake list.
01258 function (basis_string_to_list LST STR)
01259   set (TMP "${STR}")
01260   set (OUT)
01261   # 1. extract elements such as "a string with spaces"
01262   while (TMP MATCHES "\"[^\"]*\"")
01263     string (REGEX REPLACE "^(.*)\"([^\"]*)\"(.*)$" "\\1\\3" TMP "${TMP}")
01264     if (OUT)
01265       set (OUT "${CMAKE_MATCH_2};${OUT}")
01266     else (OUT)
01267       set (OUT "${CMAKE_MATCH_2}")
01268     endif ()
01269   endwhile ()
01270   # 2. extract other elements separated by spaces (excluding first and last)
01271   while (TMP MATCHES " [^\" ]+ ")
01272     string (REGEX REPLACE "^(.*) ([^\" ]+) (.*)$" "\\1\\3" TMP "${TMP}")
01273     if (OUT)
01274       set (OUT "${CMAKE_MATCH_2};${OUT}")
01275     else (OUT)
01276       set (OUT "${CMAKE_MATCH_2}")
01277     endif ()
01278   endwhile ()
01279   # 3. extract first and last elements (if not done yet)
01280   if (TMP MATCHES "^[^\" ]+")
01281     set (OUT "${CMAKE_MATCH_0};${OUT}")
01282   endif ()
01283   if (NOT "${CMAKE_MATCH_0}" STREQUAL "${TMP}" AND TMP MATCHES "[^\" ]+$")
01284     set (OUT "${OUT};${CMAKE_MATCH_0}")
01285   endif ()
01286   # return resulting list
01287   set (${LST} "${OUT}" PARENT_SCOPE)
01288 endfunction ()
01289 
01290 # ----------------------------------------------------------------------------
01291 ## @brief Compare two lists.
01292 #
01293 # @param [out] RESULT Result of comparison.
01294 # @param [in]  LIST1  Name of variable holding the first list.
01295 # @param [in]  LIST2  Name of varaible holding the second list.
01296 #
01297 # @retval 0 The two lists are not identical.
01298 # @retval 1 Both lists have identical elements (not necessarily in the same order).
01299 macro (basis_compare_lists RESULT LIST1 LIST2)
01300   set (_L1 "${${LIST1}}")
01301   set (_L2 "${${LIST2}}")
01302   list (SORT _L1)
01303   list (SORT _L2)
01304   if ("${_L1}" STREQUAL "${_L2}")
01305     set (RESULT TRUE)
01306   else ()
01307     set (RESULT FALSE)
01308   endif ()
01309   unset (_L1)
01310   unset (_L2)
01311 endmacro ()
01312 
01313 # ============================================================================
01314 # name <=> UID
01315 # ============================================================================
01316 
01317 # ----------------------------------------------------------------------------
01318 ## @brief Derive target name from source file name.
01319 #
01320 # @param [out] TARGET_NAME Target name.
01321 # @param [in]  SOURCE_FILE Source file.
01322 # @param [in]  ARGN        Third argument to get_filename_component().
01323 #                          If not specified, the given path is only sanitized.
01324 #
01325 # @returns Target name derived from @p SOURCE_FILE.
01326 function (basis_get_source_target_name TARGET_NAME SOURCE_FILE)
01327   # remove ".in" suffix from file name
01328   string (REGEX REPLACE "\\.in$" "" OUT "${SOURCE_FILE}")
01329   # get name component
01330   if (ARGC GREATER 2)
01331     get_filename_component (OUT "${OUT}" ${ARGV2})
01332   endif ()
01333   # replace special characters
01334   string (REGEX REPLACE "[./\\]" "_" OUT "${OUT}")
01335   # return
01336   set (${TARGET_NAME} "${OUT}" PARENT_SCOPE)
01337 endfunction ()
01338 
01339 # ----------------------------------------------------------------------------
01340 ## @brief Make target UID from given target name.
01341 #
01342 # This function is intended for use by the basis_add_*() functions only.
01343 #
01344 # @param [out] TARGET_UID  "Global" target name, i.e., actual CMake target name.
01345 # @param [in]  TARGET_NAME Target name used as argument to BASIS CMake functions.
01346 #
01347 # @returns Sets @p TARGET_UID to the UID of the build target @p TARGET_NAME.
01348 #
01349 # @sa basis_get_target_uid()
01350 macro (basis_make_target_uid TARGET_UID TARGET_NAME)
01351   set (${TARGET_UID} "${PROJECT_NAMESPACE_CMAKE}.${TARGET_NAME}")
01352   # optionally strip off top-level namespace part
01353   if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
01354     basis_sanitize_for_regex (_bmtu_RE "${BASIS_PROJECT_NAMESPACE_CMAKE}")
01355     string (REGEX REPLACE "^${_bmtu_RE}\\." "" ${TARGET_UID} "${${TARGET_UID}}")
01356     unset (_bmtu_RE)
01357   endif ()
01358 endmacro ()
01359 
01360 # ----------------------------------------------------------------------------
01361 ## @brief Get "global" target name, i.e., actual CMake target name.
01362 #
01363 # In order to ensure that CMake target names are unique across modules of
01364 # a BASIS project, the target name given to the BASIS CMake functions is
01365 # converted by basis_make_target_uid() into a so-called target UID which is
01366 # used as actual CMake target name. This function can be used to get for a
01367 # given target name or UID the closest match of a known target UID.
01368 #
01369 # The individual parts of the target UID, i.e, package name,
01370 # module name, and target name are separated by a dot (.).
01371 # If @c BASIS_USE_FULLY_QUALIFIED_UIDS is set to @c OFF, the common part of
01372 # all target UIDs is removed by this function from the target UID.
01373 # When the target is exported, however, this common part will be
01374 # prefixed again. This is done by the basis_export_targets() function.
01375 #
01376 # Note that names of imported targets are not prefixed in any case.
01377 #
01378 # The counterpart basis_get_target_name() can be used to convert the target UID
01379 # back to the target name without namespace prefix.
01380 #
01381 # @note At the moment, BASIS does not support modules which themselves have
01382 #       modules again. This would require a more nested namespace hierarchy
01383 #       and makes things unnecessarily complicated.
01384 #
01385 # @param [out] TARGET_UID  "Global" target name, i.e., actual CMake target name.
01386 # @param [in]  TARGET_NAME Target name used as argument to BASIS CMake functions.
01387 #
01388 # @returns Sets @p TARGET_UID to the UID of the build target @p TARGET_NAME.
01389 #
01390 # @sa basis_get_target_name()
01391 function (basis_get_target_uid TARGET_UID TARGET_NAME)
01392   basis_sanitize_for_regex (BASE_RE "${BASIS_PROJECT_NAMESPACE_CMAKE}")
01393   # in case of a leading namespace separator, do not modify target name
01394   if (TARGET_NAME MATCHES "^\\.")
01395     set (UID "${TARGET_NAME}")
01396   # otherwise,
01397   else ()
01398     set (UID "${TARGET_NAME}")
01399     # try prepending namespace or parts of it until target is known,
01400     # first assuming the simplified UIDs without the common prefix
01401     # of this package which applies to targets of this package
01402     if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS AND NOT TARGET "${UID}")
01403       string (REGEX REPLACE "^${BASE_RE}\\." "" PREFIX "${PROJECT_NAMESPACE_CMAKE}")
01404       while (PREFIX)
01405         if (TARGET "${PREFIX}.${TARGET_NAME}")
01406           set (UID "${PREFIX}.${TARGET_NAME}")
01407           break ()
01408         else ()
01409           if (PREFIX MATCHES "(.*)\\.[^.]+")
01410             set (PREFIX "${CMAKE_MATCH_1}")
01411           else ()
01412             break ()
01413           endif ()
01414         endif ()
01415       endwhile ()
01416     endif ()
01417     # and then with the fully qualified UIDs for imported targets
01418     if (NOT TARGET "${UID}")
01419       set (PREFIX "${PROJECT_NAMESPACE_CMAKE}")
01420       while (PREFIX)
01421         if (TARGET "${PREFIX}.${TARGET_NAME}")
01422           set (UID "${PREFIX}.${TARGET_NAME}")
01423           break ()
01424         else ()
01425           if (PREFIX MATCHES "(.*)\\.[^.]+")
01426             set (PREFIX "${CMAKE_MATCH_1}")
01427           else ()
01428             break ()
01429           endif ()
01430         endif ()
01431       endwhile ()
01432     endif ()
01433   endif ()
01434   # strip off top-level namespace part (optional)
01435   if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
01436     string (REGEX REPLACE "^${BASE_RE}\\." "" UID "${UID}")
01437   endif ()
01438   # return
01439   set ("${TARGET_UID}" "${UID}" PARENT_SCOPE)
01440 endfunction ()
01441 
01442 # ----------------------------------------------------------------------------
01443 ## @brief Get fully-qualified target name.
01444 #
01445 # This function always returns a fully-qualified target UID, no matter if
01446 # the option @c BASIS_USE_FULLY_QUALIFIED_UIDS is @c OFF. Note that
01447 # if this option is @c ON, the returned target UID is may not be the
01448 # actual name of a CMake target.
01449 #
01450 # @param [out] TARGET_UID  Fully-qualified target UID.
01451 # @param [in]  TARGET_NAME Target name used as argument to BASIS CMake functions.
01452 #
01453 # @sa basis_get_target_uid()
01454 function (basis_get_fully_qualified_target_uid TARGET_UID TARGET_NAME)
01455   basis_get_target_uid (UID "${TARGET_NAME}")
01456   if (TARGET "${UID}" AND NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
01457     get_target_property (IMPORTED "${UID}" IMPORTED)
01458     if (NOT IMPORTED)
01459       set (UID "${BASIS_PROJECT_NAMESPACE_CMAKE}.${UID}")
01460     endif ()
01461   endif ()
01462   set (${TARGET_UID} "${UID}" PARENT_SCOPE)
01463 endfunction ()
01464 
01465 # ----------------------------------------------------------------------------
01466 ## @brief Get namespace of build target.
01467 #
01468 # @param [out] TARGET_NS  Namespace part of target UID.
01469 # @param [in]  TARGET_UID Target UID/name.
01470 function (basis_get_target_namespace TARGET_NS TARGET_UID)
01471   # make sure we have a fully-qualified target UID
01472   basis_get_fully_qualified_target_uid (UID "${TARGET_UID}")
01473   # return namespace part
01474   if (UID MATCHES "^(.*)\\.")
01475     set ("${TARGET_NS}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
01476   else ()
01477     set ("${TARGET_NS}" "" PARENT_SCOPE)
01478   endif ()
01479 endfunction ()
01480 
01481 # ----------------------------------------------------------------------------
01482 ## @brief Get "local" target name, i.e., BASIS target name.
01483 #
01484 # @param [out] TARGET_NAME Target name used as argument to BASIS functions.
01485 # @param [in]  TARGET_UID  "Global" target name, i.e., actual CMake target name.
01486 #
01487 # @returns Sets @p TARGET_NAME to the name of the build target with UID @p TARGET_UID.
01488 #
01489 # @sa basis_get_target_uid()
01490 function (basis_get_target_name TARGET_NAME TARGET_UID)
01491   # make sure we have a fully-qualified target UID
01492   basis_get_fully_qualified_target_uid (UID "${TARGET_UID}")
01493   # strip off namespace of current project
01494   basis_sanitize_for_regex (RE "${PROJECT_NAMESPACE_CMAKE}")
01495   string (REGEX REPLACE "^${RE}\\." "" NAME "${UID}")
01496   # return
01497   set (${TARGET_NAME} "${NAME}" PARENT_SCOPE)
01498 endfunction ()
01499 
01500 # ----------------------------------------------------------------------------
01501 ## @brief Checks whether a given name is a valid target name.
01502 #
01503 # Displays fatal error message when target name is invalid.
01504 #
01505 # @param [in] TARGET_NAME Desired target name.
01506 #
01507 # @returns Nothing.
01508 function (basis_check_target_name TARGET_NAME)
01509   # reserved target name ?
01510   foreach (PATTERN IN LISTS BASIS_RESERVED_TARGET_NAMES)
01511     if (TARGET_NAME MATCHES "^${PATTERN}$")
01512       message (FATAL_ERROR "Target name \"${TARGET_NAME}\" is reserved and cannot be used.")
01513     endif ()
01514   endforeach ()
01515   # invalid target name ?
01516   if (NOT TARGET_NAME MATCHES "^[a-zA-Z]([a-zA-Z0-9_+]|-)*$|^__init__(_py)?$")
01517     message (FATAL_ERROR "Target name '${TARGET_NAME}' is invalid.\nChoose a target name"
01518                          " which only contains alphanumeric characters,"
01519                          " '_', '-', or '+', and starts with a letter."
01520                          " The only exception from this rule is __init__[_py] for"
01521                          " a __init__.py script.\n")
01522   endif ()
01523 
01524   # unique ?
01525   basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
01526   if (TARGET "${TARGET_UID}")
01527     message (FATAL_ERROR "There exists already a target named ${TARGET_UID}."
01528                          " Target names must be unique.")
01529   endif ()
01530 endfunction ()
01531 
01532 # ----------------------------------------------------------------------------
01533 ## @brief Make test UID from given test name.
01534 #
01535 # This function is intended for use by the basis_add_test() only.
01536 #
01537 # @param [out] TEST_UID  "Global" test name, i.e., actual CTest test name.
01538 # @param [in]  TEST_NAME Test name used as argument to BASIS CMake functions.
01539 #
01540 # @returns Sets @p TEST_UID to the UID of the test @p TEST_NAME.
01541 #
01542 # @sa basis_get_test_uid()
01543 macro (basis_make_test_uid TEST_UID TEST_NAME)
01544   basis_make_target_uid ("${TEST_UID}" "${TEST_NAME}")
01545 endmacro ()
01546 
01547 # ----------------------------------------------------------------------------
01548 ## @brief Get "global" test name, i.e., actual CTest test name.
01549 #
01550 # In order to ensure that CTest test names are unique across BASIS projects,
01551 # the test name used by a developer of a BASIS project is converted by this
01552 # function into another test name which is used as actual CTest test name.
01553 #
01554 # The function basis_get_test_name() can be used to convert the unique test
01555 # name, the test UID, back to the original test name passed to this function.
01556 #
01557 # @param [out] TEST_UID  "Global" test name, i.e., actual CTest test name.
01558 # @param [in]  TEST_NAME Test name used as argument to BASIS CMake functions.
01559 #
01560 # @returns Sets @p TEST_UID to the UID of the test @p TEST_NAME.
01561 #
01562 # @sa basis_get_test_name()
01563 function (basis_get_test_uid TEST_UID TEST_NAME)
01564   if (TEST_NAME MATCHES "^\\.")
01565     set (UID "${TEST_NAME}")
01566   else ()
01567     set (UID "${PROJECT_NAMESPACE_CMAKE}.${TEST_NAME}")
01568   endif ()
01569   # strip off top-level namespace part (optional)
01570   if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
01571     basis_sanitize_for_regex (RE "${BASIS_PROJECT_NAMESPACE_CMAKE}")
01572     string (REGEX REPLACE "^${RE}\\." "" UID "${UID}")
01573   endif ()
01574   # return
01575   set (${TEST_UID} "${UID}" PARENT_SCOPE)
01576 endfunction ()
01577 
01578 # ----------------------------------------------------------------------------
01579 ## @brief Get "global" test name, i.e., actual CTest test name.
01580 #
01581 # This function always returns a fully-qualified test UID, no matter if
01582 # the option @c BASIS_USE_FULLY_QUALIFIED_UIDS is @c OFF. Note that
01583 # if this option is @c ON, the returned test UID may not be the
01584 # actual name of a CMake test.
01585 #
01586 # @param [out] TEST_UID  Fully-qualified test UID.
01587 # @param [in]  TEST_NAME Test name used as argument to BASIS CMake functions.
01588 #
01589 # @sa basis_get_test_uid()
01590 function (basis_get_fully_qualified_test_uid TEST_UID TEST_NAME)
01591   if (TEST_NAME MATCHES "\\.")
01592     set (UID "${TEST_NAME}")
01593   else ()
01594     set (UID "${BASIS_PROJECT_NAMESPACE_CMAKE}.${TEST_NAME}")
01595   endif ()
01596   set (${TEST_UID} "${UID}" PARENT_SCOPE)
01597 endfunction ()
01598 
01599 # ----------------------------------------------------------------------------
01600 ## @brief Get namespace of test.
01601 #
01602 # @param [out] TEST_NS  Namespace part of test UID. If @p TEST_UID is
01603 #                       no UID, i.e., does not contain a namespace part,
01604 #                       the namespace of this project is returned.
01605 # @param [in]  TEST_UID Test UID/name.
01606 macro (basis_get_test_namespace TEST_NS TEST_UID)
01607   if (TEST_UID MATCHES "^(.*)\\.")
01608     set (${TEST_NS} "${CMAKE_MATCH_1}")
01609   else ()
01610     set (${TEST_NS} "")
01611   endif ()
01612 endmacro ()
01613 
01614 # ----------------------------------------------------------------------------
01615 ## @brief Get "local" test name, i.e., BASIS test name.
01616 #
01617 # @param [out] TEST_NAME Test name used as argument to BASIS functions.
01618 # @param [in]  TEST_UID  "Global" test name, i.e., actual CTest test name.
01619 #
01620 # @returns Sets @p TEST_NAME to the name of the test with UID @p TEST_UID.
01621 #
01622 # @sa basis_get_test_uid()
01623 macro (basis_get_test_name TEST_NAME TEST_UID)
01624   if (TEST_UID MATCHES "([^.]+)$")
01625     set (${TEST_NAME} "${CMAKE_MATCH_1}")
01626   else ()
01627     set (${TEST_NAME} "")
01628   endif ()
01629 endmacro ()
01630 
01631 # ----------------------------------------------------------------------------
01632 ## @brief Checks whether a given name is a valid test name.
01633 #
01634 # Displays fatal error message when test name is invalid.
01635 #
01636 # @param [in] TEST_NAME Desired test name.
01637 #
01638 # @returns Nothing.
01639 function (basis_check_test_name TEST_NAME)
01640   # reserved test name ?
01641   foreach (PATTERN IN LISTS BASIS_RESERVED_TARGET_NAMES)
01642     if (TARGET_NAME MATCHES "^${PATTERN}$")
01643       message (FATAL_ERROR "Test name \"${TARGET_NAME}\" is reserved and cannot be used.")
01644     endif ()
01645   endforeach ()
01646   # invalid test name ?
01647   if (NOT TEST_NAME MATCHES "^[a-zA-Z]([a-zA-Z0-9_+]|-)*$")
01648     message (FATAL_ERROR "Test name ${TEST_NAME} is invalid.\nChoose a test name "
01649                          " which only contains alphanumeric characters,"
01650                          " '_', '-', or '+', and starts with a letter.\n")
01651   endif ()
01652 endfunction ()
01653 
01654 # ============================================================================
01655 # common target tools
01656 # ============================================================================
01657 
01658 # ----------------------------------------------------------------------------
01659 ## @brief Whether a given target exists.
01660 #
01661 # This function should be used instead of the if(TARGET) command of CMake
01662 # because target names are mapped by BASIS to target UIDs.
01663 #
01664 # @param [out] RESULT_VARIABLE Boolean result variable.
01665 # @param [in]  TARGET_NAME     Name which to check whether it is a target.
01666 #
01667 # @sa basis_make_target_uid()
01668 # @sa basis_get_target_uid()
01669 macro (basis_exists_target RESULT_VARIABLE TARGET_NAME)
01670   basis_get_target_uid (_UID "${TARGET_NAME}")
01671   if (TARGET ${_UID})
01672     set (${RESULT_VARIABLE} TRUE)
01673   else ()
01674     set (${RESULT_VARIABLE} FALSE)
01675   endif ()
01676   unset (_UID)
01677 endmacro ()
01678 
01679 # ----------------------------------------------------------------------------
01680 ## @brief Get default subdirectory prefix of scripted library modules.
01681 #
01682 # @param [out] PREFIX   Name of variable which is set to the default library
01683 #                       prefix, i.e., subdirectory relative to the library
01684 #                       root directory as used for the @c PREFIX property of
01685 #                       scripted module libraries (see basis_add_script_library())
01686 #                       or relative to the include directory in case of C++.
01687 #                       Note that this prefix includes a trailing slash to
01688 #                       indicate that the prefix is a subdirectory.
01689 # @param [in]  LANGUAGE Programming language (case-insenitive), e.g.,
01690 #                       @c CXX, @c Python, @c Matlab...
01691 macro (basis_library_prefix PREFIX LANGUAGE)
01692   string (TOUPPER "${LANGUAGE}" _LANGUAGE_U)
01693   if (PROJECT_NAMESPACE_${_LANGUAGE_U})
01694     basis_sanitize_for_regex (_RE "${BASIS_NAMESPACE_DELIMITER_${_LANGUAGE_U}}")
01695     string (REGEX REPLACE "${_RE}" "/" ${PREFIX} "${PROJECT_NAMESPACE_${_LANGUAGE_U}}")
01696     set (${PREFIX} "${${PREFIX}}/")
01697     unset (_RE)
01698   else ()
01699     message (FATAL_ERROR "basis_library_prefix(): PROJECT_NAMESPACE_${_LANGUAGE_U} not set!"
01700                          " Make sure that the LANGUAGE argument is supported and in"
01701                          " uppercase letters only.")
01702   endif ()
01703   unset (_LANGUAGE_U)
01704 endmacro ()
01705 
01706 # ----------------------------------------------------------------------------
01707 ## @brief Get file name of compiled script.
01708 #
01709 # @param [out] CFILE  File path of compiled script file.
01710 # @param [in]  SOURCE Script source file.
01711 # @param [in]  ARGV2  Language of script file. If not specified, the language
01712 #                     is derived from the file name extension and shebang of
01713 #                     the script source file.
01714 function (basis_get_compiled_file CFILE SOURCE)
01715   if (ARGC GREATER 2)
01716     set (LANGUAGE "${ARGV2}")
01717   else ()
01718     basis_get_source_language (LANGUAGE "${SOURCE}")
01719   endif ()
01720   set (${CFILE} "" PARENT_SCOPE)
01721   if (SOURCE)
01722     if (LANGUAGE MATCHES "PYTHON")
01723       set (${CFILE} "${SOURCE}c" PARENT_SCOPE)
01724     elseif (LANGUAGE MATCHES "JYTHON")
01725       if (SOURCE MATCHES "(.*)\\.([^.]+)$")
01726         set (${CFILE} "${CMAKE_MATCH_1}$${CMAKE_MATCH_2}.class" PARENT_SCOPE)
01727       endif ()
01728     endif ()
01729   endif ()
01730 endfunction ()
01731 
01732 # ----------------------------------------------------------------------------
01733 ## @brief Get file path of Jython file compiled from the given Python module.
01734 #
01735 # Python modules are also compiled using Jython. This macro returns the file
01736 # path of the compiled Jython file in the build tree which corresponds to the
01737 # specified Python module.
01738 #
01739 # @param [out] CFILE  Path of corresponding compiled Jython file.
01740 # @param [in]  MODULE Path of input Python module in build tree.
01741 macro (basis_get_compiled_jython_file_of_python_module CFILE MODULE)
01742   if (BINARY_PYTHON_LIBRARY_DIR AND BINARY_JYTHON_LIBRARY_DIR)
01743     file (RELATIVE_PATH _GCJFOPM_REL "${BINARY_PYTHON_LIBRARY_DIR}" "${MODULE}")
01744   else ()
01745     set (_GCJFOPM_REL)
01746   endif ()
01747   if (NOT _GCJFOPM_REL MATCHES "^$|^\\.\\./")
01748     basis_get_compiled_file (${CFILE} "${BINARY_JYTHON_LIBRARY_DIR}/${_GCJFOPM_REL}" JYTHON)
01749   else ()
01750     basis_get_compiled_file (${CFILE} "${MODULE}" JYTHON)
01751   endif ()
01752   unset (_GCJFOPM_REL)
01753 endmacro ()
01754 
01755 # ----------------------------------------------------------------------------
01756 ## @brief Whether to compile Python modules for Jython interpreter.
01757 #
01758 # This macro returns a boolean value stating whether Python modules shall also
01759 # be compiled for use by Jython interpreter if BASIS_COMPILE_SCRIPTS is ON.
01760 #
01761 # @param [out] FLAG Set to either TRUE or FALSE depending on whether Python
01762 #                   modules shall be compiled using Jython or not.
01763 macro (basis_compile_python_modules_for_jython FLAG)
01764   if (BASIS_COMPILE_SCRIPTS AND JYTHON_EXECUTABLE)
01765     set (${FLAG} TRUE)
01766   else ()
01767     set (${FLAG} FALSE)
01768   endif ()
01769   if (DEFINED USE_JythonInterp AND NOT USE_JythonInterp)
01770     set (${FLAG} FALSE)
01771   endif ()
01772 endmacro ()
01773 
01774 # ----------------------------------------------------------------------------
01775 ## @brief Glob source files.
01776 #
01777 # This function gets a list of source files and globbing expressions, evaluates
01778 # the globbing expression, and replaces these by the found source files such
01779 # that the resulting list of source files contains only absolute paths of
01780 # source files. It is used by basis_add_executable() and basis_add_library()
01781 # to get a list of all source files. The syntax for the glob expressions
01782 # corresponds to the one used by CMake's
01783 # <a href="http://www.cmake.org/cmake/help/v2.8.8/cmake.html#command:file">
01784 # file(GLOB)</a> command. Additionally, if the pattern <tt>**</tt> is found
01785 # in a glob expression, it is replaced by a single <tt>*</tt> and the
01786 # recursive version, i.e., <tt>file(GLOB_RECURSE)</tt>, is used instead.
01787 #
01788 # @param [in]  TARGET_UID UID of build target which builds the globbed source files.
01789 #                         The custom target which re-globs the source files
01790 #                         before each build of this target is named after this
01791 #                         build target with two leading underscores (__).
01792 # @param [out] SOURCES    List of absolute source paths.
01793 # @param [in]  ARGN       Input file paths and/or globbing expressions.
01794 #
01795 # @sa basis_add_executable()
01796 # @sa basis_add_library()
01797 function (basis_add_glob_target TARGET_UID SOURCES)
01798   # prepare globbing expressions
01799   # make paths absolute and turn directories into recursive globbing expressions
01800   set (EXPRESSIONS)
01801   foreach (EXPRESSION IN LISTS ARGN)
01802     if (NOT IS_ABSOLUTE "${EXPRESSION}")
01803       set (EXPRESSION "${CMAKE_CURRENT_SOURCE_DIR}/${EXPRESSION}")
01804     endif ()
01805     if (IS_DIRECTORY "${EXPRESSION}")
01806       set (EXPRESSION "${EXPRESSION}/**")
01807     endif ()
01808     list (APPEND EXPRESSIONS "${EXPRESSION}")
01809   endforeach ()
01810   # only if at least one globbing expression is given we need to go through this hassle
01811   if (EXPRESSIONS MATCHES "[*?]|\\[[0-9]+-[0-9]+\\]")
01812     set (BUILD_DIR    "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TARGET_UID}.dir")
01813     set (SOURCES_FILE "${BUILD_DIR}/sources.txt")
01814     # get initial list of source files
01815     execute_process (
01816       COMMAND "${CMAKE_COMMAND}"
01817                   "-DEXPRESSIONS:STRING=${EXPRESSIONS}"
01818                   "-DINIT:BOOLEAN=TRUE"
01819                   "-DSOURCES_FILE:FILEPATH=${SOURCES_FILE}"
01820                   -P "${BASIS_MODULE_PATH}/glob.cmake"
01821       RESULT_VARIABLE RETVAL
01822     )
01823     if (NOT RETVAL EQUAL 0 OR NOT EXISTS "${SOURCES_FILE}")
01824       message (FATAL_ERROR "Target ${TARGET_UID}: Failed to glob source files!")
01825     endif ()
01826     # note that including this file here, which is modified whenever a
01827     # source file is added or removed, triggers a re-configuration of the
01828     # build system which is required to re-execute this function.
01829     include ("${SOURCES_FILE}")
01830     set (${SOURCES} "${INITIAL_SOURCES}" PARENT_SCOPE)
01831     # add custom target to re-glob files before each build
01832     set (ERRORMSG "You have either added, removed, or renamed a source file which"
01833                   " matches one of the globbing expressions specified for the"
01834                   " list of source files from which to build the ${TARGET_UID} target."
01835                   " Therefore, the build system must be re-configured. Either try to"
01836                   " build again which should trigger CMake and re-configure the build"
01837                   " system or run CMake manually.")
01838     basis_list_to_string (ERRORMSG ${ERRORMSG})
01839     add_custom_target (
01840       __${TARGET_UID}
01841       COMMAND "${CMAKE_COMMAND}"
01842                   "-DEXPRESSIONS:STRING=${EXPRESSIONS}"
01843                   "-DINIT:BOOLEAN=FALSE"
01844                   "-DSOURCES_FILE:FILEPATH=${SOURCES_FILE}"
01845                   "-DERRORMSG:STRING=${ERRORMSG}"
01846                   -P "${BASIS_MODULE_PATH}/glob.cmake"
01847       COMMENT "Checking if source files for target ${TARGET_UID} were added or removed"
01848       VERBATIM
01849     )
01850   # otherwise, just return the given absolute source file paths
01851   else ()
01852     set (${SOURCES} "${EXPRESSIONS}" PARENT_SCOPE)
01853   endif ()
01854 endfunction ()
01855 
01856 # ----------------------------------------------------------------------------
01857 ## @brief Detect programming language of given source code files.
01858 #
01859 # This function determines the programming language in which the given source
01860 # code files are written. If no common programming language could be determined,
01861 # "AMBIGUOUS" is returned. If none of the following programming languages
01862 # could be determined, "UNKNOWN" is returned: CXX (i.e., C++), JAVA, MATLAB,
01863 # PYTHON, JYTHON, PERL, BASH, BATCH.
01864 #
01865 # @param [out] LANGUAGE Detected programming language.
01866 # @param [in]  ARGN     List of source code files.
01867 function (basis_get_source_language LANGUAGE)
01868   set (LANGUAGE_OUT)
01869   # iterate over source files
01870   foreach (SOURCE_FILE ${ARGN})
01871     get_filename_component (SOURCE_FILE "${SOURCE_FILE}" ABSOLUTE)
01872 
01873     if (IS_DIRECTORY "${SOURCE_FILE}")
01874 
01875       file (GLOB_RECURSE SOURCE_FILES "${SOURCE_FILE}/*")
01876       list (APPEND ARGN ${SOURCE_FILES})
01877 
01878     else ()
01879 
01880       # ------------------------------------------------------------------------
01881       # determine language based on extension for those without shebang
01882       set (LANG)
01883       # C++
01884       if (SOURCE_FILE MATCHES "\\.(c|cc|cpp|cxx|h|hpp|hxx|txx|inl)(\\.in)?$")
01885         set (LANG "CXX")
01886       # Java
01887       elseif (SOURCE_FILE MATCHES "\\.java(\\.in)?$")
01888         set (LANG "JAVA")
01889       # MATLAB
01890       elseif (SOURCE_FILE MATCHES "\\.m(\\.in)?$")
01891         set (LANG "MATLAB")
01892       endif ()
01893 
01894       # ------------------------------------------------------------------------
01895       # determine language from shebang directive
01896       #
01897       # Note that some scripting languages may use identical file name extensions.
01898       # This is in particular the case for Python and Jython. The only way we
01899       # can distinguish these two is by looking at the shebang directive.
01900       if (NOT LANG)
01901         
01902         if (NOT EXISTS "${SOURCE_FILE}" AND EXISTS "${SOURCE_FILE}.in")
01903           set (SOURCE_FILE "${SOURCE_FILE}.in")
01904         endif ()
01905         if (EXISTS "${SOURCE_FILE}")
01906           file (STRINGS "${SOURCE_FILE}" FIRST_LINE LIMIT_COUNT 1)
01907           if (FIRST_LINE MATCHES "^#!")
01908             if (FIRST_LINE MATCHES "^#! */usr/bin/env +([^ ]+)")
01909               set (INTERPRETER "${CMAKE_MATCH_1}")
01910             elseif (FIRST_LINE MATCHES "^#! *([^ ]+)")
01911               set (INTERPRETER "${CMAKE_MATCH_1}")
01912               get_filename_component (INTERPRETER "${INTERPRETER}" NAME)
01913             else ()
01914               set (INTERPRETER)
01915             endif ()
01916             if (INTERPRETER MATCHES "^(python|jython|perl|bash)$")
01917               string (TOUPPER "${INTERPRETER}" LANG)
01918             endif ()
01919           endif ()
01920         endif ()
01921       endif ()
01922 
01923       # ------------------------------------------------------------------------
01924       # determine language from further known extensions
01925       if (NOT LANG)
01926         # Python
01927         if (SOURCE_FILE MATCHES "\\.py(\\.in)?$")
01928           set (LANG "PYTHON")
01929         # Perl
01930         elseif (SOURCE_FILE MATCHES "\\.(pl|pm|t)(\\.in)?$")
01931           set (LANG "PERL")
01932         # BASH
01933         elseif (SOURCE_FILE MATCHES "\\.sh(\\.in)?$")
01934           set (LANG "BASH")
01935         # Batch
01936         elseif (SOURCE_FILE MATCHES "\\.bat(\\.in)?$")
01937           set (LANG "BATCH")
01938         # unknown
01939         else ()
01940           set (LANGUAGE_OUT "UNKNOWN")
01941           break ()
01942         endif ()
01943       endif ()
01944 
01945       # ------------------------------------------------------------------------
01946       # detect ambiguity
01947       if (LANGUAGE_OUT AND NOT LANG MATCHES "^${LANGUAGE_OUT}$")
01948         if (LANGUAGE_OUT MATCHES "CXX" AND LANG MATCHES "MATLAB")
01949           # MATLAB Compiler can handle this...
01950         elseif (LANGUAGE_OUT MATCHES "MATLAB" AND LANG MATCHES "CXX")
01951           set (LANG "MATLAB") # language stays MATLAB
01952         elseif (LANGUAGE_OUT MATCHES "PYTHON" AND LANG MATCHES "JYTHON")
01953           # Jython can deal with Python scripts/modules
01954         elseif (LANGUAGE_OUT MATCHES "JYTHON" AND LANG MATCHES "PYTHON")
01955           set (LANG "JYTHON") # language stays JYTHON
01956         else ()
01957           # ambiguity
01958           set (LANGUAGE_OUT "AMBIGUOUS")
01959           break ()
01960         endif ()
01961       endif ()
01962 
01963       # update current language
01964       set (LANGUAGE_OUT "${LANG}")
01965     endif ()
01966   endforeach ()
01967   # return
01968   set (${LANGUAGE} "${LANGUAGE_OUT}" PARENT_SCOPE)
01969 endfunction ()
01970 
01971 # ----------------------------------------------------------------------------
01972 ## @brief Configure .in source files.
01973 #
01974 # This function configures each source file in the given argument list with
01975 # a .in file name suffix and stores the configured file in the build tree
01976 # with the same relative directory as the template source file itself.
01977 # The first argument names the CMake variable of the list of configured
01978 # source files where each list item is the absolute file path of the
01979 # corresponding (configured) source file.
01980 #
01981 # @param [out] LIST_NAME Name of output list.
01982 # @param [in]  ARGN      These arguments are parsed and the following
01983 #                        options recognized. All remaining arguments are
01984 #                        considered to be source file paths.
01985 # @par
01986 # <table border="0">
01987 #   <tr>
01988 #     @tp @b BINARY_DIRECTORY @endtp
01989 #     <td>Explicitly specify directory in build tree where configured
01990 #         source files should be written to.</td>
01991 #   </tr>
01992 #   <tr>
01993 #     @tp @b KEEP_DOT_IN_SUFFIX @endtp
01994 #     <td>By default, after a source file with the .in extension has been
01995 #         configured, the .in suffix is removed from the file name.
01996 #         This can be omitted by giving this option.</td>
01997 #   </tr>
01998 # </table>
01999 #
02000 # @returns Nothing.
02001 function (basis_configure_sources LIST_NAME)
02002   # parse arguments
02003   CMAKE_PARSE_ARGUMENTS (ARGN "KEEP_DOT_IN_SUFFIX" "BINARY_DIRECTORY" "" ${ARGN})
02004 
02005   if (ARGN_BINARY_DIRECTORY AND NOT ARGN_BINARY_DIRECTORY MATCHES "^${PROJECT_BINARY_DIR}")
02006     message (FATAL_ERROR "Specified BINARY_DIRECTORY must be inside the build tree!")
02007   endif ()
02008 
02009   # configure source files
02010   set (CONFIGURED_SOURCES)
02011   foreach (SOURCE ${ARGN_UNPARSED_ARGUMENTS})
02012     # The .in suffix is optional, add it here if a .in file exists for this
02013     # source file, but only if the source file itself does not name an actually
02014     # existing source file.
02015     #
02016     # If the source file path is relative, prefer possibly already configured
02017     # sources in build tree such as the test driver source file created by
02018     # create_test_sourcelist() or a manual use of configure_file().
02019     #
02020     # Note: Make path absolute, otherwise EXISTS check will not work!
02021     if (NOT IS_ABSOLUTE "${SOURCE}")
02022       if (EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}")
02023         set (SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}")
02024       elseif (EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}.in")
02025         set (SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}.in")
02026       elseif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}")
02027         set (SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}")
02028       elseif (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.in")
02029         set (SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}.in")
02030       endif ()
02031     else ()
02032       if (NOT EXISTS "${SOURCE}" AND EXISTS "${SOURCE}.in")
02033         set (SOURCE "${SOURCE}.in")
02034       endif ()
02035     endif ()
02036     # configure source file if filename ends in .in suffix
02037     if (SOURCE MATCHES "\\.in$")
02038       # if binary directory was given explicitly, use it
02039       if (ARGN_BINARY_DIRECTORY)
02040         get_filename_component (SOURCE_NAME "${SOURCE}" NAME)
02041         if (NOT ARGN_KEEP_DOT_IN_SUFFIX)
02042           string (REGEX REPLACE "\\.in$" "" SOURCE_NAME "${SOURCE_NAME}")
02043         endif ()
02044         set (CONFIGURED_SOURCE "${ARGN_BINARY_DIRECTORY}/${SOURCE_NAME}")
02045       # otherwise,
02046       else ()
02047         # if source is in project's source tree use relative binary directory
02048         basis_sanitize_for_regex (REGEX "${PROJECT_SOURCE_DIR}")
02049         if (SOURCE MATCHES "^${REGEX}")
02050           basis_get_relative_path (CONFIGURED_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}" "${SOURCE}")
02051           get_filename_component (CONFIGURED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${CONFIGURED_SOURCE}" ABSOLUTE)
02052           if (NOT ARGN_KEEP_DOT_IN_SUFFIX)
02053             string (REGEX REPLACE "\\.in$" "" CONFIGURED_SOURCE "${CONFIGURED_SOURCE}")
02054           endif ()
02055         # otherwise, use current binary directory
02056         else ()
02057           get_filename_component (SOURCE_NAME "${SOURCE}" NAME)
02058           if (NOT ARGN_KEEP_DOT_IN_SUFFIX)
02059             string (REGEX REPLACE "\\.in$" "" SOURCE_NAME "${SOURCE_NAME}")
02060           endif ()
02061           set (CONFIGURED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE_NAME}")
02062         endif ()
02063       endif ()
02064       # configure source file
02065       configure_file ("${SOURCE}" "${CONFIGURED_SOURCE}" @ONLY)
02066       if (BASIS_DEBUG)
02067         message ("** Configured source file with .in extension")
02068       endif ()
02069     # otherwise, skip configuration of this source file
02070     else ()
02071       set (CONFIGURED_SOURCE "${SOURCE}")
02072       if (BASIS_DEBUG)
02073         message ("** Skipped configuration of source file")
02074       endif ()
02075     endif ()
02076     if (BASIS_DEBUG)
02077       message ("**     Source:            ${SOURCE}")
02078       message ("**     Configured source: ${CONFIGURED_SOURCE}")
02079     endif ()
02080     list (APPEND CONFIGURED_SOURCES "${CONFIGURED_SOURCE}")
02081   endforeach ()
02082   # return
02083   set (${LIST_NAME} "${CONFIGURED_SOURCES}" PARENT_SCOPE)
02084 endfunction ()
02085 
02086 # ----------------------------------------------------------------------------
02087 ## @brief Configure and optionally compile script file.
02088 #
02089 # This function is used to configure script files during the build. It is
02090 # called by the build script generated by basis_add_script_target() for each script
02091 # target. It is further used to configure the modules of the packages
02092 # implemented in supported scripting languages which are located in the
02093 # @c PROJECT_LIBRARY_DIR of the source tree.
02094 #
02095 # In case of executable scripts, this function automatically prepends the
02096 # module search paths such that the modules of this software package are found
02097 # (and preferred in case of potential name conflicts with other packages).
02098 # Moreover, it adds (or replaces) the shebang directive on Unix such that the
02099 # configured interpreter version is used. On Windows, it converts the executable
02100 # script into a Windows Command instead which executes the proper interpreter
02101 # with the code section of the input script.
02102 #
02103 # @param [in] INPUT  Input script file.
02104 # @param [in] OUTPUT Configured output script file.
02105 # @param [in] ARGN   Optional arguments:
02106 # @par
02107 # <table border=0>
02108 #   <tr>
02109 #     @tp @b COMPILE @endtp
02110 #     <td>Whether to compile module scripts if suitable, i.e., an intermediate
02111 #         format exists for the specific scripting language. For example,
02112 #         Python modules can be compiled.</td>
02113 #   </tr>
02114 #   <tr>
02115 #     @tp @b COPYONLY @endtp
02116 #     <td>Whether to only copy the script file without replacing CMake variables
02117 #         within the file. This option is passed on to CMake's configure_file()
02118 #         command used to configure the script file. By default, the option
02119 #         \@ONLY is used instead.</td>
02120 #   </tr>
02121 #   <tr>
02122 #     @tp @b EXECUTABLE @endtp
02123 #     <td>Specifies that the given script file is an executable script and not a
02124 #         module script. Otherwise, if this option is not given and the output
02125 #         file name contains a file name extension, the given script file is
02126 #         configured as module script. A script file with an output file name
02127 #         that has no extension, is always considered to be an executable.</td>
02128 #   </tr>
02129 #   <tr>
02130 #     @tp @b DESTINATION dir @endtp
02131 #     <td>Installation directory for configured script. If this option is given,
02132 #         the @c BUILD_INSTALL_SCRIPT variable is set to @c TRUE before including
02133 #         any specified script configuration files (see @p CONFIG_FILE option).
02134 #         Moreover, the @c __DIR__ variable is set to the specified directory.
02135 #         Otherwise, if this option is omitted, the @c BUILD_INSTALL_SCRIPT variable
02136 #         is set to @c FALSE instead and @c __DIR__ is set to the directory of
02137 #         the configured @p OUTPUT file. Note that the @c BUILD_INSTALL_SCRIPT and
02138 #         @c __DIR__ variables are in particular used by basis_set_script_path()
02139 #         to convert the given paths to paths relative to the location of the
02140 #         configured/installed script.</td>
02141 #   </tr>
02142 #   <tr>
02143 #     @tp @b CACHE_FILE file1 [file2...] @endtp
02144 #     <td>List of CMake files with dump of variables which should be included
02145 #         before configuring the script. The cache files can be generated using
02146 #         the basis_dump_variables() function.</td>
02147 #   </tr>
02148 #   <tr>
02149 #     @tp @b CONFIG_FILE file1 [file2...] @endtp
02150 #     <td>List of script configuration files to include before the configuration
02151 #         of the script. See also the documentation of the @p DESTINATION option.</td>
02152 #   </tr>
02153 #   <tr>
02154 #     @tp @b LINK_DEPENDS dep1 [dep2...] @endtp
02155 #     <td>List of "link" dependencies, i.e., modules and script/module libraries
02156 #         required by this script. For executable scripts, the paths to these
02157 #         modules/packages is added to the module search path. If the prefix
02158 #         "relative " is given before a file path, it is made relative to the
02159 #         output/installation directory of the script file. All given input paths
02160 #         must be absolute, however, as the relative location depends on
02161 #         whether the script will be installed, i.e., the @c DESTINATION
02162 #         is specified, or not.</td>
02163 #   </tr>
02164 # </table>
02165 function (basis_configure_script INPUT OUTPUT)
02166   # rename arguments to avoid conflict with script configuration
02167   set (_INPUT_FILE  "${INPUT}")
02168   set (_OUTPUT_FILE "${OUTPUT}")
02169   # --------------------------------------------------------------------------
02170   # parse arguments
02171   CMAKE_PARSE_ARGUMENTS (
02172     ARGN
02173       "COMPILE;COPYONLY;EXECUTABLE"
02174       "DESTINATION;LANGUAGE"
02175       "CACHE_FILE;CONFIG_FILE;LINK_DEPENDS"
02176     ${ARGN}
02177   )
02178   if (ARGN_UNPARSED_ARGUMENTS)
02179     message (FATAL_ERROR "Unrecognized arguments given: ${ARGN_UNPARSED_ARGUMENTS}")
02180   endif ()
02181   if (NOT ARGN_LANGUAGE)
02182     basis_get_source_language (ARGN_LANGUAGE "${_INPUT_FILE}")
02183   endif ()
02184   # --------------------------------------------------------------------------
02185   # include cache files
02186   foreach (_F IN LISTS ARGN_CACHE_FILE)
02187     get_filename_component (_F "${_F}" ABSOLUTE)
02188     if (NOT EXISTS "${_F}")
02189       message (FATAL_ERROR "Cache file ${_F} does not exist!")
02190     endif ()
02191     include ("${_F}")
02192   endforeach ()
02193   # --------------------------------------------------------------------------
02194   # set general variables for use in scripts
02195   set (__FILE__ "${_OUTPUT_FILE}")
02196   get_filename_component (__NAME__ "${_OUTPUT_FILE}" NAME)
02197   # --------------------------------------------------------------------------
02198   # variables mainly intended for use in script configurations, in particular,
02199   # these are used by basis_set_script_path() to make absolute paths relative
02200   if (ARGN_DESTINATION)
02201     if (NOT IS_ABSOLUTE "${ARGN_DESTINATION}")
02202       set (ARGN_DESTINATION "${CMAKE_INSTALL_PREFIX}/${ARGN_DESTINATION}")
02203     endif ()
02204     set (BUILD_INSTALL_SCRIPT TRUE)
02205     set (__DIR__ "${ARGN_DESTINATION}")
02206   else ()
02207     set (BUILD_INSTALL_SCRIPT FALSE)
02208     get_filename_component (__DIR__ "${_OUTPUT_FILE}" PATH)
02209   endif ()
02210   # --------------------------------------------------------------------------
02211   # include script configuration files
02212   foreach (_F IN LISTS ARGN_CONFIG_FILE)
02213     get_filename_component (_F "${_F}" ABSOLUTE)
02214     if (NOT EXISTS "${_F}")
02215       message (FATAL_ERROR "Script configuration file ${_F} does not exist!")
02216     endif ()
02217     include ("${_F}")
02218   endforeach ()
02219   # --------------------------------------------------------------------------
02220   # configure executable script
02221   if (ARGN_EXECUTABLE)
02222     # Attention: Every line of code added/removed will introduce a mismatch
02223     #            between error messages of the interpreter and the original
02224     #            source file. To not confuse/mislead developers too much,
02225     #            keep number of lines added/removed at a minimum or at least
02226     #            try to balance the number of lines added and removed.
02227     #            Moreover, blank lines can be used to insert code without
02228     #            changing the number of source code lines.
02229     file (READ "${_INPUT_FILE}" SCRIPT)
02230     # (temporarily) remove existing shebang directive
02231     file (STRINGS "${_INPUT_FILE}" FIRST_LINE LIMIT_COUNT 1)
02232     if (FIRST_LINE MATCHES "^#!")
02233       basis_sanitize_for_regex (FIRST_LINE_RE "${FIRST_LINE}")
02234       string (REGEX REPLACE "^${FIRST_LINE_RE}\n?" "" SCRIPT "${SCRIPT}")
02235       set (SHEBANG "${FIRST_LINE}")
02236     endif ()
02237     # replace CMake variables used in script
02238     if (NOT ARGN_COPYONLY)
02239       string (CONFIGURE "${SCRIPT}" SCRIPT @ONLY)
02240     endif ()
02241     # add code to set module search path
02242     if (ARGN_LANGUAGE MATCHES "[JP]YTHON")
02243       if (ARGN_LINK_DEPENDS)
02244         string (REGEX REPLACE "^[ \t]*\n" "" SCRIPT "${SCRIPT}") # remove a blank line therefore
02245         set (PYTHON_CODE "import sys; import os.path; __dir__ = os.path.dirname(os.path.realpath(__file__))")
02246         list (REVERSE ARGN_LINK_DEPENDS)
02247         foreach (DIR ${ARGN_LINK_DEPENDS})
02248           if (DIR MATCHES "^relative +(.*)$")
02249             basis_get_relative_path (DIR "${__DIR__}" "${CMAKE_MATCH_1}")
02250           endif ()
02251           if (DIR MATCHES "\\.(py|class)$")
02252             get_filename_component (DIR "${DIR}" PATH)
02253           endif ()
02254           if (IS_ABSOLUTE "${DIR}")
02255             set (PYTHON_CODE "${PYTHON_CODE}; sys.path.insert(0, os.path.realpath('${DIR}'))")
02256           else ()
02257             set (PYTHON_CODE "${PYTHON_CODE}; sys.path.insert(0, os.path.realpath(os.path.join(__dir__, '${DIR}')))")
02258           endif ()
02259         endforeach ()
02260         set (SCRIPT "${PYTHON_CODE} # <-- added by BASIS\n${SCRIPT}")
02261       endif ()
02262     elseif (ARGN_LANGUAGE MATCHES "PERL")
02263       if (ARGN_LINK_DEPENDS)
02264         string (REGEX REPLACE "^[ \t]*\n" "" SCRIPT "${SCRIPT}") # remove a blank line therefore
02265         set (PERL_CODE "use Cwd qw(realpath); use File::Basename;")
02266         foreach (DIR ${ARGN_LINK_DEPENDS})
02267           if (DIR MATCHES "^relative +(.*)$")
02268             basis_get_relative_path (DIR "${__DIR__}" "${CMAKE_MATCH_1}")
02269           endif ()
02270           if (DIR MATCHES "\\.pm$")
02271             get_filename_component (DIR "${DIR}" PATH)
02272           endif ()
02273           if (IS_ABSOLUTE "${DIR}")
02274             set (PERL_CODE "${PERL_CODE} use lib '${DIR}';")
02275           else ()
02276             set (PERL_CODE "${PERL_CODE} use lib dirname(realpath(__FILE__)) . '/${DIR}';")
02277           endif ()
02278         endforeach ()
02279         set (SCRIPT "${PERL_CODE} # <-- added by BASIS\n${SCRIPT}")
02280       endif ()
02281     elseif (ARGN_LANGUAGE MATCHES "BASH")
02282       basis_library_prefix (PREFIX BASH)
02283       # In case of Bash, set BASIS_BASH_UTILITIES which is required to first source the
02284       # BASIS utilities modules (in particular core.sh). This variable should be set to
02285       # the utilities.sh module of BASIS by default as part of the BASIS installation
02286       # (environment variable) and is here set to the project-specific basis.sh module.
02287       #
02288       # Note that folks at SBIA may submit a Bash script directly to a batch queuing
02289       # system such as the Oracle Grid Engine (SGE) instead of writing a separate submit
02290       # script. To avoid not finding the BASIS utilities in this case only because the
02291       # Bash file was copied by SGE to a temporary file, consider the <PROJECT>_DIR
02292       # environment variable as an alternative.
02293       string (REGEX REPLACE "^[ \t]*\n" "" SCRIPT "${SCRIPT}") # remove a blank line therefore
02294       set (BASH_CODE
02295 # Note: Code formatted such that it can be on single line. Use no comments within!
02296 "__FILE__=\"$(cd -P -- \"$(dirname -- \"$BASH_SOURCE\")\" && pwd -P)/$(basename -- \"$BASH_SOURCE\")\"
02297 if [[ -n \"$SGE_ROOT\" ]] && [[ $__FILE__ =~ $SGE_ROOT/.* ]] && [[ -n \"\${${PROJECT_NAME}_DIR}\" ]] && [[ -f \"\${${PROJECT_NAME}_DIR}/bin/${__NAME__}\" ]]
02298 then __FILE__=\"\${${PROJECT_NAME}_DIR}/bin/${__NAME__}\"
02299 fi
02300 i=0
02301 lnk=\"$__FILE__\"
02302 while [[ -h \"$lnk\" ]] && [[ $i -lt 100 ]]
02303 do dir=`dirname -- \"$lnk\"`
02304 lnk=`readlink -- \"$lnk\"`
02305 lnk=`cd \"$dir\" && cd $(dirname -- \"$lnk\") && pwd`/`basename -- \"$lnk\"`
02306 let i++
02307 done
02308 [[ $i -lt 100 ]] && __FILE__=\"$lnk\"
02309 unset -v i dir lnk
02310 __DIR__=\"$(dirname -- \"$__FILE__\")\"
02311 BASIS_BASH_UTILITIES=\"$__DIR__/${BASH_LIBRARY_DIR}/${PREFIX}basis.sh\""
02312       )
02313       string (REPLACE "\n" "; " BASH_CODE "${BASH_CODE}")
02314       # set BASHPATH which is used by import() function provided by core.sh module of BASIS
02315       set (BASHPATH)
02316       foreach (DIR ${ARGN_LINK_DEPENDS})
02317         if (DIR MATCHES "^relative +(.*)$")
02318           basis_get_relative_path (DIR "${__DIR__}" "${CMAKE_MATCH_1}")
02319         endif ()
02320         if (DIR MATCHES "\\.sh$")
02321           get_filename_component (DIR "${DIR}" PATH)
02322         endif ()
02323         if (IS_ABSOLUTE "${DIR}")
02324           list (APPEND BASHPATH "${DIR}")
02325         else ()
02326           list (APPEND BASHPATH "$__DIR__/${DIR}")
02327         endif ()
02328       endforeach ()
02329       if (BASHPATH)
02330         list (REMOVE_DUPLICATES BASHPATH)
02331         list (APPEND BASHPATH "$BASHPATH")
02332         basis_list_to_delimited_string (BASHPATH ":" NOAUTOQUOTE ${BASHPATH})
02333         set (BASH_CODE "${BASH_CODE}; BASHPATH=\"${BASHPATH}\"")
02334       endif ()
02335       set (SCRIPT "${BASH_CODE} # <-- added by BASIS\n${SCRIPT}")
02336     endif ()
02337     # replace shebang directive
02338     if (ARGN_LANGUAGE MATCHES "PYTHON" AND PYTHON_EXECUTABLE)
02339       if (WIN32)
02340         set (SHEBANG "@setlocal enableextensions & \"${PYTHON_EXECUTABLE}\" -x \"%~f0\" %* & goto :EOF")
02341       else ()
02342         set (SHEBANG "#! ${PYTHON_EXECUTABLE}")
02343       endif ()
02344     elseif (ARGN_LANGUAGE MATCHES "JYTHON" AND JYTHON_EXECUTABLE)
02345       if (WIN32)
02346         set (SHEBANG "@setlocal enableextensions & \"${JYTHON_EXECUTABLE}\" -x \"%~f0\" %* & goto :EOF")
02347       else ()
02348         # Attention: It is IMPORTANT to not use "#! <interpreter>" even if the <interpreter>
02349         #            is given as full path in case of jython. Otherwise, the Jython executable
02350         #            fails to execute from within a Python script using the os.system(),
02351         #            subprocess.popen(), subprocess.call() or similar function!
02352         #            Don't ask me for an explanation, but possibly the used shell otherwise does
02353         #            not recognize the shebang as being valid. Using /usr/bin/env helps out here,
02354         #            -schuha
02355         set (SHEBANG "#! /usr/bin/env ${JYTHON_EXECUTABLE}")
02356       endif ()
02357     elseif (ARGN_LANGUAGE MATCHES "PERL" AND PERL_EXECUTABLE)
02358       if (WIN32)
02359         set (SHEBANG "@goto = \"START_OF_BATCH\" ;\n@goto = ();")
02360         set (SCRIPT "${SCRIPT}\n\n__END__\n\n:\"START_OF_BATCH\"\n@\"${PERL_EXECUTABLE}\" -w -S \"%~f0\" %*")
02361       else ()
02362         set (SHEBANG "#! ${PERL_EXECUTABLE} -w")
02363       endif ()
02364     elseif (ARGN_LANGUAGE MATCHES "BASH" AND BASH_EXECUTABLE)
02365       set (SHEBANG "#! ${BASH_EXECUTABLE}")
02366     endif ()
02367     # add (modified) shebang directive again
02368     if (SHEBANG)
02369       set (SCRIPT "${SHEBANG}\n${SCRIPT}")
02370     endif ()
02371     # write configured script
02372     file (WRITE "${_OUTPUT_FILE}" "${SCRIPT}")
02373     # make script executable on Unix
02374     if (UNIX AND NOT ARGN_DESTINATION)
02375       execute_process (COMMAND /bin/chmod +x "${_OUTPUT_FILE}")
02376     endif ()
02377   # --------------------------------------------------------------------------
02378   # configure module script
02379   else ()
02380     # configure module - do not use configure_file() as it will not update the
02381     #                    file if nothing has changed. the update of the modification
02382     #                    time is however in particular required for the
02383     #                    configure_script.cmake build command which uses this
02384     #                    function to build script targets. otherwise, the custom
02385     #                    build command is reexecuted only because the output files
02386     #                    never appear to be more recent than the dependencies
02387     file (READ "${_INPUT_FILE}" SCRIPT)
02388     if (NOT ARGN_COPYONLY)
02389       string (CONFIGURE "${SCRIPT}" SCRIPT @ONLY)
02390     endif ()
02391     file (WRITE "${_OUTPUT_FILE}" "${SCRIPT}")
02392     # compile module if requested
02393     if (ARGN_COMPILE)
02394       if (ARGN_LANGUAGE MATCHES "PYTHON" AND PYTHON_EXECUTABLE)
02395         basis_get_compiled_file (CFILE "${_OUTPUT_FILE}" PYTHON)
02396         execute_process (COMMAND "${PYTHON_EXECUTABLE}" -E -c "import py_compile; py_compile.compile('${_OUTPUT_FILE}', '${CFILE}')")
02397         basis_compile_python_modules_for_jython (RV)
02398         if (RV)
02399           basis_get_compiled_jython_file_of_python_module (CFILE "${_OUTPUT_FILE}")
02400           get_filename_component (CDIR "${CFILE}" PATH)
02401           file (MAKE_DIRECTORY "${CDIR}")
02402           execute_process (COMMAND "${JYTHON_EXECUTABLE}" -c "import py_compile; py_compile.compile('${_OUTPUT_FILE}', '${CFILE}')")
02403         endif ()
02404       elseif (ARGN_LANGUAGE MATCHES "JYTHON" AND JYTHON_EXECUTABLE)
02405         basis_get_compiled_file (CFILE "${_OUTPUT_FILE}" JYTHON)
02406         execute_process (COMMAND "${JYTHON_EXECUTABLE}" -c "import py_compile; py_compile.compile('${_OUTPUT_FILE}', '${CFILE}')")
02407       endif ()
02408     endif ()
02409   endif ()
02410 endfunction ()
02411 
02412 # ----------------------------------------------------------------------------
02413 ## @brief Get type name of target.
02414 #
02415 # @param [out] TYPE        The target's type name or NOTFOUND.
02416 # @param [in]  TARGET_NAME The name of the target.
02417 function (basis_get_target_type TYPE TARGET_NAME)
02418   basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
02419   if (TARGET ${TARGET_UID})
02420     get_target_property (TYPE_OUT ${TARGET_UID} "BASIS_TYPE")
02421     if (NOT TYPE_OUT)
02422       # in particular imported targets may not have a BASIS_TYPE property
02423       get_target_property (TYPE_OUT ${TARGET_UID} "TYPE")
02424     endif ()
02425   else ()
02426     set (TYPE_OUT "NOTFOUND")
02427   endif ()
02428   set ("${TYPE}" "${TYPE_OUT}" PARENT_SCOPE)
02429 endfunction ()
02430 
02431 # ----------------------------------------------------------------------------
02432 ## @brief Get location of build target output file(s).
02433 #
02434 # This convenience function can be used to get the full path of the output
02435 # file(s) generated by a given build target. It is similar to the read-only
02436 # @c LOCATION property of CMake targets and should be used instead of
02437 # reading this porperty. In case of scripted libraries, this function returns
02438 # the path of the root directory of the library that has to be added to the
02439 # module search path.
02440 #
02441 # @param [out] VAR         Path of build target output file.
02442 # @param [in]  TARGET_NAME Name of build target.
02443 # @param [in]  PART        Which file name component of the @c LOCATION
02444 #                          property to return. See get_filename_component().
02445 #                          If POST_INSTALL_RELATIVE is given as argument,
02446 #                          @p VAR is set to the path of the installed file
02447 #                          relative to the installation prefix. Similarly,
02448 #                          POST_INSTALL sets @p VAR to the absolute path
02449 #                          of the installed file post installation.
02450 #
02451 # @returns Path of output file similar to @c LOCATION property of CMake targets.
02452 #
02453 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#prop_tgt:LOCATION
02454 function (basis_get_target_location VAR TARGET_NAME PART)
02455   basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
02456   if (TARGET "${TARGET_UID}")
02457     basis_get_target_name (TARGET_NAME "${TARGET_UID}")
02458     basis_get_target_type (TYPE        "${TARGET_UID}")
02459     get_target_property (IMPORTED ${TARGET_UID} IMPORTED)
02460     # ------------------------------------------------------------------------
02461     # imported custom targets
02462     #
02463     # Note: This might not be required though as even custom executable
02464     #       and library targets can be imported using CMake's
02465     #       add_executable(<NAME> IMPORTED) and add_library(<NAME> <TYPE> IMPORTED)
02466     #       commands. Such executable can, for example, also be a BASH
02467     #       script built by basis_add_script().
02468     if (IMPORTED)
02469       # 1. Try IMPORTED_LOCATION_<CMAKE_BUILD_TYPE>
02470       if (CMAKE_BUILD_TYPE)
02471         string (TOUPPER "${CMAKE_BUILD_TYPE}" U)
02472       else ()
02473         set (U "NOCONFIG")
02474       endif ()
02475       get_target_property (LOCATION ${TARGET_UID} IMPORTED_LOCATION_${U})
02476       # 2. Try IMPORTED_LOCATION
02477       if (NOT LOCATION)
02478         get_target_property (LOCATION ${TARGET_UID} IMPORTED_LOCATION)
02479       endif ()
02480       # 3. Prefer Release over all other configurations
02481       if (NOT LOCATION)
02482         get_target_property (LOCATION ${TARGET_UID} IMPORTED_LOCATION_RELEASE)
02483       endif ()
02484       # 4. Just use any of the imported configurations
02485       if (NOT LOCATION)
02486         get_property (CONFIGS TARGET ${TARGET_UID} PROPERTY IMPORTED_CONFIGURATIONS)
02487         foreach (C IN LISTS CONFIGS)
02488           string (TOUPPER "${C}" C)
02489           get_target_property (LOCATION ${TARGET_UID} IMPORTED_LOCATION_${C})
02490           if (LOCATION)
02491             break ()
02492           endif ()
02493         endforeach ()
02494       endif ()
02495       # make path relative to CMAKE_INSTALL_PREFIX if POST_CMAKE_INSTALL_PREFIX given
02496       if (LOCATION AND ARGV2 MATCHES "POST_INSTALL_RELATIVE")
02497         file (RELATIVE_PATH LOCATION "${CMAKE_INSTALL_PREFIX}" "${LOCATION}")
02498       endif ()
02499     # ------------------------------------------------------------------------
02500     # non-imported custom targets
02501     else ()
02502       # Attention: The order of the matches/if cases is matters here!
02503       # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
02504       # scripts
02505       if (TYPE MATCHES "^SCRIPT_(EXECUTABLE|MODULE)$")
02506         if (PART MATCHES "POST_INSTALL")
02507           get_target_property (DIRECTORY ${TARGET_UID} INSTALL_DIRECTORY)
02508         else ()
02509           get_target_property (DIRECTORY ${TARGET_UID} OUTPUT_DIRECTORY)
02510         endif ()
02511         get_target_property (FNAME ${TARGET_UID} OUTPUT_NAME)
02512       # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
02513       # libraries
02514       elseif (TYPE MATCHES "LIBRARY|MODULE|MEX")
02515         if (TYPE MATCHES "STATIC")
02516           if (PART MATCHES "POST_INSTALL")
02517             get_target_property (DIRECTORY ${TARGET_UID} ARCHIVE_INSTALL_DIRECTORY)
02518           else ()
02519             get_target_property (DIRECTORY ${TARGET_UID} ARCHIVE_OUTPUT_DIRECTORY)
02520           endif ()
02521           get_target_property (FNAME ${TARGET_UID} ARCHIVE_OUTPUT_NAME)
02522         else ()
02523           if (PART MATCHES "POST_INSTALL")
02524             get_target_property (DIRECTORY ${TARGET_UID} LIBRARY_INSTALL_DIRECTORY)
02525           else ()
02526             get_target_property (DIRECTORY ${TARGET_UID} LIBRARY_OUTPUT_DIRECTORY)
02527           endif ()
02528           get_target_property (FNAME ${TARGET_UID} LIBRARY_OUTPUT_NAME)
02529         endif ()
02530       # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
02531       # executables
02532       else ()
02533         if (PART MATCHES "POST_INSTALL")
02534           get_target_property (DIRECTORY ${TARGET_UID} RUNTIME_INSTALL_DIRECTORY)
02535         else ()
02536           get_target_property (DIRECTORY ${TARGET_UID} RUNTIME_OUTPUT_DIRECTORY)
02537         endif ()
02538         get_target_property (FNAME ${TARGET_UID} RUNTIME_OUTPUT_NAME)
02539       endif ()
02540       if (DIRECTORY MATCHES "NOTFOUND")
02541         message (FATAL_ERROR "Failed to get directory of ${TYPE} ${TARGET_UID}!"
02542                              " Check implementation of basis_get_target_location()"
02543                              " and make sure that the required *INSTALL_DIRECTORY"
02544                              " property is set on the target!")
02545       endif ()
02546       if (DIRECTORY)
02547         # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
02548         # get output name of built file (if applicable)
02549         if (NOT FNAME)
02550           get_target_property (FNAME ${TARGET_UID} OUTPUT_NAME)
02551         endif ()
02552         if (NOT TYPE MATCHES "^SCRIPT_LIBRARY$")
02553           get_target_property (PREFIX ${TARGET_UID} PREFIX)
02554           get_target_property (SUFFIX ${TARGET_UID} SUFFIX)
02555           if (FNAME)
02556             set (TARGET_FILE "${FNAME}")
02557           else ()
02558             set (TARGET_FILE "${TARGET_NAME}")
02559           endif ()
02560           if (PREFIX)
02561             set (TARGET_FILE "${PREFIX}${TARGET_FILE}")
02562           endif ()
02563           if (SUFFIX)
02564             set (TARGET_FILE "${TARGET_FILE}${SUFFIX}")
02565           elseif (WIN32 AND TYPE MATCHES "^EXECUTABLE$")
02566             set (TARGET_FILE "${TARGET_FILE}.exe")
02567           endif ()
02568         endif ()
02569         # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
02570         # assemble final path
02571         if (PART MATCHES "POST_INSTALL_RELATIVE")
02572           if (IS_ABSOLUTE "${DIRECTORY}")
02573             file (RELATIVE_PATH DIRECTORY "${CMAKE_INSTALL_PREFIX}" "${DIRECTORY}")
02574             if (NOT DIRECTORY)
02575               set (DIRECTORY ".")
02576             endif ()
02577           endif ()
02578         elseif (PART MATCHES "POST_INSTALL")
02579           if (NOT IS_ABSOLUTE "${DIRECTORY}")
02580             set (DIRECTORY "${CMAKE_INSTALL_PREFIX}/${DIRECTORY}")
02581           endif ()
02582         endif ()
02583         if (TARGET_FILE)
02584           set (LOCATION "${DIRECTORY}/${TARGET_FILE}")
02585         else ()
02586           set (LOCATION "${DIRECTORY}")
02587         endif ()
02588       else ()
02589         set (LOCATION "${DIRECTORY}")
02590       endif ()
02591     endif ()
02592     # get filename component
02593     if (LOCATION AND PART MATCHES "(^|_)(PATH|NAME|NAME_WE)$")
02594       get_filename_component (LOCATION "${LOCATION}" "${CMAKE_MATCH_2}")
02595     endif ()
02596   else ()
02597     message (FATAL_ERROR "basis_get_target_location(): Unknown target ${TARGET_UID}")
02598   endif ()
02599   # return
02600   set ("${VAR}" "${LOCATION}" PARENT_SCOPE)
02601 endfunction ()
02602 
02603 # ----------------------------------------------------------------------------
02604 ## @brief Get link libraries/dependencies of (imported) target.
02605 #
02606 # This function recursively adds the dependencies of the dependencies as well
02607 # and returns them together with the list of the direct link dependencies.
02608 # Moreover, for script targets, if any of the dependencies uses the BASIS
02609 # utilities for the given language (@c BASIS_UTILITIES property), the
02610 # corresponding utilities library is added to the list of dependencies.
02611 # Note that therefore the BASIS utilities targets have to be added already,
02612 # which is only the case during the finalization of script targets.
02613 #
02614 # @param [out] LINK_DEPENDS List of all link dependencies. In case of scripts,
02615 #                           the dependencies are the required modules or
02616 #                           paths to required packages, respectively.
02617 # @param [in]  TARGET_NAME  Name of the target.
02618 function (basis_get_target_link_libraries LINK_DEPENDS TARGET_NAME)
02619   basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
02620   if (NOT TARGET "${TARGET_UID}")
02621     message (FATAL_ERROR "basis_get_target_link_libraries(): Unknown target: ${TARGET_UID}")
02622   endif ()
02623   if (BASIS_DEBUG AND BASIS_VERBOSE)
02624     message (STATUS "** basis_get_target_link_libraries():")
02625     message (STATUS "**   TARGET_NAME:     ${TARGET_NAME}")
02626     message (STATUS "**   CURRENT_DEPENDS: ${ARGN}")
02627   endif ()
02628   # get type of target
02629   get_target_property (BASIS_TYPE ${TARGET_UID} BASIS_TYPE)
02630   # get direct link dependencies of target
02631   get_target_property (IMPORTED ${TARGET_UID} IMPORTED)
02632   if (IMPORTED)
02633     # 1. Try IMPORTED_LINK_INTERFACE_LIBRARIES_<CMAKE_BUILD_TYPE>
02634     if (CMAKE_BUILD_TYPE)
02635       string (TOUPPER "${CMAKE_BUILD_TYPE}" U)
02636     else ()
02637       set (U "NOCONFIG")
02638     endif ()
02639     get_target_property (DEPENDS ${TARGET_UID} "IMPORTED_LINK_INTERFACE_LIBRARIES_${U}")
02640     # 2. Try IMPORTED_LINK_INTERFACE_LIBRARIES
02641     if (NOT DEPENDS)
02642       get_target_property (DEPENDS ${TARGET_UID} "IMPORTED_LINK_INTERFACE_LIBRARIES")
02643     endif ()
02644     # 3. Prefer Release over all other configurations
02645     if (NOT DEPENDS)
02646       get_target_property (DEPENDS ${TARGET_UID} "IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE")
02647     endif ()
02648     # 4. Just use any of the imported configurations
02649     if (NOT DEPENDS)
02650       get_property (CONFIGS TARGET "${TARGET_UID}" PROPERTY IMPORTED_CONFIGURATIONS)
02651       foreach (C IN LISTS CONFIGS)
02652         get_target_property (DEPENDS ${TARGET_UID} "IMPORTED_LINK_INTERFACE_LIBRARIES_${C}")
02653         if (DEPENDS)
02654           break ()
02655         endif ()
02656       endforeach ()
02657     endif ()
02658   # otherwise, get LINK_DEPENDS property value
02659   elseif (BASIS_TYPE MATCHES "^EXECUTABLE$|^(SHARED|STATIC|MODULE)_LIBRARY$")
02660     get_target_property (DEPENDS ${TARGET_UID} BASIS_LINK_DEPENDS)
02661   else ()
02662     get_target_property (DEPENDS ${TARGET_UID} LINK_DEPENDS)
02663   endif ()
02664   if (NOT DEPENDS)
02665     set (DEPENDS)
02666   endif ()
02667   # prepend BASIS utilities if used (and added)
02668   if (BASIS_TYPE MATCHES "SCRIPT")
02669     set (BASIS_UTILITIES_TARGETS)
02670     foreach (UID IN ITEMS ${TARGET_UID} ${DEPENDS})
02671       if (TARGET "${UID}")
02672         get_target_property (BASIS_UTILITIES ${UID} BASIS_UTILITIES)
02673         get_target_property (LANGUAGE        ${UID} LANGUAGE)
02674         if (BASIS_UTILITIES)
02675           set (BASIS_UTILITIES_TARGET)
02676           if (LANGUAGE MATCHES "[JP]YTHON")
02677             basis_get_source_target_name (BASIS_UTILITIES_TARGET "basis.py" NAME)
02678           elseif (LANGUAGE MATCHES "PERL")
02679             basis_get_source_target_name (BASIS_UTILITIES_TARGET "Basis.pm" NAME)
02680           elseif (LANGUAGE MATCHES "BASH")
02681             basis_get_source_target_name (BASIS_UTILITIES_TARGET "basis.sh" NAME)
02682           endif ()
02683           if (BASIS_UTILITIES_TARGET)
02684             basis_get_target_uid (BASIS_UTILITIES_TARGET ${BASIS_UTILITIES_TARGET})
02685           endif ()
02686           if (TARGET ${BASIS_UTILITIES_TARGET})
02687             list (APPEND BASIS_UTILITIES_TARGETS ${BASIS_UTILITIES_TARGET})
02688           endif ()
02689         endif ()
02690       endif ()
02691     endforeach ()
02692     if (BASIS_UTILITIES_TARGETS)
02693       list (INSERT DEPENDS 0 ${BASIS_UTILITIES_TARGETS})
02694     endif ()
02695   endif ()
02696   # convert target names to UIDs
02697   set (_DEPENDS)
02698   foreach (LIB IN LISTS DEPENDS)
02699     basis_get_target_uid (UID "${LIB}")
02700     if (TARGET ${UID})
02701       list (APPEND _DEPENDS "${UID}")
02702     else ()
02703       list (APPEND _DEPENDS "${LIB}")
02704     endif ()
02705   endforeach ()
02706   set (DEPENDS "${_DEPENDS}")
02707   unset (_DEPENDS)
02708   # recursively add link dependencies of dependencies
02709   # TODO implement it non-recursively for better performance
02710   foreach (LIB IN LISTS DEPENDS)
02711     if (TARGET ${LIB})
02712       list (FIND ARGN "${LIB}" IDX) # avoid recursive loop
02713       if (IDX EQUAL -1)
02714         basis_get_target_link_libraries (LIB_DEPENDS ${LIB} ${ARGN} ${DEPENDS})
02715         list (APPEND DEPENDS ${LIB_DEPENDS})
02716       endif ()
02717     endif ()
02718   endforeach ()
02719   # remove duplicate entries
02720   if (DEPENDS)
02721     list (REMOVE_DUPLICATES DEPENDS)
02722   endif ()
02723   # return
02724   set (${LINK_DEPENDS} "${DEPENDS}" PARENT_SCOPE)
02725 endfunction ()
02726 
02727 # ============================================================================
02728 # generator expressions
02729 # ============================================================================
02730 
02731 # ----------------------------------------------------------------------------
02732 ## @brief Process generator expressions in arguments.
02733 #
02734 # This command evaluates the $&lt;TARGET_FILE:tgt&gt; and related generator
02735 # expressions also for custom targets such as scripts and MATLAB Compiler
02736 # targets. For other generator expressions whose argument is a target name,
02737 # this function replaces the target name by the target UID, i.e., the actual
02738 # CMake target name such that the expression can be evaluated by CMake.
02739 # The following generator expressions are directly evaluated by this function:
02740 # <table border=0>
02741 #   <tr>
02742 #     @tp <b><tt>$&lt;TARGET_FILE:tgt&gt;</tt></b> @endtp
02743 #     <td>Absolute file path of built target.</td>
02744 #   </tr>
02745 #   <tr>
02746 #     @tp <b><tt>$&lt;TARGET_FILE_POST_INSTALL:tgt&gt;</tt></b> @endtp
02747 #     <td>Absolute path of target file after installation using the
02748 #         current @c CMAKE_INSTALL_PREFIX.</td>
02749 #   </tr>
02750 #   <tr>
02751 #     @tp <b><tt>$&lt;TARGET_FILE_POST_INSTALL_RELATIVE:tgt&gt;</tt></b> @endtp
02752 #     <td>Path of target file after installation relative to @c CMAKE_INSTALL_PREFIX.</td>
02753 #   </tr>
02754 # </table>
02755 # Additionally, the suffix <tt>_NAME</tt> or <tt>_DIR</tt> can be appended
02756 # to the name of each of these generator expressions to get only the basename
02757 # of the target file including the extension or the corresponding directory
02758 # path, respectively.
02759 #
02760 # Generator expressions are in particular supported by basis_add_test().
02761 #
02762 # @param [out] ARGS Name of output list variable.
02763 # @param [in]  ARGN List of arguments to process.
02764 #
02765 # @sa basis_add_test()
02766 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:add_test
02767 function (basis_process_generator_expressions ARGS)
02768   set (ARGS_OUT)
02769   foreach (ARG IN LISTS ARGN)
02770     string (REGEX MATCHALL "\\$<.*TARGET.*:.*>" EXPRS "${ARG}")
02771     foreach (EXPR IN LISTS EXPRS)
02772       if (EXPR MATCHES "\\$<(.*):(.*)>")
02773         set (EXPR_NAME   "${CMAKE_MATCH_1}")
02774         set (TARGET_NAME "${CMAKE_MATCH_2}")
02775         # TARGET_FILE* expression, including custom targets
02776         if (EXPR_NAME MATCHES "^TARGET_FILE(.*)")
02777           if (NOT CMAKE_MATCH_1)
02778             set (CMAKE_MATCH_1 "ABSOLUTE")
02779           endif ()
02780           string (REGEX REPLACE "^_" "" PART "${CMAKE_MATCH_1}")
02781           basis_get_target_location (ARG "${TARGET_NAME}" ${PART})
02782         # other generator expression supported by CMake
02783         # only replace target name, but do not evaluate expression
02784         else ()
02785           basis_get_target_uid (TARGET_UID "${CMAKE_MATCH_2}")
02786           string (REPLACE "${EXPR}" "$<${CMAKE_MATCH_1}:${TARGET_UID}>" ARG "${ARG}")
02787         endif ()
02788         if (BASIS_DEBUG AND BASIS_VERBOSE)
02789           message ("** basis_process_generator_expressions():")
02790           message ("**   Expression:  ${EXPR}")
02791           message ("**   Keyword:     ${EXPR_NAME}")
02792           message ("**   Argument:    ${TARGET_NAME}")
02793           message ("**   Replaced by: ${ARG}")
02794         endif ()
02795       endif ()
02796     endforeach ()
02797     list (APPEND ARGS_OUT "${ARG}")
02798   endforeach ()
02799   set (${ARGS} "${ARGS_OUT}" PARENT_SCOPE)
02800 endfunction ()
02801 
02802 
02803 ## @}
02804 # end of Doxygen group