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:
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_<Pkg></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><Pkg>_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 # <Package>[-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 <Pkg>_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 <Pkg>_INCLUDES, @c <Pkg>_INCLUDE_DIRS, or @c <Pkg>_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 <PKG>_INCLUDES is defined, but not @c <Pkg>_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 # <Package>[-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:
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:
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:
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:
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 <CONFIG>_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:
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:
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 $<TARGET_FILE:tgt> 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>$<TARGET_FILE:tgt></tt></b> @endtp
01719 # <td>Absolute file path of built target.</td>
01720 # </tr>
01721 # <tr>
01722 # @tp <b><tt>$<TARGET_FILE_POST_INSTALL:tgt></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>$<TARGET_FILE_POST_INSTALL_RELATIVE:tgt></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