BASIS  version 1.2.3 (revision 2104)
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 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 ## @addtogroup CMakeUtilities
00021 #  @{
00022 
00023 
00024 # ============================================================================
00025 # find other packages
00026 # ============================================================================
00027 
00028 # ----------------------------------------------------------------------------
00029 macro (find_package)
00030   if (BASIS_DEBUG)
00031     message ("find_package(${ARGV})")
00032   endif ()
00033   _find_package(${ARGV})
00034 endmacro ()
00035 
00036 # ----------------------------------------------------------------------------
00037 ## @brief Find external software package or other project module.
00038 #
00039 # This function replaces CMake's
00040 # <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package">
00041 # find_package()</a> command and extends its functionality.
00042 # In particular, if the given package name is the name of another module
00043 # of this project (the top-level project), it ensures that this module is
00044 # found instead of an external package.
00045 #
00046 # If the package is found, but only optionally used, i.e., the @c REQUIRED
00047 # argument was not given to this macro, a <tt>USE_&lt;Pkg&gt;</tt> option is
00048 # added by this macro which is by default @c ON. This option can be set to
00049 # @c OFF by the user in order to force the <tt>&lt;Pkg&gt;_FOUND</tt> variable
00050 # to be set to @c FALSE again even if the package was found. This allows the
00051 # user to specify which of the optional dependencies should actually not be
00052 # used for the build of the software even though these packages are installed
00053 # on their system.
00054 #
00055 # @param [in] PACKAGE Name of other package. Optionally, the package name
00056 #                     can include a version specification as suffix which
00057 #                     is separated by the package name using a dash (-), i.e.,
00058 #                     &lt;Package&gt;[-major[.minor[.patch[.tweak]]]].
00059 #                     If a version specification is given, it is passed on as
00060 #                     @c version argument to CMake's
00061 #                     <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package">
00062 #                     find_package()</a> command.
00063 # @param [in] ARGN    Advanced arguments for
00064 #                     <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package">
00065 #                     find_package()</a>.
00066 #
00067 # @retval <PACKAGE>_FOUND Whether the given package was found.
00068 #
00069 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:find_package
00070 #
00071 # @ingroup CMakeAPI
00072 macro (basis_find_package PACKAGE)
00073   # parse arguments
00074   set (PKG "${PACKAGE}")
00075   set (VER)
00076   CMAKE_PARSE_ARGUMENTS (
00077     ARGN
00078     "EXACT;QUIET;REQUIRED"
00079     ""
00080     "COMPONENTS"
00081     ${ARGN}
00082   )
00083   # --------------------------------------------------------------------------
00084   # extract components from PACKAGE
00085   if (PKG MATCHES "^([^ ]+)[ \\n\\t]*{(.*)}$")
00086     set (PKG "${CMAKE_MATCH_1}")
00087     string (REPLACE "," ";" CMPS "${CMAKE_MATCH_2}")
00088     foreach (CMP IN LISTS CMPS)
00089       string (STRIP "${CMP}" CMP)
00090       list (APPEND ARGN_COMPONENTS ${CMP})
00091     endforeach ()
00092     unset (CMP)
00093     unset (CMPS)
00094   endif ()
00095   # split PACKAGE into package name and version number
00096   if (ARGN_UNPARSED_ARGUMENTS MATCHES "^[0-9]+(\\.[0-9]+)*$")
00097     set (VER "${CMAKE_MATCH_0}")
00098   endif ()
00099   if (PKG MATCHES "^(.*)-([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(\\.[0-9]+)?$")
00100     if (VER)
00101       message (FATAL_ERROR "Cannot use both version specification as part of "
00102                            "package name and explicit version argument.")
00103     endif ()
00104     set (PKG "${CMAKE_MATCH_1}")
00105     set (VER "${CMAKE_MATCH_2}${CMAKE_MATCH_3}${CMAKE_MATCH_4}${CMAKE_MATCH_5}")
00106   endif ()
00107   # --------------------------------------------------------------------------
00108   # preserve <PKG>_DIR variable which might get reset if different versions
00109   # of the package are searched or if package is optional and deselected
00110   set (PKG_DIR "${${PKG}_DIR}")
00111   # --------------------------------------------------------------------------
00112   # some debugging output
00113   if (BASIS_DEBUG)
00114     message ("** basis_find_package()")
00115     message ("**     Package:    ${PKG}")
00116     if (VER)
00117     message ("**     Version:    ${VER}")
00118     endif ()
00119     if (ARGN_COMPONENTS)
00120     message ("**     Components: [${ARGN_COMPONENTS}]")
00121     endif ()
00122   endif ()
00123   # --------------------------------------------------------------------------
00124   # find other modules of same project
00125   if (PROJECT_IS_MODULE)
00126     # allow modules to specify top-level project as dependency
00127     if (PKG MATCHES "^${BASIS_PROJECT_NAME}$")
00128       if (BASIS_DEBUG)
00129         message ("**     This is the top-level project.")
00130       endif ()
00131       set (${PKG}_FOUND TRUE)
00132     else ()
00133       # look for other module of top-level project
00134       list (FIND PROJECT_MODULES "${PKG}" IDX)
00135       if (NOT IDX EQUAL -1)
00136         list (FIND PROJECT_MODULES_ENABLED "${PKG}" IDX)
00137         if (IDX EQUAL -1)
00138           set (${PKG}_FOUND FALSE)
00139         else ()
00140           if (BASIS_DEBUG)
00141             message ("**     Identified it as other module of this project.")
00142           endif ()
00143           include ("${${PKG}_DIR}/${PKG}Config.cmake")
00144           set (${PKG}_FOUND TRUE)
00145         endif ()
00146       endif ()
00147     endif ()
00148   endif ()
00149   # --------------------------------------------------------------------------
00150   # hide or show already defined <PKG>_DIR cache entry
00151   if (DEFINED ${PKG}_DIR AND DEFINED USE_${PKG})
00152     if (USE_${PKG})
00153       mark_as_advanced (CLEAR ${PKG}_DIR)
00154     else ()
00155       mark_as_advanced (FORCE ${PKG}_DIR)
00156     endif ()
00157   endif ()
00158   # --------------------------------------------------------------------------
00159   # find external packages
00160   string (TOUPPER "${PKG}" PKG_UPPER)
00161   if (NOT ${PKG}_FOUND AND (NOT DEFINED USE_${PKG} OR USE_${PKG}))
00162     # circumvent issue with CMake's find_package() interpreting these variables
00163     # relative to the current binary directory instead of the top-level directory
00164     if (${PKG}_DIR AND NOT IS_ABSOLUTE "${${PKG}_DIR}")
00165       set (${PKG}_DIR "${CMAKE_BINARY_DIR}/${${PKG}_DIR}")
00166       get_filename_component (${PKG}_DIR "${${PKG}_DIR}" ABSOLUTE)
00167     endif ()
00168     # set EXTENSION_NAME in case of Slicer if not set
00169     if (PKG MATCHES "^Slicer$")
00170       if (NOT EXTENSION_NAME)
00171         set (EXTENSION_NAME "${PROJECT_NAME}")
00172       endif ()
00173     endif ()
00174     # now look for the package
00175     set (FIND_ARGN)
00176     if (ARGN_EXACT)
00177       list (APPEND FIND_ARGN "EXACT")
00178     endif ()
00179     if (ARGN_QUIET)
00180       list (APPEND FIND_ARGN "QUIET")
00181     endif ()
00182     if (ARGN_COMPONENTS)
00183       list (APPEND FIND_ARGN "COMPONENTS" ${ARGN_COMPONENTS})
00184     elseif (ARGN_REQUIRED)
00185       list (APPEND FIND_ARGN "REQUIRED")
00186     endif ()
00187     if ("${PKG}" MATCHES "^(MFC|wxWidgets)$")
00188       # if Find<Pkg>.cmake prints status message, don't do it here
00189       find_package (${PKG} ${VER} ${FIND_ARGN})
00190     else ()
00191       set (MSG "${PKG}")
00192       if (VER)
00193         set (MSG "${PKG} ${VER}")
00194       endif ()
00195       message (STATUS "Looking for ${MSG}...")
00196       find_package (${PKG} ${VER} ${FIND_ARGN})
00197       if (${PKG_UPPER}_FOUND)
00198         set (${PKG}_FOUND TRUE)
00199       endif ()
00200       if (${PKG}_FOUND)
00201         if (DEFINED ${PKG}_DIR)
00202           message (STATUS "Looking for ${MSG}... - found: ${${PKG}_DIR}")
00203         elseif (DEFINED ${PKG_UPPER}_DIR)
00204           message (STATUS "Looking for ${MSG}... - found: ${${PKG_UPPER}_DIR}")
00205         else ()
00206           message (STATUS "Looking for ${MSG}... - found")
00207         endif ()
00208       else ()
00209         message (STATUS "Looking for ${MSG}... - not found")
00210       endif ()
00211     endif ()
00212     # provide option which allows users to disable use of not required packages
00213     if (${PKG}_FOUND AND NOT ARGN_REQUIRED)
00214       option (USE_${PKG} "Enable/disable use of package ${PKG}." ON)
00215       if (NOT USE_${PKG})
00216         set (${PKG}_FOUND       FALSE)
00217         set (${PKG_UPPER}_FOUND FALSE)
00218       endif ()
00219     endif ()
00220   endif ()
00221   # --------------------------------------------------------------------------
00222   # reset <PKG>_DIR variable for possible search of different package version
00223   if (PKG_DIR AND NOT ${PKG}_DIR)
00224     basis_set_or_update_cache (${PKG}_DIR "${PKG_DIR}")
00225   endif ()
00226   # --------------------------------------------------------------------------
00227   # unset locally used variables
00228   unset (PACKAGE_DIR)
00229   unset (PKG)
00230   unset (PKG_UPPER)
00231   unset (VER)
00232   unset (USE_PKG_OPTION)
00233 endmacro ()
00234 
00235 # ----------------------------------------------------------------------------
00236 ## @brief Use found package.
00237 #
00238 # This macro includes the package's use file if the variable @c &lt;Pkg&gt;_USE_FILE
00239 # is defined. Otherwise, it adds the include directories to the search path
00240 # for include paths if possible. Therefore, the corresponding package
00241 # configuration file has to set the proper CMake variables, i.e.,
00242 # either @c &lt;Pkg&gt;_INCLUDES, @c &lt;Pkg&gt;_INCLUDE_DIRS, or @c &lt;Pkg&gt;_INCLUDE_DIR.
00243 #
00244 # If the given package name is the name of another module of this project
00245 # (the top-level project), this function includes the use file of the specified
00246 # module.
00247 #
00248 # @note As some packages still use all captial variables instead of ones
00249 #       prefixed by a string that follows the same capitalization as the
00250 #       package's name, this function also considers these if defined instead.
00251 #       Hence, if @c &lt;PKG&gt;_INCLUDES is defined, but not @c &lt;Pkg&gt;_INCLUDES, it
00252 #       is used in place of the latter.
00253 #
00254 # @note According to an email on the CMake mailing list, it is not a good idea
00255 #       to use basis_link_directories() any more given that the arguments to
00256 #       basis_target_link_libraries() are absolute paths to the library files.
00257 #       Therefore, this code is commented and not used. It remains here as a
00258 #       reminder only.
00259 #
00260 # @param [in] PACKAGE Name of other package. Optionally, the package name
00261 #                     can include a version specification as suffix which
00262 #                     is separated by the package name using a dash (-), i.e.,
00263 #                     &lt;Package&gt;[-major[.minor[.patch[.tweak]]]].
00264 #                     A version specification is simply ignored by this macro.
00265 #
00266 # @ingroup CMakeAPI
00267 macro (basis_use_package PACKAGE)
00268   set (PKG "${PACKAGE}")
00269   # extract components from PACKAGE
00270   if (PKG MATCHES "^([^ ]+){.*}$")
00271     set (PKG "${CMAKE_MATCH_1}")
00272   endif ()
00273   # split PACKAGE into package name and version number
00274   if (PKG MATCHES "^(.*)-([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(\\.[0-9]+)?$")
00275     set (PKG "${CMAKE_MATCH_1}")
00276   endif ()
00277   # use package
00278   foreach (A IN ITEMS "WORKAROUND FOR NOT BEING ABLE TO USE RETURN")
00279     if (BASIS_DEBUG)
00280       message ("** basis_use_package()")
00281       message ("**    Package: ${PKG}")
00282     endif ()
00283     if (PROJECT_IS_MODULE)
00284       # allow modules to specify top-level project as dependency
00285       if (PKG MATCHES "^${BASIS_PROJECT_NAME}$")
00286         if (BASIS_DEBUG)
00287           message ("**     This is the top-level project.")
00288         endif ()
00289         break () # instead of return()
00290       else ()
00291         # use other module of top-level project
00292         list (FIND PROJECT_MODULES "${PKG}" IDX)
00293         if (NOT IDX EQUAL -1)
00294           if (${PKG}_FOUND)
00295             if (BASIS_DEBUG)
00296               message ("**     Include package use file of other module.")
00297             endif ()
00298             include ("${${PKG}_DIR}/${PKG}Use.cmake")
00299             break () # instead of return()
00300           else ()
00301             message (FATAL_ERROR "Module ${PKG} not found! This must be a "
00302                                  "mistake of BASIS. Talk to the maintainer of this "
00303                                  "package and have them fix it.")
00304           endif ()
00305         endif ()
00306       endif ()
00307     endif ()
00308     # use external package
00309     string (TOUPPER "${PKG}" PKG_UPPER)
00310     if (${PKG}_FOUND OR ${PKG_UPPER}_FOUND)
00311       # use package only if basis_use_package() not invoked before
00312       if (BASIS_USE_${PKG}_INCLUDED)
00313         if (BASIS_DEBUG)
00314           message ("**     External package used before already.")
00315         endif ()
00316         break ()
00317       endif ()
00318       if (${PKG}_USE_FILE)
00319         if (BASIS_DEBUG)
00320           message ("**     Include package use file of external package.")
00321         endif ()
00322         include ("${${PKG}_USE_FILE}")
00323       elseif (${PKG_UPPER}_USE_FILE)
00324         if (BASIS_DEBUG)
00325           message ("**     Include package use file of external package.")
00326         endif ()
00327         include ("${${PKG_UPPER}_USE_FILE}")
00328       else ()
00329         if (BASIS_DEBUG)
00330           message ("**     Use variables which were set by basis_find_package().")
00331         endif ()
00332         # OpenCV
00333         if ("${PKG}" STREQUAL "OpenCV")
00334           # the cv.h may be found as part of PerlLibs, the include path of
00335           # which is added at first by BASISConfig.cmake
00336           if (OpenCV_INCLUDE_DIRS)
00337             basis_include_directories (BEFORE ${OpenCV_INCLUDE_DIRS})
00338           elseif (OpenCV_INCLUDE_DIR)
00339             basis_include_directories (BEFORE ${OpenCV_INCLUDE_DIR})
00340           endif ()
00341         # generic
00342         else ()
00343           if (${PKG}_INCLUDE_DIRS OR ${PKG_UPPER}_INCLUDE_DIRS)
00344             if (${PKG}_INCLUDE_DIRS)
00345               basis_include_directories (${${PKG}_INCLUDE_DIRS})
00346             else ()
00347               basis_include_directories (${${PKG_UPPER}_INCLUDE_DIRS})
00348             endif ()
00349           elseif (${PKG}_INCLUDES OR ${v}_INCLUDES)
00350             if (${PKG}_INCLUDES)
00351               basis_include_directories (${${PKG}_INCLUDES})
00352             else ()
00353               basis_include_directories (${${PKG_UPPER}_INCLUDES})
00354             endif ()
00355           elseif (${PKG}_INCLUDE_PATH OR ${PKG_UPPER}_INCLUDE_PATH)
00356             if (${PKG}_INCLUDE_PATH)
00357               basis_include_directories (${${PKG}_INCLUDE_PATH})
00358             else ()
00359               basis_include_directories (${${PKG_UPPER}_INCLUDE_PATH})
00360             endif ()
00361           elseif (${PKG}_INCLUDE_DIR OR ${PKG_UPPER}_INCLUDE_DIR)
00362             if (${PKG}_INCLUDE_DIR)
00363               basis_include_directories (${${PKG}_INCLUDE_DIR})
00364             else ()
00365               basis_include_directories (${${PKG_UPPER}_INCLUDE_DIR})
00366             endif ()  
00367           endif ()
00368         endif ()
00369       endif ()
00370       set (BASIS_USE_${PKG}_INCLUDED TRUE)
00371     elseif (ARGC GREATER 1 AND "${ARGV1}" MATCHES "^REQUIRED$")
00372       if (BASIS_DEBUG)
00373         basis_dump_variables ("${PROJECT_BINARY_DIR}/VariablesAfterFind${PKG}.cmake")
00374       endif ()
00375       message (FATAL_ERROR "Package ${PACKAGE} not found!")
00376     endif ()
00377     unset (PKG_UPPER)
00378   endforeach ()
00379 endmacro ()
00380 
00381 # ============================================================================
00382 # basis_get_filename_component / basis_get_relative_path
00383 # ============================================================================
00384 
00385 # ----------------------------------------------------------------------------
00386 ## @brief Fixes CMake's
00387 #         <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_filename_component">
00388 #         get_filename_component()</a> command.
00389 #
00390 # The get_filename_component() command of CMake returns the entire portion
00391 # after the first period (.) [including the period] as extension. However,
00392 # only the component following the last period (.) [including the period]
00393 # should be considered to be the extension.
00394 #
00395 # @note Consider the use of the basis_get_filename_component() macro as
00396 #       an alias to emphasize that this function is different from CMake's
00397 #       <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_filename_component">
00398 #       get_filename_component()</a> command.
00399 #
00400 # @param [in,out] ARGN Arguments as accepted by get_filename_component().
00401 #
00402 # @returns Sets the variable named by the first argument to the requested
00403 #          component of the given file path.
00404 #
00405 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_filename_component
00406 # @sa basis_get_filename_component()
00407 function (get_filename_component)
00408   if (ARGC GREATER 4)
00409     message (FATAL_ERROR "(basis_)get_filename_component(): Too many arguments!")
00410   endif ()
00411 
00412   list (GET ARGN 0 VAR)
00413   list (GET ARGN 1 STR)
00414   list (GET ARGN 2 CMD)
00415   if (CMD MATCHES "^EXT")
00416     _get_filename_component (${VAR} "${STR}" ${CMD})
00417     string (REGEX MATCHALL "\\.[^.]*" PARTS "${${VAR}}")
00418     list (LENGTH PARTS LEN)
00419     if (LEN GREATER 1)
00420       math (EXPR LEN "${LEN} - 1")
00421       list (GET PARTS ${LEN} ${VAR})
00422     endif ()
00423   elseif (CMD MATCHES "NAME_WE")
00424     _get_filename_component (${VAR} "${STR}" NAME)
00425     string (REGEX REPLACE "\\.[^.]*$" "" ${VAR} ${${VAR}})
00426   else ()
00427     _get_filename_component (${VAR} "${STR}" ${CMD})
00428   endif ()
00429   if (ARGC EQUAL 4)
00430     if (NOT ARGV3 MATCHES "^CACHE$")
00431       message (FATAL_ERROR "(basis_)get_filename_component(): Invalid fourth argument: ${ARGV3}!")
00432     else ()
00433       set (${VAR} "${${VAR}}" CACHE STRING "")
00434     endif ()
00435   else ()
00436     set (${VAR} "${${VAR}}" PARENT_SCOPE)
00437   endif ()
00438 endfunction ()
00439 
00440 # ----------------------------------------------------------------------------
00441 ## @brief Alias for the overwritten get_filename_component() function.
00442 #
00443 # @sa get_filename_component()
00444 #
00445 # @ingroup CMakeAPI
00446 macro (basis_get_filename_component)
00447   get_filename_component (${ARGN})
00448 endmacro ()
00449 
00450 # ----------------------------------------------------------------------------
00451 ## @brief Get path relative to a given base directory.
00452 #
00453 # Unlike the file(RELATIVE_PATH ...) command of CMake which if @p PATH and
00454 # @p BASE are the same directory returns an empty string, this function
00455 # returns a dot (.) in this case instead.
00456 #
00457 # @param [out] REL  @c PATH relative to @c BASE.
00458 # @param [in]  BASE Path of base directory. If a relative path is given, it
00459 #                   is made absolute using basis_get_filename_component()
00460 #                   with ABSOLUTE as last argument.
00461 # @param [in]  PATH Absolute or relative path. If a relative path is given
00462 #                   it is made absolute using basis_get_filename_component()
00463 #                   with ABSOLUTE as last argument.
00464 #
00465 # @returns Sets the variable named by the first argument to the relative path.
00466 #
00467 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:file
00468 #
00469 # @ingroup CMakeAPI
00470 function (basis_get_relative_path REL BASE PATH)
00471   basis_get_filename_component (PATH "${PATH}" ABSOLUTE)
00472   basis_get_filename_component (BASE "${BASE}" ABSOLUTE)
00473   if (NOT PATH)
00474     message (FATAL_ERROR "basis_get_relative_path(): No PATH given!")
00475   endif ()
00476   if (NOT BASE)
00477     message (FATAL_ERROR "basis_get_relative_path(): No BASE given!")
00478   endif ()
00479   file (RELATIVE_PATH P "${BASE}" "${PATH}")
00480   if ("${P}" STREQUAL "")
00481     set (P ".")
00482   endif ()
00483   set (${REL} "${P}" PARENT_SCOPE)
00484 endfunction ()
00485 
00486 # ============================================================================
00487 # name / version
00488 # ============================================================================
00489 
00490 # ----------------------------------------------------------------------------
00491 ## @brief Convert string to lowercase only or mixed case.
00492 #
00493 # Strings in all uppercase or all lowercase are converted to all lowercase
00494 # letters because these are usually used for acronymns. All other strings
00495 # are returned unmodified with the one exception that the first letter has
00496 # to be uppercase for mixed case strings.
00497 #
00498 # This function is in particular used to normalize the project name for use
00499 # in installation directory paths and namespaces.
00500 #
00501 # @param [out] OUT String in CamelCase.
00502 # @param [in]  STR String.
00503 function (basis_normalize_name OUT STR)
00504   # strings in all uppercase or all lowercase such as acronymns are an
00505   # exception and shall be converted to all lowercase instead
00506   string (TOLOWER "${STR}" L)
00507   string (TOUPPER "${STR}" U)
00508   if ("${STR}" STREQUAL "${L}" OR "${STR}" STREQUAL "${U}")
00509     set (${OUT} "${L}" PARENT_SCOPE)
00510   # change first letter to uppercase
00511   else ()
00512     string (SUBSTRING "${U}"   0  1 A)
00513     string (SUBSTRING "${STR}" 1 -1 B)
00514     set (${OUT} "${A}${B}" PARENT_SCOPE)
00515   endif ()
00516 endfunction ()
00517 
00518 # ----------------------------------------------------------------------------
00519 ## @brief Extract version numbers from version string.
00520 #
00521 # @param [in]  VERSION Version string in the format "MAJOR[.MINOR[.PATCH]]".
00522 # @param [out] MAJOR   Major version number if given or 0.
00523 # @param [out] MINOR   Minor version number if given or 0.
00524 # @param [out] PATCH   Patch number if given or 0.
00525 #
00526 # @returns See @c [out] parameters.
00527 function (basis_version_numbers VERSION MAJOR MINOR PATCH)
00528   if (VERSION MATCHES "([0-9]+)\\.([0-9]+)rc[1-9][0-9]*")
00529     set (VERSION_MAJOR ${CMAKE_MATCH_1})
00530     set (VERSION_MINOR ${CMAKE_MATCH_2})
00531     set (VERSION_PATCH 0)
00532   else ()
00533     string (REGEX MATCHALL "[0-9]+" VERSION_PARTS "${VERSION}")
00534     list (LENGTH VERSION_PARTS VERSION_COUNT)
00535 
00536     if (VERSION_COUNT GREATER 0)
00537       list (GET VERSION_PARTS 0 VERSION_MAJOR)
00538     else ()
00539       set (VERSION_MAJOR "0")
00540     endif ()
00541     if (VERSION_COUNT GREATER 1)
00542       list (GET VERSION_PARTS 1 VERSION_MINOR)
00543     else ()
00544       set (VERSION_MINOR "0")
00545     endif ()
00546     if (VERSION_COUNT GREATER 2)
00547       list (GET VERSION_PARTS 2 VERSION_PATCH)
00548     else ()
00549       set (VERSION_PATCH "0")
00550     endif ()
00551   endif ()
00552   set ("${MAJOR}" "${VERSION_MAJOR}" PARENT_SCOPE)
00553   set ("${MINOR}" "${VERSION_MINOR}" PARENT_SCOPE)
00554   set ("${PATCH}" "${VERSION_PATCH}" PARENT_SCOPE)
00555 endfunction ()
00556 
00557 # ============================================================================
00558 # set
00559 # ============================================================================
00560 
00561 # ----------------------------------------------------------------------------
00562 ## @brief Set variable.
00563 #
00564 # If the variable is cached, this function will update the cache value,
00565 # otherwise, it simply sets the CMake variable uncached to the given value(s).
00566 function (basis_set_or_update_cache VAR)
00567   if (DEFINED "${VAR}")
00568     get_property (CACHED CACHE "${VAR}" PROPERTY VALUE DEFINED)
00569   else ()
00570     set (CACHED FALSE)
00571   endif ()
00572   if (CACHED)
00573     if (ARGC GREATER 1)
00574       set_property (CACHE "${VAR}" PROPERTY VALUE ${ARGN})
00575     else ()
00576       set ("${VAR}" "" CACHE INTERNAL "" FORCE)
00577     endif ()
00578   else ()
00579     set ("${VAR}" ${ARGN} PARENT_SCOPE)
00580   endif ()
00581 endfunction ()
00582 
00583 # ----------------------------------------------------------------------------
00584 ## @brief Update cache variable.
00585 function (basis_update_cache VAR)
00586   if (DEFINED "${VAR}")
00587     get_property (CACHED CACHE "${VAR}" PROPERTY VALUE DEFINED)
00588   else ()
00589     set (CACHED FALSE)
00590   endif ()
00591   if (CACHED)
00592     set_property (CACHE "${VAR}" PROPERTY VALUE ${ARGN})
00593   endif ()
00594 endfunction ()
00595 
00596 # ----------------------------------------------------------------------------
00597 ## @brief Set value of variable only if variable is not set already.
00598 #
00599 # @param [out] VAR  Name of variable.
00600 # @param [in]  ARGN Arguments to set() command excluding variable name.
00601 #
00602 # @returns Sets @p VAR if its value was not valid before.
00603 macro (basis_set_if_empty VAR)
00604   if (NOT "${VAR}")
00605     set ("${VAR}" ${ARGN})
00606   endif ()
00607 endmacro ()
00608 
00609 # ----------------------------------------------------------------------------
00610 ## @brief Set value of variable only if variable is not defined yet.
00611 #
00612 # @param [out] VAR  Name of variable.
00613 # @param [in]  ARGN Arguments to set() command excluding variable name.
00614 #
00615 # @returns Sets @p VAR if it was not defined before.
00616 macro (basis_set_if_not_set VAR)
00617   if (NOT DEFINED "${VAR}")
00618     set ("${VAR}" ${ARGN})
00619   endif ()
00620 endmacro ()
00621 
00622 # ----------------------------------------------------------------------------
00623 ## @brief Set path relative to script file.
00624 #
00625 # This function can be used in script configurations. It takes a variable
00626 # name and a path as input arguments. If the given path is relative, it makes
00627 # it first absolute using @c PROJECT_SOURCE_DIR. Then the path is made
00628 # relative to the directory of the built script file. A CMake variable of the
00629 # given name is set to the specified relative path. Optionally, a third
00630 # argument, the path used for building the script for the install tree
00631 # can be passed as well. If a relative path is given as this argument,
00632 # it is made absolute by prefixing it with @c INSTALL_PREFIX instead.
00633 #
00634 # @note This function can only be used in script configurations such as
00635 #       in particular the ScriptConfig.cmake.in file. The actual definition
00636 #       of the function is generated by basis_add_script_finalize() and added
00637 #       to the top of the build script. The definition in CommonTools.cmake
00638 #       is only used to include the function in the API documentation.
00639 #
00640 # @param [out] VAR   Name of the variable.
00641 # @param [in]  PATH  Path to directory or file.
00642 # @param [in]  ARGV3 Path to directory or file inside install tree.
00643 #                    If this argument is not given, PATH is used for both
00644 #                    the build and install tree version of the script.
00645 #
00646 # @ingroup CMakeAPI
00647 function (basis_set_script_path VAR PATH)
00648   message (FATAL_ERROR "This function can only be used in ScriptConfig.cmake.in!")
00649 endfunction ()
00650 
00651 # ============================================================================
00652 # set/get any property
00653 # ============================================================================
00654 
00655 # ----------------------------------------------------------------------------
00656 ## @brief Convert list into regular expression.
00657 #
00658 # This function is in particular used to convert a list of property names
00659 # such as &lt;CONFIG&gt;_OUTPUT_NAME, e.g., the list @c BASIS_PROPERTIES_ON_TARGETS,
00660 # into a regular expression which can be used in pattern matches.
00661 #
00662 # @param [out] REGEX Name of variable for resulting regular expression.
00663 # @param [in]  ARGN  List of patterns which may contain placeholders in the
00664 #                    form of "<this is a placeholder>". These are replaced
00665 #                    by the regular expression "[^ ]+".
00666 macro (basis_list_to_regex REGEX)
00667   string (REGEX REPLACE "<[^>]+>" "[^ ]+" ${REGEX} "${ARGN}")
00668   string (REGEX REPLACE ";" "|" ${REGEX} "${${REGEX}}")
00669   set (${REGEX} "^(${${REGEX}})$")
00670 endmacro ()
00671 
00672 # ----------------------------------------------------------------------------
00673 ## @brief Output current CMake variables to file.
00674 function (basis_dump_variables RESULT_FILE)
00675   set (DUMP)
00676   get_cmake_property (VARIABLE_NAMES VARIABLES)
00677   foreach (V IN LISTS VARIABLE_NAMES)
00678     if (NOT V MATCHES "^_|^RESULT_FILE$|^ARGC$|^ARGV[0-9]?$")
00679       set (VALUE "${${V}}")
00680       # sanitize value for use in set() command
00681       string (REPLACE "\\" "\\\\" VALUE "${VALUE}") # escape backspaces
00682       string (REPLACE "\"" "\\\"" VALUE "${VALUE}") # escape double quotes
00683       # Escape ${VAR} by \${VAR} such that CMake does not evaluate it.
00684       # Escape $STR{VAR} by \$STR{VAR} such that CMake does not report a
00685       # syntax error b/c it expects either ${VAR}, $ENV{VAR}, or $CACHE{VAR}.
00686       # Escape @VAR@ by \@VAR\@ such that CMake does not evaluate it.
00687       string (REGEX REPLACE "([^\\])\\\$([^ ]*){" "\\1\\\\\$\\2{" VALUE "${VALUE}")
00688       string (REGEX REPLACE "([^\\])\\\@([^ ]*)\@" "\\1\\\\\@\\2\\\\\@" VALUE "${VALUE}")
00689       # append variable to output file
00690       set (DUMP "${DUMP}set (${V} \"${VALUE}\")\n")
00691     endif ()
00692   endforeach ()
00693   file (WRITE "${RESULT_FILE}" "# CMake variables dump created by BASIS\n${DUMP}")
00694 endfunction ()
00695 
00696 # ----------------------------------------------------------------------------
00697 ## @brief Set a named property in a given scope.
00698 #
00699 # This function replaces CMake's
00700 # <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property">
00701 # set_property()</a> command.
00702 #
00703 # @param [in] SCOPE The argument for the @p SCOPE parameter of
00704 #                   <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property">
00705 #                   set_property()</a>.
00706 # @param [in] ARGN  Arguments as accepted by.
00707 #                   <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property">
00708 #                   set_property()</a>.
00709 #
00710 # @returns Sets the specified property.
00711 #
00712 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:set_property
00713 #
00714 # @ingroup CMakeAPI
00715 function (basis_set_property SCOPE)
00716   if (SCOPE MATCHES "^TARGET$|^TEST$")
00717     # map target/test names to UIDs
00718     list (LENGTH ARGN ARGN_LENGTH)
00719     if (ARGN_LENGTH EQUAL 0)
00720       message (FATAL_ERROR "basis_set_property(${SCOPE}): Expected arguments after SCOPE!")
00721     endif ()
00722     set (IDX 0)
00723     set (ARG)
00724     while (IDX LESS ARGN_LENGTH)
00725       list (GET ARGN ${IDX} ARG)
00726       if (ARG MATCHES "^APPEND$")
00727         math (EXPR IDX "${IDX} + 1")
00728         list (GET ARGN ${IDX} ARG)
00729         if (NOT ARG MATCHES "^PROPERTY$")
00730           message (FATAL_ERROR "basis_set_properties(${SCOPE}): Expected PROPERTY keyword after APPEND!")
00731         endif ()
00732         break ()
00733       elseif (ARG MATCHES "^PROPERTY$")
00734         break ()
00735       else ()
00736         if (SCOPE MATCHES "^TEST$")
00737           basis_get_test_uid (UID "${ARG}")
00738         else ()
00739           basis_get_target_uid (UID "${ARG}")
00740         endif ()
00741         list (INSERT ARGN ${IDX} "${UID}")
00742         math (EXPR IDX "${IDX} + 1")
00743         list (REMOVE_AT ARGN ${IDX}) # after insert to avoid index out of range
00744       endif ()
00745     endwhile ()
00746     if (IDX EQUAL ARGN_LENGTH)
00747       message (FATAL_ERROR "basis_set_properties(${SCOPE}): Missing PROPERTY keyword!")
00748     endif ()
00749     math (EXPR IDX "${IDX} + 1")
00750     list (GET ARGN ${IDX} ARG)
00751     # property name matches DEPENDS
00752     if (ARG MATCHES "DEPENDS")
00753       math (EXPR IDX "${IDX} + 1")
00754       while (IDX LESS ARGN_LENGTH)
00755         list (GET ARGN ${IDX} ARG)
00756         if (SCOPE MATCHES "^TEST$")
00757           basis_get_test_uid (UID "${ARG}")
00758         else ()
00759           basis_get_target_uid (UID "${ARG}")
00760         endif ()
00761         list (INSERT ARGN ${IDX} "${UID}")
00762         math (EXPR IDX "${IDX} + 1")
00763         list (REMOVE_AT ARGN ${IDX}) # after insert ot avoid index out of range
00764       endwhile ()
00765     endif ()
00766   endif ()
00767   if (BASIS_DEBUG)
00768     message ("** basis_set_property():")
00769     message ("**   Scope:     ${SCOPE}")
00770     message ("**   Arguments: [${ARGN}]")
00771   endif ()
00772   set_property (${SCOPE} ${ARGN})
00773 endfunction ()
00774 
00775 # ----------------------------------------------------------------------------
00776 ## @brief Get a property.
00777 #
00778 # This function replaces CMake's
00779 # <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
00780 # get_property()</a> command.
00781 #
00782 # @param [out] VAR     Property value.
00783 # @param [in]  SCOPE   The argument for the @p SCOPE argument of
00784 #                      <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
00785 #                      get_property()</a>.
00786 # @param [in]  ELEMENT The argument for the @p ELEMENT argument of
00787 #                      <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
00788 #                      get_property()</a>.
00789 # @param [in]  ARGN    Arguments as accepted by
00790 #                      <a href="http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property">
00791 #                      get_property()</a>.
00792 #
00793 # @returns Sets @p VAR to the value of the requested property.
00794 #
00795 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:get_property
00796 #
00797 # @ingroup CMakeAPI
00798 function (basis_get_property VAR SCOPE ELEMENT)
00799   if (SCOPE MATCHES "^TARGET$")
00800     basis_get_target_uid (ELEMENT "${ELEMENT}")
00801   elseif (SCOPE MATCHES "^TEST$")
00802     basis_get_test_uid (ELEMENT "${ELEMENT}")
00803   endif ()
00804   get_property (VALUE ${SCOPE} ${ELEMENT} ${ARGN})
00805   set ("${VAR}" "${VALUE}" PARENT_SCOPE)
00806 endfunction ()
00807 
00808 # ----------------------------------------------------------------------------
00809 ## @brief Set project-global property.
00810 #
00811 # Set property associated with current project/module. The property is in
00812 # fact just a cached variable whose name is prefixed by the project's name.
00813 function (basis_set_project_property)
00814   CMAKE_PARSE_ARGUMENTS (
00815     ARGN
00816       "APPEND"
00817       "PROJECT"
00818       "PROPERTY"
00819     ${ARGN}
00820   )
00821 
00822   if (NOT ARGN_PROJECT)
00823     set (ARGN_PROJECT "${PROJECT_NAME}")
00824   endif ()
00825   if (NOT ARGN_PROPERTY)
00826     message (FATAL_ERROR "Missing PROPERTY argument!")
00827   endif ()
00828 
00829   list (GET ARGN_PROPERTY 0 PROPERTY_NAME)
00830   list (REMOVE_AT ARGN_PROPERTY 0) # remove property name from values
00831 
00832   if (ARGN_APPEND)
00833     basis_get_project_property (CURRENT PROPERTY ${PROPERTY_NAME})
00834     if (NOT "${CURRENT}" STREQUAL "")
00835       list (INSERT ARGN_PROPERTY 0 "${CURRENT}")
00836     endif ()
00837   endif ()
00838 
00839   set (
00840     ${ARGN_PROJECT}_${PROPERTY_NAME}
00841       "${ARGN_PROPERTY}"
00842     CACHE INTERNAL
00843       "Property ${PROPERTY_NAME} of project ${ARGN_PROJECT}."
00844     FORCE
00845   )
00846 endfunction ()
00847 
00848 # ----------------------------------------------------------------------------
00849 ## @brief Get project-global property value.
00850 #
00851 # Example:
00852 # @code
00853 # basis_get_project_property(TARGETS)
00854 # basis_get_project_property(TARGETS ${PROJECT_NAME})
00855 # basis_get_project_property(TARGETS ${PROJECT_NAME} TARGETS)
00856 # basis_get_project_property(TARGETS PROPERTY TARGETS)
00857 # @endcode
00858 #
00859 # @param [out] VARIABLE Name of result variable.
00860 # @param [in]  ARGN     See the example uses. The optional second argument
00861 #                       is either the name of the project similar to CMake's
00862 #                       get_target_property() command or the keyword PROPERTY
00863 #                       followed by the name of the property.
00864 function (basis_get_project_property VARIABLE)
00865   if (ARGC GREATER 3)
00866     message (FATAL_ERROR "Too many arguments!")
00867   endif ()
00868   if (ARGC EQUAL 1)
00869     set (ARGN_PROJECT "${PROJECT_NAME}")
00870     set (ARGN_PROPERTY "${VARIABLE}")
00871   elseif (ARGC EQUAL 2)
00872     if (ARGV1 MATCHES "^PROPERTY$")
00873       message (FATAL_ERROR "Expected argument after PROPERTY keyword!")
00874     endif ()
00875     set (ARGN_PROJECT  "${ARGV1}")
00876     set (ARGN_PROPERTY "${VARIABLE}")
00877   else ()
00878     if (ARGV1 MATCHES "^PROPERTY$")
00879       set (ARGN_PROJECT "${PROJECT_NAME}")
00880     else ()
00881       set (ARGN_PROJECT  "${ARGV1}")
00882     endif ()
00883     set (ARGN_PROPERTY "${ARGV2}")
00884   endif ()
00885   set (${VARIABLE} "${${ARGN_PROJECT}_${ARGN_PROPERTY}}" PARENT_SCOPE)
00886 endfunction ()
00887 
00888 # ============================================================================
00889 # list / string manipulations
00890 # ============================================================================
00891 
00892 # ----------------------------------------------------------------------------
00893 ## @brief Sanitize string variable for use in regular expression.
00894 #
00895 # @note This function may not work for all cases, but is used in particular
00896 #       to sanitize project names, target names, namespace identifiers,...
00897 #
00898 # @param [out] OUT String that can be used in regular expression.
00899 # @param [in]  STR String to sanitize.
00900 macro (basis_sanitize_for_regex OUT STR)
00901   string (REGEX REPLACE "([.+*?^$])" "\\\\\\1" ${OUT} "${STR}")
00902 endmacro ()
00903 
00904 # ----------------------------------------------------------------------------
00905 ## @brief Concatenates all list elements into a single string.
00906 #
00907 # The list elements are concatenated without any delimiter in between.
00908 # Use basis_list_to_delimited_string() to specify a delimiter such as a
00909 # whitespace character or comma (,) as delimiter.
00910 #
00911 # @param [out] STR  Output string.
00912 # @param [in]  ARGN Input list.
00913 #
00914 # @returns Sets @p STR to the resulting string.
00915 #
00916 # @sa basis_list_to_delimited_string()
00917 function (basis_list_to_string STR)
00918   set (OUT)
00919   foreach (ELEM ${ARGN})
00920     set (OUT "${OUT}${ELEM}")
00921   endforeach ()
00922   set ("${STR}" "${OUT}" PARENT_SCOPE)
00923 endfunction ()
00924 
00925 # ----------------------------------------------------------------------------
00926 ## @brief Concatenates all list elements into a single delimited string.
00927 #
00928 # @param [out] STR   Output string.
00929 # @param [in]  DELIM Delimiter used to separate list elements.
00930 #                    Each element which contains the delimiter as substring
00931 #                    is surrounded by double quotes (") in the output string.
00932 # @param [in]  ARGN  Input list.
00933 #
00934 # @returns Sets @p STR to the resulting string.
00935 function (basis_list_to_delimited_string STR DELIM)
00936   set (OUT)
00937   foreach (ELEM ${ARGN})
00938     if (OUT)
00939       set (OUT "${OUT}${DELIM}")
00940     endif ()
00941     if (ELEM MATCHES "${DELIM}")
00942       set (OUT "${OUT}\"${ELEM}\"")
00943     else ()
00944       set (OUT "${OUT}${ELEM}")
00945     endif ()
00946   endforeach ()
00947   set ("${STR}" "${OUT}" PARENT_SCOPE)
00948 endfunction ()
00949 
00950 # ----------------------------------------------------------------------------
00951 ## @brief Splits a string at space characters into a list.
00952 #
00953 # @todo Probably this can be done in a better way...
00954 #       Difficulty is, that string(REPLACE) does always replace all
00955 #       occurrences. Therefore, we need a regular expression which matches
00956 #       the entire string. More sophisticated regular expressions should do
00957 #       a better job, though.
00958 #
00959 # @param [out] LST  Output list.
00960 # @param [in]  STR  Input string.
00961 #
00962 # @returns Sets @p LST to the resulting CMake list.
00963 function (basis_string_to_list LST STR)
00964   set (TMP "${STR}")
00965   set (OUT)
00966   # 1. extract elements such as "a string with spaces"
00967   while (TMP MATCHES "\"[^\"]*\"")
00968     string (REGEX REPLACE "^(.*)\"([^\"]*)\"(.*)$" "\\1\\3" TMP "${TMP}")
00969     if (OUT)
00970       set (OUT "${CMAKE_MATCH_2};${OUT}")
00971     else (OUT)
00972       set (OUT "${CMAKE_MATCH_2}")
00973     endif ()
00974   endwhile ()
00975   # 2. extract other elements separated by spaces (excluding first and last)
00976   while (TMP MATCHES " [^\" ]+ ")
00977     string (REGEX REPLACE "^(.*) ([^\" ]+) (.*)$" "\\1\\3" TMP "${TMP}")
00978     if (OUT)
00979       set (OUT "${CMAKE_MATCH_2};${OUT}")
00980     else (OUT)
00981       set (OUT "${CMAKE_MATCH_2}")
00982     endif ()
00983   endwhile ()
00984   # 3. extract first and last elements (if not done yet)
00985   if (TMP MATCHES "^[^\" ]+")
00986     set (OUT "${CMAKE_MATCH_0};${OUT}")
00987   endif ()
00988   if (NOT "${CMAKE_MATCH_0}" STREQUAL "${TMP}" AND TMP MATCHES "[^\" ]+$")
00989     set (OUT "${OUT};${CMAKE_MATCH_0}")
00990   endif ()
00991   # return resulting list
00992   set (${LST} "${OUT}" PARENT_SCOPE)
00993 endfunction ()
00994 
00995 # ============================================================================
00996 # name <=> UID
00997 # ============================================================================
00998 
00999 # ----------------------------------------------------------------------------
01000 ## @brief Derive target name from source file name.
01001 #
01002 # @param [out] TARGET_NAME Target name.
01003 # @param [in]  SOURCE_FILE Source file.
01004 # @param [in]  COMPONENT   Third argument to get_filename_component().
01005 #
01006 # @returns Target name derived from @p SOURCE_FILE.
01007 function (basis_get_source_target_name TARGET_NAME SOURCE_FILE COMPONENT)
01008   # remove ".in" suffix from file name
01009   string (REGEX REPLACE "\\.in$" "" SOURCE_FILE "${SOURCE_FILE}")
01010   # get name component
01011   get_filename_component (OUT "${SOURCE_FILE}" ${COMPONENT})
01012   # replace special characters
01013   string (REPLACE "." "_" OUT "${OUT}")
01014   # return
01015   set (${TARGET_NAME} "${OUT}" PARENT_SCOPE)
01016 endfunction ()
01017 
01018 # ----------------------------------------------------------------------------
01019 ## @brief Make target UID from given target name.
01020 #
01021 # This function is intended for use by the basis_add_*() functions only.
01022 #
01023 # @param [out] TARGET_UID  "Global" target name, i.e., actual CMake target name.
01024 # @param [in]  TARGET_NAME Target name used as argument to BASIS CMake functions.
01025 #
01026 # @returns Sets @p TARGET_UID to the UID of the build target @p TARGET_NAME.
01027 #
01028 # @sa basis_get_target_uid()
01029 macro (basis_make_target_uid TARGET_UID TARGET_NAME)
01030   set ("${TARGET_UID}" "${PROJECT_NAMESPACE_CMAKE}.${TARGET_NAME}")
01031   # strip off top-level namespace part (optional)
01032   if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
01033     string (
01034       REGEX REPLACE
01035         "^${BASIS_PROJECT_NAMESPACE_CMAKE_REGEX}\\."
01036         ""
01037       "${TARGET_UID}"
01038         "${${TARGET_UID}}"
01039     )
01040   endif ()
01041 endmacro ()
01042 
01043 # ----------------------------------------------------------------------------
01044 ## @brief Get "global" target name, i.e., actual CMake target name.
01045 #
01046 # In order to ensure that CMake target names are unique across modules of
01047 # a BASIS project, the target name given to the BASIS CMake functions is
01048 # converted by basis_make_target_uid() into a so-called target UID which is
01049 # used as actual CMake target name. This function can be used to get for a
01050 # given target name or UID the closest match of a known target UID.
01051 #
01052 # In particular, if this project is a module of another BASIS project, the
01053 # namespace given by @c PROJECT_NAMESPACE_CMAKE is used as prefix, where the
01054 # namespace prefix and the build target name are separated by a dot (.).
01055 # Otherwise, if this project is the top-level project, no namespace prefix is
01056 # used and if the project's namespace is given as prefix, it will be removed.
01057 # When the target is exported, however, the namespace of this project will be
01058 # prefixed again. This is done by the basis_export_targets() function.
01059 #
01060 # Note that names of imported targets are not prefixed in any case.
01061 #
01062 # The counterpart basis_get_target_name() can be used to convert the target UID
01063 # back to the target name without namespace prefix.
01064 #
01065 # @note At the moment, BASIS does not support modules which themselves have
01066 #       modules again. This would require a more nested namespace hierarchy.
01067 #
01068 # @param [out] TARGET_UID  "Global" target name, i.e., actual CMake target name.
01069 # @param [in]  TARGET_NAME Target name used as argument to BASIS CMake functions.
01070 #
01071 # @returns Sets @p TARGET_UID to the UID of the build target @p TARGET_NAME.
01072 #
01073 # @sa basis_get_target_name()
01074 function (basis_get_target_uid TARGET_UID TARGET_NAME)
01075   # in case of a leading namespace separator, do not modify target name
01076   if (TARGET_NAME MATCHES "^\\.")
01077     set (UID "${TARGET_NAME}")
01078   # otherwise,
01079   else ()
01080     set (UID "${TARGET_NAME}")
01081     # try prepending namespace or parts of it until target is known
01082     if (BASIS_DEBUG AND BASIS_VERBOSE)
01083       message ("** basis_get_target_uid()")
01084     endif ()
01085     set (PREFIX "${PROJECT_NAMESPACE_CMAKE}")
01086     if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
01087       string (
01088         REGEX REPLACE
01089           "^${BASIS_PROJECT_NAMESPACE_CMAKE_REGEX}\\."
01090           ""
01091         PREFIX
01092           "${PREFIX}"
01093       )
01094     endif ()
01095     while (PREFIX)
01096       if (BASIS_DEBUG AND BASIS_VERBOSE)
01097         message ("**     Trying: ${PREFIX}.${TARGET_NAME}")
01098       endif ()
01099       if (TARGET "${PREFIX}.${TARGET_NAME}")
01100         set (UID "${PREFIX}.${TARGET_NAME}")
01101         break ()
01102       else ()
01103         if (PREFIX MATCHES "(.*)\\.[^.]+")
01104           set (PREFIX "${CMAKE_MATCH_1}")
01105         else ()
01106           break ()
01107         endif ()
01108       endif ()
01109     endwhile ()
01110   endif ()
01111   # strip off top-level namespace part (optional)
01112   if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
01113     string (
01114       REGEX REPLACE
01115         "^${BASIS_PROJECT_NAMESPACE_CMAKE_REGEX}\\."
01116         ""
01117       UID
01118         "${UID}"
01119     )
01120   endif ()
01121   # return
01122   if (BASIS_DEBUG AND BASIS_VERBOSE)
01123     message ("** basis_get_target_uid(): ${TARGET_NAME} -> ${UID}")
01124   endif ()
01125   set ("${TARGET_UID}" "${UID}" PARENT_SCOPE)
01126 endfunction ()
01127 
01128 # ----------------------------------------------------------------------------
01129 ## @brief Get fully-qualified target name.
01130 #
01131 # This function always returns a fully-qualified target UID, no matter if
01132 # the option @c BASIS_USE_FULLY_QUALIFIED_UIDS is @c OFF. Note that
01133 # if this option is @c ON, the returned target UID is may not be the
01134 # actual name of a CMake target.
01135 #
01136 # @param [out] TARGET_UID  Fully-qualified target UID.
01137 # @param [in]  TARGET_NAME Target name used as argument to BASIS CMake functions.
01138 #
01139 # @sa basis_get_target_uid()
01140 function (basis_get_fully_qualified_target_uid TARGET_UID TARGET_NAME)
01141   basis_get_target_uid (UID "${TARGET_NAME}")
01142   if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
01143     get_target_property (IMPORTED "${UID}" IMPORTED)
01144     if (NOT IMPORTED)
01145       set (UID "${BASIS_PROJECT_NAMESPACE_CMAKE}.${UID}")
01146     endif ()
01147   endif ()
01148   set ("${TARGET_UID}" "${UID}" PARENT_SCOPE)
01149 endfunction ()
01150 
01151 # ----------------------------------------------------------------------------
01152 ## @brief Get namespace of build target.
01153 #
01154 # @param [out] TARGET_NS  Namespace part of target UID.
01155 # @param [in]  TARGET_UID Target UID/name.
01156 function (basis_get_target_namespace TARGET_NS TARGET_UID)
01157   # make sure we have a fully-qualified target UID
01158   basis_get_fully_qualified_target_uid (UID "${TARGET_UID}")
01159   # return namespace part
01160   if (UID MATCHES "^(.*)\\.")
01161     set ("${TARGET_NS}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
01162   else ()
01163     set ("${TARGET_NS}" "" PARENT_SCOPE)
01164   endif ()
01165 endfunction ()
01166 
01167 # ----------------------------------------------------------------------------
01168 ## @brief Get "local" target name, i.e., BASIS target name.
01169 #
01170 # @param [out] TARGET_NAME Target name used as argument to BASIS functions.
01171 # @param [in]  TARGET_UID  "Global" target name, i.e., actual CMake target name.
01172 #
01173 # @returns Sets @p TARGET_NAME to the name of the build target with UID @p TARGET_UID.
01174 #
01175 # @sa basis_get_target_uid()
01176 function (basis_get_target_name TARGET_NAME TARGET_UID)
01177   # make sure we have a fully-qualified target UID
01178   basis_get_fully_qualified_target_uid (UID "${TARGET_UID}")
01179   # strip off namespace of current project
01180   string (REGEX REPLACE "^${PROJECT_NAMESPACE_CMAKE_REGEX}\\." "" NAME "${UID}")
01181   # return
01182   if (BASIS_DEBUG AND BASIS_VERBOSE)
01183     message ("** basis_get_target_name(): ${UID} -> ${NAME}")
01184   endif ()
01185   set ("${TARGET_NAME}" "${NAME}" PARENT_SCOPE)
01186 endfunction ()
01187 
01188 # ----------------------------------------------------------------------------
01189 ## @brief Checks whether a given name is a valid target name.
01190 #
01191 # Displays fatal error message when target name is invalid.
01192 #
01193 # @param [in] TARGET_NAME Desired target name.
01194 #
01195 # @returns Nothing.
01196 function (basis_check_target_name TARGET_NAME)
01197   # reserved target name ?
01198   list (FIND BASIS_RESERVED_TARGET_NAMES "${TARGET_NAME}" IDX)
01199   if (NOT IDX EQUAL -1)
01200     message (FATAL_ERROR "Target name \"${TARGET_NAME}\" is reserved and cannot be used.")
01201   endif ()
01202 
01203   # invalid target name ?
01204   if (NOT TARGET_NAME MATCHES "^[a-zA-Z]([a-zA-Z0-9_+]|-)*$|^__init__(_py)?$")
01205     message (FATAL_ERROR "Target name '${TARGET_NAME}' is invalid.\nChoose a target name"
01206                          " which only contains alphanumeric characters,"
01207                          " '_', '-', or '+', and starts with a letter."
01208                          " The only exception from this rule is __init__[_py] for"
01209                          " a __init__.py script.\n")
01210   endif ()
01211 
01212   # unique ?
01213   basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
01214 
01215   if (TARGET "${TARGET_UID}")
01216     message (FATAL_ERROR "There exists already a target named ${TARGET_UID}."
01217                          " Target names must be unique.")
01218   endif ()
01219 endfunction ()
01220 
01221 # ----------------------------------------------------------------------------
01222 ## @brief Make test UID from given test name.
01223 #
01224 # This function is intended for use by the basis_add_test() only.
01225 #
01226 # @param [out] TEST_UID  "Global" test name, i.e., actual CTest test name.
01227 # @param [in]  TEST_NAME Test name used as argument to BASIS CMake functions.
01228 #
01229 # @returns Sets @p TEST_UID to the UID of the test @p TEST_NAME.
01230 #
01231 # @sa basis_get_test_uid()
01232 macro (basis_make_test_uid TEST_UID TEST_NAME)
01233   basis_make_target_uid ("${TEST_UID}" "${TEST_NAME}")
01234 endmacro ()
01235 
01236 # ----------------------------------------------------------------------------
01237 ## @brief Get "global" test name, i.e., actual CTest test name.
01238 #
01239 # In order to ensure that CTest test names are unique across BASIS projects,
01240 # the test name used by a developer of a BASIS project is converted by this
01241 # function into another test name which is used as acutal CTest test name.
01242 #
01243 # The function basis_get_test_name() can be used to convert the unique test
01244 # name, the test UID, back to the original test name passed to this function.
01245 #
01246 # @param [out] TEST_UID  "Global" test name, i.e., actual CTest test name.
01247 # @param [in]  TEST_NAME Test name used as argument to BASIS CMake functions.
01248 #
01249 # @returns Sets @p TEST_UID to the UID of the test @p TEST_NAME.
01250 #
01251 # @sa basis_get_test_name()
01252 macro (basis_get_test_uid TEST_UID TEST_NAME)
01253   if (TEST_NAME MATCHES "\\.")
01254     set ("${TEST_UID}" "${TEST_NAME}")
01255   else ()
01256     set ("${TEST_UID}" "${PROJECT_NAMESPACE_CMAKE}.${TEST_NAME}")
01257   endif ()
01258   # strip off top-level namespace part (optional)
01259   if (NOT BASIS_USE_FULLY_QUALIFIED_UIDS)
01260     string (
01261       REGEX REPLACE
01262         "^${BASIS_PROJECT_NAMESPACE_CMAKE_REGEX}\\."
01263         ""
01264       "${TEST_UID}"
01265         "${${TEST_UID}}"
01266     )
01267   endif ()
01268 endmacro ()
01269 
01270 # ----------------------------------------------------------------------------
01271 ## @brief Get "global" test name, i.e., actual CTest test name.
01272 #
01273 # This function always returns a fully-qualified test UID, no matter if
01274 # the option @c BASIS_USE_FULLY_QUALIFIED_UIDS is @c OFF. Note that
01275 # if this option is @c ON, the returned test UID may not be the
01276 # actual name of a CMake test.
01277 #
01278 # @param [out] TEST_UID  Fully-qualified test UID.
01279 # @param [in]  TEST_NAME Test name used as argument to BASIS CMake functions.
01280 #
01281 # @sa basis_get_test_uid()
01282 macro (basis_get_fully_qualified_test_uid TEST_UID TEST_NAME)
01283   if (TEST_NAME MATCHES "\\.")
01284     set ("${TEST_UID}" "${TEST_NAME}")
01285   else ()
01286     set ("${TEST_UID}" "${PROJECT_NAMESPACE_CMAKE}.${TEST_NAME}")
01287   endif ()
01288 endmacro ()
01289 
01290 # ----------------------------------------------------------------------------
01291 ## @brief Get namespace of test.
01292 #
01293 # @param [out] TEST_NS  Namespace part of test UID. If @p TEST_UID is
01294 #                       no UID, i.e., does not contain a namespace part,
01295 #                       the namespace of this project is returned.
01296 # @param [in]  TEST_UID Test UID/name.
01297 macro (basis_get_test_namespace TEST_NS TEST_UID)
01298   if (TEST_UID MATCHES "^(.*)\\.")
01299     set ("${TEST_NS}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
01300   else ()
01301     set ("${TEST_NS}" "" PARENT_SCOPE)
01302   endif ()
01303 endmacro ()
01304 
01305 # ----------------------------------------------------------------------------
01306 ## @brief Get "local" test name, i.e., BASIS test name.
01307 #
01308 # @param [out] TEST_NAME Test name used as argument to BASIS functions.
01309 # @param [in]  TEST_UID  "Global" test name, i.e., actual CTest test name.
01310 #
01311 # @returns Sets @p TEST_NAME to the name of the test with UID @p TEST_UID.
01312 #
01313 # @sa basis_get_test_uid()
01314 macro (basis_get_test_name TEST_NAME TEST_UID)
01315   if (TEST_UID MATCHES "([^.]+)$")
01316     set ("${TEST_NAME}" "${CMAKE_MATCH_1}" PARENT_SCOPE)
01317   else ()
01318     set ("${TEST_NAME}" "" PARENT_SCOPE)
01319   endif ()
01320 endmacro ()
01321 
01322 # ----------------------------------------------------------------------------
01323 ## @brief Checks whether a given name is a valid test name.
01324 #
01325 # Displays fatal error message when test name is invalid.
01326 #
01327 # @param [in] TEST_NAME Desired test name.
01328 #
01329 # @returns Nothing.
01330 function (basis_check_test_name TEST_NAME)
01331   list (FIND BASIS_RESERVED_TEST_NAMES "${TEST_NAME}" IDX)
01332   if (NOT IDX EQUAL -1)
01333     message (FATAL_ERROR "Test name \"${TEST_NAME}\" is reserved and cannot be used.")
01334   endif ()
01335 
01336   if (NOT TEST_NAME MATCHES "^[a-zA-Z]([a-zA-Z0-9_+]|-)*$")
01337     message (FATAL_ERROR "Test name ${TEST_NAME} is invalid.\nChoose a test name "
01338                          " which only contains alphanumeric characters,"
01339                          " '_', '-', or '+', and starts with a letter.\n")
01340   endif ()
01341 endfunction ()
01342 
01343 # ============================================================================
01344 # common target tools
01345 # ============================================================================
01346 
01347 # ----------------------------------------------------------------------------
01348 ## @brief Detect programming language of given source code files.
01349 #
01350 # This function determines the programming language in which the given source
01351 # code files are written. If no common programming language could be determined,
01352 # "AMBIGUOUS" is returned. If none of the following programming languages
01353 # could be determined, "UNKNOWN" is returned: CXX (i.e., C++), JAVA,
01354 # JAVASCRIPT, PYTHON, PERL, BASH, BATCH, MATLAB.
01355 #
01356 # @param [out] LANGUAGE Detected programming language.
01357 # @param [in]  ARGN     List of source code files.
01358 function (basis_get_source_language LANGUAGE)
01359   set (LANGUAGE_OUT)
01360   # iterate over source files
01361   foreach (SOURCE_FILE ${ARGN})
01362     # ignore .in suffix
01363     string (REGEX REPLACE "\\.in$" "" SOURCE_FILE "${SOURCE_FILE}")
01364     # C++
01365     if (SOURCE_FILE MATCHES "\\.(c|cc|cpp|cxx|h|hpp|hxx|txx|inl)$")
01366       set (LANG "CXX")
01367     # Java
01368     elseif (SOURCE_FILE MATCHES "\\.java$")
01369       set (LANG "JAVA")
01370     # JavaScript
01371     elseif (SOURCE_FILE MATCHES "\\.js$")
01372       set (LANG "JAVASCRIPT")
01373     # Python
01374     elseif (SOURCE_FILE MATCHES "\\.py$")
01375       set (LANG "PYTHON")
01376     # Perl
01377     elseif (SOURCE_FILE MATCHES "\\.(pl|pm|t)$")
01378       set (LANG "PERL")
01379     # BASH
01380     elseif (SOURCE_FILE MATCHES "\\.sh$")
01381       set (LANG "BASH")
01382     # Batch
01383     elseif (SOURCE_FILE MATCHES "\\.bat$")
01384       set (LANG "BATCH")
01385     # MATLAB
01386     elseif (SOURCE_FILE MATCHES "\\.m$")
01387       set (LANG "MATLAB")
01388     # unknown
01389     else ()
01390       set (LANGUAGE_OUT "UNKNOWN")
01391       break ()
01392     endif ()
01393     # detect ambiguity
01394     if (LANGUAGE_OUT AND NOT LANG MATCHES "${LANGUAGE_OUT}")
01395       if (LANGUAGE_OUT MATCHES "CXX" AND LANG MATCHES "MATLAB")
01396         # MATLAB Compiler can handle this...
01397       elseif (LANGUAGE_OUT MATCHES "MATLAB" AND LANG MATCHES "CXX")
01398         # language stays MATLAB
01399         set (LANG "MATLAB")
01400       else ()
01401         # ambiguity
01402         set (LANGUAGE_OUT "AMBIGUOUS")
01403         break ()
01404       endif ()
01405     endif ()
01406     # update current language
01407     set (LANGUAGE_OUT "${LANG}")
01408   endforeach ()
01409   # return
01410   set (${LANGUAGE} "${LANGUAGE_OUT}" PARENT_SCOPE)
01411 endfunction ()
01412 
01413 # ----------------------------------------------------------------------------
01414 ## @brief Configure .in source files.
01415 #
01416 # This function configures each source file in the given argument list with
01417 # a .in file name suffix and stores the configured file in the build tree
01418 # with the same relative directory as the template source file itself.
01419 # The first argument names the CMake variable of the list of configured
01420 # source files where each list item is the absolute file path of the
01421 # corresponding (configured) source file.
01422 #
01423 # @param [out] LIST_NAME Name of output list.
01424 # @param [in]  ARGN      These arguments are parsed and the following
01425 #                        options recognized. All remaining arguments are
01426 #                        considered to be source file paths.
01427 # @par
01428 # <table border="0">
01429 #   <tr>
01430 #     @tp @b BINARY_DIRECTORY @endtp
01431 #     <td>Explicitly specify directory in build tree where configured
01432 #         source files should be written to.</td>
01433 #   </tr>
01434 #   <tr>
01435 #     @tp @b KEEP_DOT_IN_SUFFIX @endtp
01436 #     <td>By default, after a source file with the .in extension has been
01437 #         configured, the .in suffix is removed from the file name.
01438 #         This can be omitted by giving this option.</td>
01439 #   </tr>
01440 # </table>
01441 #
01442 # @returns Nothing.
01443 function (basis_configure_sources LIST_NAME)
01444   # parse arguments
01445   CMAKE_PARSE_ARGUMENTS (ARGN "KEEP_DOT_IN_SUFFIX" "BINARY_DIRECTORY" "" ${ARGN})
01446 
01447   if (ARGN_BINARY_DIRECTORY AND NOT ARGN_BINARY_DIRECTORY MATCHES "^${PROJECT_BINARY_DIR}")
01448     message (FATAL_ERROR "Specified BINARY_DIRECTORY must be inside the build tree!")
01449   endif ()
01450 
01451   # configure source files
01452   set (CONFIGURED_SOURCES)
01453   foreach (SOURCE ${ARGN_UNPARSED_ARGUMENTS})
01454     # make path absolute, otherwise EXISTS check will not work
01455     if (NOT IS_ABSOLUTE "${SOURCE}")
01456       set (SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE}")
01457     endif ()
01458     # the .in suffix is optional, add it here if a .in file exists for this
01459     # source file, but only if the source file itself does not name an acutally
01460     # existing source file
01461     if (NOT SOURCE MATCHES "\\.in$" AND NOT EXISTS "${SOURCE}" AND EXISTS "${SOURCE}.in")
01462       set (SOURCE "${SOURCE}.in")
01463     endif ()
01464     if (SOURCE MATCHES "\\.in$")
01465       # if binary directory was given explicitly, use it
01466       if (ARGN_BINARY_DIRECTORY)
01467         get_filename_component (SOURCE_NAME "${SOURCE}" NAME)
01468         if (NOT ARGN_KEEP_DOT_IN_SUFFIX)
01469           string (REGEX REPLACE "\\.in$" "" SOURCE_NAME "${SOURCE_NAME}")
01470         endif ()
01471         set (CONFIGURED_SOURCE "${ARGN_BINARY_DIRECTORY}/${SOURCE_NAME}")
01472       # otherwise,
01473       else ()
01474         # if source is in project's source tree use relative binary directory
01475         basis_sanitize_for_regex (REGEX "${PROJECT_SOURCE_DIR}")
01476         if (SOURCE MATCHES "^${REGEX}")
01477           basis_get_relative_path (CONFIGURED_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}" "${SOURCE}")
01478           get_filename_component (CONFIGURED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${CONFIGURED_SOURCE}" ABSOLUTE)
01479           if (NOT ARGN_KEEP_DOT_IN_SUFFIX)
01480             string (REGEX REPLACE "\\.in$" "" CONFIGURED_SOURCE "${CONFIGURED_SOURCE}")
01481           endif ()
01482         # otherwise, use current binary directory
01483         else ()
01484           get_filename_component (SOURCE_NAME "${SOURCE}" NAME)
01485           if (NOT ARGN_KEEP_DOT_IN_SUFFIX)
01486             string (REGEX REPLACE "\\.in$" "" SOURCE_NAME "${SOURCE_NAME}")
01487           endif ()
01488           set (CONFIGURED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE_NAME}")
01489         endif ()
01490       endif ()
01491       # configure source file
01492       configure_file ("${SOURCE}" "${CONFIGURED_SOURCE}" @ONLY)
01493       if (BASIS_DEBUG)
01494         message ("** Configured source file with .in extension")
01495         message ("**     Source:            ${SOURCE}")
01496         message ("**     Configured source: ${CONFIGURED_SOURCE}")
01497       endif ()
01498     else ()
01499       # if the source file path is relative, prefer possibly already
01500       # configured sources in build tree such as the test driver source file
01501       # created by create_test_sourcelist() or a manual use of configure_file()
01502       if (IS_ABSOLUTE "${SOURCE}")
01503         set (CONFIGURED_SOURCE "${SOURCE}")
01504       else ()
01505         if (EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}")
01506           set (CONFIGURED_SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}")
01507         else ()
01508           get_filename_component (CONFIGURED_SOURCE "${SOURCE}" ABSOLUTE)
01509         endif ()
01510       endif ()
01511       if (BASIS_DEBUG)
01512         message ("** Skipped configuration of source file")
01513         message ("**     Source:            ${SOURCE}")
01514         message ("**     Configured source: ${CONFIGURED_SOURCE}")
01515       endif ()
01516     endif ()
01517     list (APPEND CONFIGURED_SOURCES "${CONFIGURED_SOURCE}")
01518   endforeach ()
01519   # return
01520   set (${LIST_NAME} "${CONFIGURED_SOURCES}" PARENT_SCOPE)
01521 endfunction ()
01522 
01523 # ----------------------------------------------------------------------------
01524 ## @brief Get type name of target.
01525 #
01526 # @param [out] TYPE        The target's type name or NOTFOUND.
01527 # @param [in]  TARGET_NAME The name of the target.
01528 function (basis_get_target_type TYPE TARGET_NAME)
01529   basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
01530   if (TARGET ${TARGET_UID})
01531     get_target_property (TYPE_OUT ${TARGET_UID} "BASIS_TYPE")
01532     if (NOT TYPE_OUT)
01533       # in particular imported targets may not have a BASIS_TYPE property
01534       get_target_property (TYPE_OUT ${TARGET_UID} "TYPE")
01535     endif ()
01536   else ()
01537     set (TYPE_OUT "NOTFOUND")
01538   endif ()
01539   set ("${TYPE}" "${TYPE_OUT}" PARENT_SCOPE)
01540 endfunction ()
01541 
01542 # ----------------------------------------------------------------------------
01543 ## @brief Get location of build target output file.
01544 #
01545 # This convenience function can be used to get the full path of the output
01546 # file generated by a given build target. It is similar to the read-only
01547 # @c LOCATION property of CMake targets and should be used instead of
01548 # reading this porperty.
01549 #
01550 # @param [out] VAR         Path of build target output file.
01551 # @param [in]  TARGET_NAME Name of build target.
01552 # @param [in]  PART        Which file name component of the @c LOCATION
01553 #                          property to return. See get_filename_component().
01554 #                          If POST_INSTALL_RELATIVE is given as argument,
01555 #                          @p VAR is set to the path of the installed file
01556 #                          relative to the installation prefix. Similarly,
01557 #                          POST_INSTALL sets @p VAR to the absolute path
01558 #                          of the installed file post installation.
01559 #
01560 # @returns Path of output file similar to @c LOCATION property of CMake targets.
01561 #
01562 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#prop_tgt:LOCATION
01563 function (basis_get_target_location VAR TARGET_NAME PART)
01564   basis_get_target_uid (TARGET_UID "${TARGET_NAME}")
01565   if (TARGET "${TARGET_UID}")
01566     basis_get_target_name (TARGET_NAME "${TARGET_UID}")
01567     basis_get_target_type (TYPE        "${TARGET_UID}")
01568     get_target_property (IMPORTED ${TARGET_UID} "IMPORTED")
01569 
01570     # ------------------------------------------------------------------------
01571     # imported custom targets
01572     #
01573     # Note: This might not be required though as even custom executable
01574     #       and library targets can be imported using CMake's
01575     #       add_executable(<NAME> IMPORTED) and add_library(<NAME> <TYPE> IMPORTED)
01576     #       commands. Such executable can, for example, also be a BASH
01577     #       script built by basis_add_script().
01578 
01579     if (IMPORTED)
01580 
01581       # 1. Try IMPORTED_LOCATION_<CMAKE_BUILD_TYPE>
01582       if (CMAKE_BUILD_TYPE)
01583         string (TOUPPER "${CMAKE_BUILD_TYPE}" U)
01584       else ()
01585         set (U "NOCONFIG")
01586       endif ()
01587       get_target_property (LOCATION ${TARGET_UID} "IMPORTED_LOCATION_${U}")
01588       # 2. Try IMPORTED_LOCATION
01589       if (NOT LOCATION)
01590         get_target_property (LOCATION ${TARGET_UID} "IMPORTED_LOCATION")
01591       endif ()
01592       # 3. Prefer Release over all other configurations
01593       if (NOT LOCATION)
01594         get_target_property (LOCATION ${TARGET_UID} "IMPORTED_LOCATION_RELEASE")
01595       endif ()
01596       # 4. Just use any of the imported configurations
01597       if (NOT LOCATION)
01598         get_property (CONFIGS TARGET "${TARGET_UID}" PROPERTY IMPORTED_CONFIGURATIONS)
01599         foreach (C IN LISTS CONFIGS)
01600           get_target_property (LOCATION ${TARGET_UID} "IMPORTED_LOCATION_${C}")
01601           if (LOCATION)
01602             break ()
01603           endif ()
01604         endforeach ()
01605       endif ()
01606 
01607       # Make path relative to INSTALL_PREFIX if POST_INSTALL_PREFIX given
01608       if (LOCATION AND ARGV2 MATCHES "POST_INSTALL_RELATIVE")
01609         file (RELATIVE_PATH LOCATION "${INSTALL_PREFIX}" "${LOCATION}")
01610       endif ()
01611 
01612     # ------------------------------------------------------------------------
01613     # non-imported custom targets
01614 
01615     else ()
01616 
01617       # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01618       # libraries
01619 
01620       if (TYPE MATCHES "LIBRARY|MEX")
01621 
01622         if (TYPE MATCHES "STATIC")
01623           if (PART MATCHES "^POST_INSTALL$|^POST_INSTALL_RELATIVE$")
01624             get_target_property (DIRECTORY "${TARGET_UID}" "ARCHIVE_INSTALL_DIRECTORY")
01625           endif ()
01626           if (NOT DIRECTORY)
01627             get_target_property (DIRECTORY "${TARGET_UID}" "ARCHIVE_OUTPUT_DIRECTORY")
01628           endif ()
01629           get_target_property (FNAME     "${TARGET_UID}" "ARCHIVE_OUTPUT_NAME")
01630         else ()
01631           if (PART MATCHES "^POST_INSTALL$|^POST_INSTALL_RELATIVE$")
01632             get_target_property (DIRECTORY "${TARGET_UID}" "LIBRARY_INSTALL_DIRECTORY")
01633           endif ()
01634           if (NOT DIRECTORY)
01635             get_target_property (DIRECTORY "${TARGET_UID}" "LIBRARY_OUTPUT_DIRECTORY")
01636           endif ()
01637           get_target_property (FNAME "${TARGET_UID}" "LIBRARY_OUTPUT_NAME")
01638         endif ()
01639 
01640       # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
01641       # executables
01642 
01643       else ()
01644 
01645         if (PART MATCHES "^POST_INSTALL$|^POST_INSTALL_RELATIVE$")
01646           get_target_property (DIRECTORY "${TARGET_UID}" "RUNTIME_INSTALL_DIRECTORY")
01647         endif ()
01648         if (NOT DIRECTORY)
01649           get_target_property (DIRECTORY "${TARGET_UID}" "RUNTIME_OUTPUT_DIRECTORY")
01650         endif ()
01651         get_target_property (FNAME "${TARGET_UID}" "RUNTIME_OUTPUT_NAME")
01652       endif ()
01653       if (NOT FNAME)
01654         get_target_property (FNAME "${TARGET_UID}" "OUTPUT_NAME")
01655       endif ()
01656       get_target_property (PREFIX "${TARGET_UID}" "PREFIX")
01657       get_target_property (SUFFIX "${TARGET_UID}" "SUFFIX")
01658 
01659       if (FNAME)
01660         set (TARGET_FILE "${FNAME}")
01661       else ()
01662         set (TARGET_FILE "${TARGET_NAME}")
01663       endif ()
01664       if (PREFIX)
01665         set (TARGET_FILE "${PREFIX}${TARGET_FILE}")
01666       endif ()
01667       if (SUFFIX)
01668         set (TARGET_FILE "${TARGET_FILE}${SUFFIX}")
01669       elseif (WIN32 AND TYPE MATCHES "^EXECUTABLE$")
01670         set (TARGET_FILE "${TARGET_FILE}.exe")
01671       endif ()
01672 
01673       if (PART MATCHES "^POST_INSTALL$")
01674         if (NOT IS_ABSOLUTE "${DIRECTORY}")
01675           set (DIRECTORY "${INSTALL_PREFIX}/${DIRECTORY}")
01676         endif ()
01677       elseif (PART MATCHES "^POST_INSTALL_RELATIVE$")
01678         if (IS_ABSOLUTE "${DIRECTORY}")
01679           file (RELATIVE_PATH DIRECTORY "${INSTALL_PREFIX}" "${DIRECTORY}")
01680           if (NOT DIRECTORY)
01681             set (DIRECTORY ".")
01682           endif ()
01683         endif ()
01684       endif ()
01685 
01686       set (LOCATION "${DIRECTORY}/${TARGET_FILE}")
01687 
01688     endif ()
01689 
01690     # get filename component
01691     if (NOT PART MATCHES "^POST_INSTALL$|^POST_INSTALL_RELATIVE$")
01692       get_filename_component (LOCATION "${LOCATION}" "${PART}")
01693     endif ()
01694 
01695   else ()
01696     message (FATAL_ERROR "basis_get_target_location(): Unknown target ${TARGET_UID}")
01697   endif ()
01698 
01699   # return
01700   set ("${VAR}" "${LOCATION}" PARENT_SCOPE)
01701 endfunction ()
01702 
01703 # ============================================================================
01704 # generator expressions
01705 # ============================================================================
01706 
01707 # ----------------------------------------------------------------------------
01708 ## @brief Process generator expressions in arguments.
01709 #
01710 # This command evaluates the $&lt;TARGET_FILE:tgt&gt; and related generator
01711 # expressions also for custom targets such as scripts and MATLAB Compiler
01712 # targets. For other generator expressions whose argument is a target name,
01713 # this function replaces the target name by the target UID, i.e., the actual
01714 # CMake target name such that the expression can be evaluated by CMake.
01715 # The following generator expressions are directly evaluated by this function:
01716 # <table border=0>
01717 #   <tr>
01718 #     @tp <b><tt>$&lt;TARGET_FILE:tgt&gt;</tt></b> @endtp
01719 #     <td>Absolute file path of built target.</td>
01720 #   </tr>
01721 #   <tr>
01722 #     @tp <b><tt>$&lt;TARGET_FILE_POST_INSTALL:tgt&gt;</tt></b> @endtp
01723 #     <td>Absolute path of target file after installation using the
01724 #         current @c INSTALL_PREFIX.</td>
01725 #   </tr>
01726 #   <tr>
01727 #     @tp <b><tt>$&lt;TARGET_FILE_POST_INSTALL_RELATIVE:tgt&gt;</tt></b> @endtp
01728 #     <td>Path of target file after installation relative to @c INSTALL_PREFIX.</td>
01729 #   </tr>
01730 # </table>
01731 # Additionally, the suffix <tt>_NAME</tt> or <tt>_DIR</tt> can be appended
01732 # to the name of each of these generator expressions to get only the basename
01733 # of the target file including the extension or the corresponding directory
01734 # path, respectively.
01735 #
01736 # Generator expressions are in particular supported by basis_add_test().
01737 #
01738 # @param [out] ARGS Name of output list variable.
01739 # @param [in]  ARGN List of arguments to process.
01740 #
01741 # @sa basis_add_test()
01742 # @sa http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:add_test
01743 function (basis_process_generator_expressions ARGS)
01744   set (ARGS_OUT)
01745   foreach (ARG IN LISTS ARGN)
01746     string (REGEX MATCHALL "\\$<.*TARGET.*:.*>" EXPRS "${ARG}")
01747     foreach (EXPR IN LISTS EXPRS)
01748       if (EXPR MATCHES "\\$<(.*):(.*)>")
01749         set (EXPR_NAME   "${CMAKE_MATCH_1}")
01750         set (TARGET_NAME "${CMAKE_MATCH_2}")
01751         # TARGET_FILE* expression, including custom targets
01752         if (EXPR_NAME MATCHES "^TARGET_FILE(.*)")
01753           if (NOT CMAKE_MATCH_1)
01754             set (CMAKE_MATCH_1 "ABSOLUTE")
01755           endif ()
01756           string (REGEX REPLACE "^_" "" PART "${CMAKE_MATCH_1}")
01757           basis_get_target_location (ARG "${TARGET_NAME}" ${PART})
01758         # other generator expression supported by CMake
01759         # only replace target name, but do not evaluate expression
01760         else ()
01761           basis_get_target_uid (TARGET_UID "${CMAKE_MATCH_2}")
01762           string (REPLACE "${EXPR}" "$<${CMAKE_MATCH_1}:${TARGET_UID}>" ARG "${ARG}")
01763         endif ()
01764         if (BASIS_DEBUG AND BASIS_VERBOSE)
01765           message ("** basis_process_generator_expressions():")
01766           message ("**   Expression:  ${EXPR}")
01767           message ("**   Keyword:     ${EXPR_NAME}")
01768           message ("**   Argument:    ${TARGET_NAME}")
01769           message ("**   Replaced by: ${ARG}")
01770         endif ()
01771       endif ()
01772     endforeach ()
01773     list (APPEND ARGS_OUT "${ARG}")
01774   endforeach ()
01775   set (${ARGS} "${ARGS_OUT}" PARENT_SCOPE)
01776 endfunction ()
01777 
01778 
01779 ## @}
01780 # end of Doxygen group