BASIS  r3148
utilities.sh
Go to the documentation of this file.
00001 ##############################################################################
00002 # @file  utilities.sh
00003 # @brief Main module of project-independent BASIS utilities.
00004 #
00005 # This module defines the default BASIS utility functions. These default
00006 # implementations are not project-specific, i.e., do not make use of particular
00007 # project attributes such as the name or version of the project. The utility
00008 # functions defined by this module are intended for use in Bash scripts that
00009 # are not build as part of a particular BASIS project. Otherwise, the
00010 # project-specific implementations should be used instead, i.e., those defined
00011 # by the basis.sh module of the project which is automatically added to the
00012 # project during the configuration of the build tree. This basis.sh module and
00013 # the submodules used by it are generated from template modules which are
00014 # customized for the particular project that is being build.
00015 #
00016 # Besides the utility functions which are common to all implementations for
00017 # the different programming languages, does this module further provide
00018 # fundamental functions for the development in Bash.
00019 #
00020 # @note In Bash, there is no concept of namespaces. Hence, the utility functions
00021 #       are all defined by the utilities.sh module which is part of the BASIS
00022 #       installation. By simply setting the constants to the project specific
00023 #       values, these utility functions are customized for this particular
00024 #       package. This, however, also means that the BASIS utilities of two
00025 #       different packages cannot be used within a Bash script at the same
00026 #       time in general. The order in which the basis.sh modules are sourced
00027 #       matters. Therefore, in Bash, care must be taken which modules of a
00028 #       BASIS-based package are being sourced and whether these in turn
00029 #       source either the utilities.sh module of BASIS or the basis.sh module
00030 #       which has been configured/customized for this particular package.
00031 #       If all modules make only use of the utilities.sh module, there are
00032 #       no conflicts. Thus, Bash should in general only be used for executable
00033 #       scripts within a project, but not to provide library functions to
00034 #       other developers. Therefore, consider the use of C++, Python, or Perl,
00035 #       instead.
00036 #
00037 # Copyright (c) 2011, 2012 University of Pennsylvania. All rights reserved.<br />
00038 # See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.
00039 #
00040 # Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00041 #
00042 # @ingroup BasisBashUtilities
00043 ##############################################################################
00044 
00045 [ "${_BASIS_UTILITIES_INCLUDED}" == 'true' ] || {
00046 _BASIS_UTILITIES_INCLUDED='true'
00047 
00048 
00049 # ============================================================================
00050 # constants
00051 # ============================================================================
00052 
00053 BASIS_UTILITIES_DIR="`cd -P -- "\`dirname -- "${BASH_SOURCE}"\`" && pwd`"
00054 readonly BASIS_UTILITIES_DIR
00055 
00056 # ============================================================================
00057 # source other modules
00058 # ============================================================================
00059 
00060 . "${BASIS_UTILITIES_DIR}/core.sh"  || exit 1 # core utilities, i.e., import()
00061 
00062 import basis.config  # constants
00063 import basis.os.path # file path manipulation
00064 import basis.shflags # command-line parsing library
00065 
00066 # ============================================================================
00067 # configuration
00068 # ============================================================================
00069 
00070 # the following contanst are set by the basis.sh module, if not, because
00071 # this module is used in a script which does not belong to a BASIS-based
00072 # project, they are initialized to project-independent defaults here
00073 
00074 ## @brief Project name.
00075 [ -n "${PROJECT}" ] || readonly PROJECT=''
00076 ## @brief Project version.
00077 [ -n "${VERSION}" ] || readonly VERSION=''
00078 ## @brief Project release.
00079 [ -n "${RELEASE}" ] || readonly RELEASE=''
00080 ## @brief Default copyright of executables.
00081 [ -n "${COPYRIGHT}" ] || readonly COPYRIGHT='2011, 2012, 2013 University of Pennsylvania'
00082 ## @brief Default license of executables.
00083 [ -n "${LICENSE}" ] || readonly LICENSE='See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.'
00084 ## @brief Default contact to use for help output of executables.
00085 [ -n "${CONTACT}" ] || readonly CONTACT='SBIA Group <sbia-software at uphs.upenn.edu>'
00086 
00087 
00088 # common prefix of target UIDs belonging to this project
00089 [ -n "${_BASIS_TARGET_UID_PREFIX}" ] || readonly _BASIS_TARGET_UID_PREFIX=''
00090 # used to make relative paths in executable target information map absolute
00091 [ -n "${_BASIS_EXECUTABLE_TARGETS_BASE}" ] || readonly _BASIS_EXECUTABLE_TARGETS_BASE="${BASIS_UTILITIES_DIR}"
00092 
00093 
00094 ## @addtogroup BasisBashUtilities
00095 #  @{
00096 
00097 
00098 # ============================================================================
00099 # executable information
00100 # ============================================================================
00101 
00102 # ----------------------------------------------------------------------------
00103 ## @brief Print contact information.
00104 #
00105 # @param [in] contact Name of contact. Defaults to <tt>${CONTACT}</tt>.
00106 #
00107 # @returns Nothing.
00108 print_contact()
00109 {
00110     [ -n "$1" ] && echo -e "Contact:\n  $1" || echo -e "Contact:\n  ${CONTACT}"
00111 }
00112 
00113 # ----------------------------------------------------------------------------
00114 ## @brief Print version information including copyright and license notices.
00115 #
00116 # Example:
00117 # @code
00118 # print_version 'foo' '1.0'
00119 # print_version 'foo' -v '1.0'
00120 # print_version -v 1.0 -p BarStool 'foo'
00121 # print_version 'foo' -v '1.2' -c '2012 University of Pennsylvania' \
00122 #                              -l 'Apache License, Version 2.0'
00123 # @endcode
00124 #
00125 # @param [in] options   Function options as documented below.
00126 # @param [in] name      Name of executable. Should not be set programmatically
00127 #                       to the first argument of the main script, but a string
00128 #                       literal instead.
00129 # @param [in] version   Version of executable. Defaults to <tt>${RELEASE}</tt>
00130 #                       if defined, otherwise this argument is required.
00131 # @par Options:
00132 # <table border="0">
00133 #   <tr>
00134 #     @tp @b -v, @b --version &lt;version&gt; @endtp
00135 #     <td>Version of executable. Can be either given as option or second
00136 #         positional argument after the @p name.</td>
00137 #   </tr>
00138 #   <tr>
00139 #     @tp @b -p,  @b --project &lt;name&gt; @endtp
00140 #     <td>Name of project this executable belongs to.
00141 #         Defaults to <tt>${PROJECT}</tt> if defined.
00142 #         If 'none', no project information is printed.</td>
00143 #   </tr>
00144 #   <tr>
00145 #     @tp @b -c  @b --copyright &lt;copyright&gt; @endtp
00146 #     <td>The copyright notice. Defaults to <tt>${COPYRIGHT}</tt>.
00147 #         If 'none', no copyright notice is printed.</td>
00148 #   </tr>
00149 #   <tr>
00150 #     @tp @b -l  @b --license &lt;license&gt; @endtp
00151 #     <td>Information regarding licensing. Defaults to <tt>${LICENSE}</tt>.
00152 #         If 'none', no license information is printed.</td>
00153 #   </tr>
00154 # </table>
00155 #
00156 # @returns Nothing.
00157 print_version()
00158 {
00159     local _basis_pv_name=''
00160     local _basis_pv_version="${RELEASE}"
00161     local _basis_pv_project="${PROJECT}"
00162     local _basis_pv_copyright="${COPYRIGHT:-}"
00163     local _basis_pv_license="${LICENSE:-}"
00164     while [ $# -gt 0 ]; do
00165         case "$1" in
00166             -v|--version)
00167                 if [ -n "${_basis_pv_version}" ]; then
00168                     echo "print_version(): Version specified twice!" 1>&2
00169                     return 1
00170                 fi
00171                 if [ $# -gt 1 ]; then
00172                     _basis_pv_version="$2"
00173                 else
00174                     echo "print_version(): Option -v, --version is missing an argument!" 1>&2
00175                     return 1
00176                 fi
00177                 shift
00178                 ;;
00179             -p|--project)
00180                 if [ $# -gt 1 ]; then
00181                     _basis_pv_project="$2"
00182                 else
00183                     echo "print_version(): Option -p, --project is missing an argument!" 1>&2
00184                     return 1
00185                 fi
00186                 shift
00187                 ;;
00188             -c|--copyright)
00189                 if [ $# -gt 1 ]; then
00190                     _basis_pv_copyright="$2"
00191                 else
00192                     echo "print_version(): Option -c, --copyright is missing an argument!" 1>&2
00193                     return 1
00194                 fi
00195                 shift
00196                 ;;
00197             -l|--license)
00198                 if [ $# -gt 1 ]; then
00199                     _basis_pv_license="$2"
00200                 else
00201                     echo "print_version(): Option -l, --license is missing an argument!" 1>&2
00202                     return 1
00203                 fi
00204                 shift
00205                 ;;
00206             *)
00207                 if   [ -z "${_basis_pv_name}" ]; then
00208                     _basis_pv_name=$1
00209                 elif [ -z "${_basis_pv_version}" ]; then
00210                     _basis_pv_version=$1
00211                 else
00212                     echo "print_version(): Too many arguments or invalid option: $1" 1>&2
00213                     return 1
00214                 fi
00215                 ;;
00216         esac
00217         shift
00218     done
00219     [ -n "${_basis_pv_name}"    ] || { echo "print_version(): Missing name argument"    1>&2; return 1; }
00220     [ -n "${_basis_pv_version}" ] || { echo "print_version(): Missing version argument" 1>&2; return 1; }
00221     echo -n "${_basis_pv_name}"
00222     [ -n "${_basis_pv_project}" ] && [ "${_basis_pv_project}" != 'none' ] && {
00223         echo -n " (${_basis_pv_project})"
00224     }
00225     echo " ${_basis_pv_version}"
00226     [ -n "${_basis_pv_copyright}" ] && [ "${_basis_pv_copyright}" != 'none' ] && {
00227         echo -e "Copyright (c) ${_basis_pv_copyright}. All rights reserved."
00228     }
00229     [ -n "${_basis_pv_license}"   ] && [ "${_basis_pv_license}"   != 'none' ] && {
00230         echo -e "${_basis_pv_license}"
00231     }
00232 }
00233 
00234 # ----------------------------------------------------------------------------
00235 ## @brief Get UID of build target.
00236 #
00237 # The UID of a build target is its name prepended by a namespace identifier
00238 # which should be unique for each project.
00239 #
00240 # This function further initializes the dictionary storing the information
00241 # about the executable targets upon the first invocation. Reason to do it
00242 # here is that every access to the dictionaries first calls this function
00243 # to get the UID of a build target. Moreover, also this function needs to
00244 # have the already initialized dictionaries to ensure that an already valid
00245 # target identifier is not modified. As Bash does not provide hash tables,
00246 # dictionary data structures, the imitation of these is necessary which,
00247 # however, results in many eval() calls with noticeable impact on running time.
00248 # Therefore, to decrease the time required to source the BASIS utilities,
00249 # the required dictionary structure is initialized only upon first use.
00250 #
00251 # @param [out] uid  UID of named build target.
00252 # @param [in]  name Name of build target.
00253 #
00254 # @returns Nothing.
00255 #
00256 # @retval 0 On success.
00257 # @retval 1 On failure.
00258 targetuid()
00259 {
00260     [ -n "$1" ] && [ $# -eq 2 ] || return 1
00261     local _basis_targetuid_target="$2"
00262     # initialize module if not done yet - this is only done here because
00263     # whenever information is looked up about an executable target, this
00264     # function is invoked first
00265     if [ "${_BASIS_EXECUTABLETARGETINFO_INITIALIZED}" != 'true' ]; then
00266         _basis_executabletargetinfo_initialize || return 1
00267     fi
00268     # empty string as input remains unchanged
00269     [ -z "${_basis_targetuid_target}" ] && local "$1" && upvar $1 '' && return 0
00270     # in case of a leading namespace separator, do not modify target name
00271     [ "${_basis_targetuid_target:0:1}" == '.' ] && local "$1" && upvar $1 "${_basis_targetuid_target}" && return 0
00272     # project namespace
00273     local _basis_targetuid_prefix="${_BASIS_TARGET_UID_PREFIX}.DUMMY"
00274     # try prepending namespace or parts of it until target is known
00275     local _basis_targetuid_path=''
00276     while [ "${_basis_targetuid_prefix/\.*/}" != "${_basis_targetuid_prefix}" ]; do
00277         _basis_targetuid_prefix="${_basis_targetuid_prefix%\.*}"
00278         _basis_executabletargetinfo_get _basis_targetuid_path "${_basis_targetuid_prefix}.${_basis_targetuid_target}" LOCATION
00279         if [ -n "${_basis_targetuid_path}" ]; then
00280             local "$1" && upvar $1 "${_basis_targetuid_prefix}.${_basis_targetuid_target}"
00281             return 0
00282         fi
00283     done
00284     # otherwise, return target name unchanged
00285     local "$1" && upvar $1 "${_basis_targetuid_target}"
00286 }
00287 
00288 # ----------------------------------------------------------------------------
00289 ## @brief Determine whether a given build target is known.
00290 #
00291 # @param [in] target Name of build target.
00292 #
00293 # @returns Whether the named target is a known executable target.
00294 istarget()
00295 {
00296     local _basis_istarget_uid && targetuid _basis_istarget_uid "$1"
00297     [ -n "${_basis_istarget_uid}" ] || return 1
00298     local _basis_istarget_path && _basis_executabletargetinfo_get _basis_istarget_path "${_basis_istarget_uid}" LOCATION
00299     [ -n "${_basis_istarget_path}" ]
00300 }
00301 
00302 # ----------------------------------------------------------------------------
00303 ## @brief Get absolute path of executable file.
00304 #
00305 # This function determines the absolute file path of an executable. If no
00306 # arguments are given, the absolute path of this executable is returned.
00307 # If the given argument is a known build target name, the absolute path
00308 # of the executable built by this target is returned. Otherwise, the named
00309 # command is searched in the system PATH and it's absolute path returned
00310 # if found. If the given argument is neither the name of a known build target
00311 # nor an executable found on the PATH, an empty string is returned and
00312 # the return value is 1.
00313 #
00314 # @param [out] path   Absolute path of executable file.
00315 # @param [in]  target Name/UID of build target. If no argument is given,
00316 #                     the file path of the calling executable is returned.
00317 #
00318 # @returns Nothing.
00319 #
00320 # @retval 0 On success.
00321 # @retval 1 On failure.
00322 exepath()
00323 {
00324     [ -n "$1" ] && [ $# -eq 1 -o $# -eq 2 ] || return 1
00325     local _basis_exepath_path=''
00326     # if no target name given, get path of this executable
00327     if [ -z "$2" ]; then
00328         _basis_exepath_path="`realpath "$0"`"
00329     # otherwise, get path of executable built by named target
00330     else
00331         # get UID of target
00332         local _basis_exepath_uid && targetuid _basis_exepath_uid "$2"
00333         [ "${_basis_exepath_uid:0:1}" == '.' ] && _basis_exepath_uid=${_basis_exepath_uid:1}
00334         if [ -n "${_basis_exepath_uid}" ]; then
00335             # get path relative to this module
00336             _basis_executabletargetinfo_get _basis_exepath_path "${_basis_exepath_uid}" LOCATION
00337             if [ -n "${_basis_exepath_path}" ]; then
00338                 # make path absolute
00339                 _basis_exepath_path=`abspath "${_BASIS_EXECUTABLE_TARGETS_BASE}" "${_basis_exepath_path}"`
00340                 [ $? -eq 0 ] || return 1
00341             else
00342                 _basis_exepath_path=`/usr/bin/which "$2" 2> /dev/null`
00343             fi
00344         else
00345             _basis_exepath_path=`/usr/bin/which "$2" 2> /dev/null`
00346         fi
00347     fi
00348     # return path
00349     local "$1" && upvar $1 "${_basis_exepath_path}"
00350     [ $? -eq 0 ] && [ -n "${_basis_exepath_path}" ]
00351 }
00352 
00353 # ----------------------------------------------------------------------------
00354 ## @brief Get name of executable file.
00355 #
00356 # @param [out] file Name of executable file or an empty string if not found.
00357 #                   If @p name is not given, the name of this executable is returned.
00358 # @param [in]  name Name of command or an empty string.
00359 #
00360 # @returns Whether or not the command was found.
00361 #
00362 # @retval 0 On success.
00363 # @retval 1 On failure.
00364 exename()
00365 {
00366     [ -n "$1" ] && [ $# -eq 1 -o $# -eq 2 ] || return 1
00367     local _basis_exename_path && exepath _basis_exename_path "$2"
00368     [ $? -eq 0 ] || return 1
00369     local _basis_exename_name="`basename "${_basis_exename_path}"`"
00370     local "$1" && upvar $1 "${_basis_exename_name}"
00371 }
00372 
00373 # ----------------------------------------------------------------------------
00374 ## @brief Get directory of executable file.
00375 #
00376 # @param [out] dir  Directory of executable file or an empty string if not found.
00377 #                   If @p name is not given, the directory of this executable is returned.
00378 # @param [in]  name Name of command or an empty string.
00379 #
00380 # @returns Whether or not the command was found.
00381 #
00382 # @retval 0 On success.
00383 # @retval 1 On failure.
00384 exedir()
00385 {
00386     [ -n "$1" ] && [ $# -eq 1 -o $# -eq 2 ] || return 1
00387     local _basis_exedir_path && exepath _basis_exedir_path "$2"
00388     [ $? -eq 0 ] || return 1
00389     local _basis_exedir_dir="`dirname "${_basis_exedir_path}"`"
00390     local "$1" && upvar $1 "${_basis_exedir_dir}"
00391 }
00392 
00393 # ============================================================================
00394 # command execution
00395 # ============================================================================
00396 
00397 # ----------------------------------------------------------------------------
00398 ## @brief Build quoted string from array.
00399 #
00400 # Example:
00401 # @code
00402 # tostring str 'this' "isn't" a 'simple example of "a quoted"' 'string'
00403 # echo "${str}"
00404 # @endcode
00405 #
00406 # @param [out] var      Name of result variable for quoted string.
00407 # @param [in]  elements All remaining arguments are considered to be the
00408 #                       elements of the array to convert.
00409 #
00410 # @returns Nothing.
00411 tostring()
00412 {
00413     local _basis_tostring_str=''
00414     local _basis_tostring_element=''
00415     # GNU bash, version 3.00.15(1)-release (x86_64-redhat-linux-gnu)
00416     # turns the array into a single string value if local is used
00417     if [ ${BASH_VERSION_MAJOR} -gt 3 ] || [ ${BASH_VERSION_MAJOR} -eq 3 -a ${BASH_VERSION_MINOR} -gt 0 ]; then
00418         local _basis_tostring_args=("$@")
00419     else
00420         _basis_tostring_args=("$@")
00421     fi
00422     local _basis_tostring_i=1 # first argument is name of return variable
00423     while [ $_basis_tostring_i -lt ${#_basis_tostring_args[@]} ]; do
00424         _basis_tostring_element="${_basis_tostring_args[$_basis_tostring_i]}"
00425         # escape double quotes
00426         _basis_tostring_element=`printf -- "${_basis_tostring_element}" | sed 's/\\"/\\\\"/g'`
00427         # surround element by double quotes if necessary
00428         match "${_basis_tostring_element}" "[' ]|^$" && _basis_tostring_element="\"${_basis_tostring_element}\""
00429         # append element
00430         [ -n "${_basis_tostring_str}" ] && _basis_tostring_str="${_basis_tostring_str} "
00431         _basis_tostring_str="${_basis_tostring_str}${_basis_tostring_element}"
00432         # next argument
00433         let _basis_tostring_i++
00434     done
00435     local "$1" && upvar $1 "${_basis_tostring_str}"
00436 }
00437 
00438 # ----------------------------------------------------------------------------
00439 ## @brief Split (quoted) string.
00440 #
00441 # This function can be used to split a (quoted) string into its elements.
00442 #
00443 # Example:
00444 # @code
00445 # str="'this' 'isn\'t' a \"simple example of \\\"a quoted\\\"\" 'string'"
00446 # qsplit array "${str}"
00447 # echo ${#array[@]}  # 5
00448 # echo "${array[3]}" # simple example of "a quoted"
00449 # @endcode
00450 #
00451 # @param [out] var Result variable for array.
00452 # @param [in]  str Quoted string.
00453 #
00454 # @returns Nothing.
00455 qsplit()
00456 {
00457     [ $# -eq 2 ] || return 1
00458     # GNU bash, version 3.00.15(1)-release (x86_64-redhat-linux-gnu)
00459     # turns the array into a single string value if local is used
00460     if [ ${BASH_VERSION_MAJOR} -gt 3 ] || [ ${BASH_VERSION_MAJOR} -eq 3 -a ${BASH_VERSION_MINOR} -gt 0 ]; then
00461         local _basis_qsplit_array=()
00462     else
00463         _basis_qsplit_array=()
00464     fi
00465     local _basis_qsplit_str=$2
00466     # match arguments from left to right
00467     while match "${_basis_qsplit_str}" "[ ]*('([^']|\\\')*[^\\]'|\"([^\"]|\\\")*[^\\]\"|[^ ]+)(.*)"; do
00468         # matched element including quotes
00469         _basis_qsplit_element="${BASH_REMATCH[1]}"
00470         # remove quotes
00471         if [[ ${_basis_qsplit_element:0:1} == '"' && ${_basis_qsplit_element: -1} == '"' ]]; then
00472             _basis_qsplit_element="${_basis_qsplit_element:1}"
00473             _basis_qsplit_element="${_basis_qsplit_element%\"}"
00474         elif [[ ${_basis_qsplit_element:0:1} == "'" && ${_basis_qsplit_element: -1} == "'" ]]; then
00475             _basis_qsplit_element="${_basis_qsplit_element:1}"
00476             _basis_qsplit_element="${_basis_qsplit_element%\'}"
00477         fi
00478         # replace quoted quotes within argument by quotes
00479         _basis_qsplit_element=`printf -- "${_basis_qsplit_element}" | sed "s/[\\]'/'/g;s/[\\]\"/\"/g"`
00480         # add to resulting array
00481         _basis_qsplit_array[${#_basis_qsplit_array[@]}]="${_basis_qsplit_element}"
00482         # continue with residual command-line
00483         _basis_qsplit_str="${BASH_REMATCH[4]}"
00484     done
00485     # return
00486     local "$1" && upvar $1 "${_basis_qsplit_array[@]}"
00487 }
00488 
00489 # ----------------------------------------------------------------------------
00490 ## @brief Execute command as subprocess.
00491 #
00492 # This function is used to execute a subprocess within a Bash script.
00493 #
00494 # Example:
00495 # @code
00496 # # the next command will exit the current shell if it fails
00497 # execute ls /not/existing
00498 # # to prevent this, provide the --allow_fail option
00499 # execute --allow_fail ls /not/existing
00500 # # to make it explicit where the command-line to execute starts, use --
00501 # execute --allow_fail -- ls /not/existing
00502 # @endcode
00503 #
00504 # Note that the output of the command is not redirected by this function.
00505 # In order to execute the command quietly, use this function as follows:
00506 # @code
00507 # execute ls / &> /dev/null
00508 # @endcode
00509 # Or to store the command output in a variable including error messages
00510 # use it as follows:
00511 # @code
00512 # output=`execute ls / 2>&1`
00513 # @endcode
00514 # Note that in this case, the option --allow_fail has no effect as the
00515 # calling shell will never be terminated. Only the subshell in which the
00516 # command is executed will be terminated. Checking the exit code $? is
00517 # in this case required.
00518 #
00519 # @param [in] options Function options as documented below.
00520 # @param [in] cmd     Executable of command to run or corresponding build
00521 #                     target name. This is assumed to be the first
00522 #                     non-option argument or the argument that follows the
00523 #                     special '--' argument.
00524 # @param [in] args    All remaining arguments are passed as arguments to
00525 #                     the given command.
00526 # @par Options:
00527 # <table border="0">
00528 #   <tr>
00529 #     @tp <b>-f, --allow_fail</b> @endtp
00530 #     <td>Allows the command to fail. By default, if the command
00531 #         returns a non-zero exit code, the exit() function is
00532 #         called to terminate the current shell.</td>
00533 #   </tr>
00534 #   <tr>
00535 #     @tp <b>-v, --verbose</b> [int] @endtp
00536 #     <td>Print command-line to stdout before execution. Optionally, as it is
00537 #         sometimes more convenient to pass in the value of another variable
00538 #         which controls the verbosity of the parent process itself, a verbosity
00539 #         value can be specified following the option flag. If this verbosity
00540 #         less or equal to zero, the command-line of the subprocess is not
00541 #         printed to stdout, otherwise it is.</td>
00542 #   </tr>
00543 #   <tr>
00544 #     @tp <b>-s, --simulate</b> @endtp
00545 #     <td>If this option is given, the command is not actually
00546 #         executed, but the command-line printed to stdout only.</td>
00547 #   </tr>
00548 # </table>
00549 #
00550 # @returns Exit code of subprocess.
00551 execute()
00552 {
00553     # parse arguments
00554     local _basis_execute_allow_fail='false'
00555     local _basis_execute_simulate='false'
00556     local _basis_execute_verbose=0
00557     local _basis_execute_args=''
00558     while [ $# -gt 0 ]; do
00559         case "$1" in
00560             -f|--allow_fail) _basis_execute_allow_fail='true'; ;;
00561             -s|--simulate)   _basis_execute_simulate='true';   ;;
00562             -v|--verbose)
00563                 match "$2" '^-?[0-9]+$'
00564                 if [ $? -eq 0 ]; then
00565                     _basis_execute_verbose=$2
00566                     shift
00567                 else
00568                     let _basis_execute_verbose++
00569                 fi
00570                 ;;
00571             --)              shift; break; ;;
00572             *)               break; ;;
00573         esac
00574         shift
00575     done
00576     # command to execute and its arguments
00577     local _basis_execute_command="$1"; shift
00578     [ -n "${_basis_execute_command}" ] || { echo "execute_process(): No command specified to execute" 1>&2; return 1; }
00579     # get absolute path of executable
00580     local _basis_execute_exec && exepath _basis_execute_exec "${_basis_execute_command}"
00581     [ -n "${_basis_execute_exec}" ] || { echo "${_basis_execute_command}: Command not found" 1>&2; exit 1; }
00582     # some verbose output
00583     if [ ${_basis_execute_verbose} -gt 0 ] || [ "${_basis_execute_simulate}" == 'true' ]; then
00584         tostring _basis_execute_args "$@"
00585         echo "\$ ${_basis_execute_exec} ${_basis_execute_args}"
00586     fi
00587     # execute command
00588     [ "${_basis_execute_simulate}" == 'true' ] || "${_basis_execute_exec}" "$@"
00589     local _basis_execute_status=$?
00590     # if command failed, exit
00591     [ ${_basis_execute_status} -eq 0 -o "${_basis_execute_allow_fail}" == 'true' ] || {
00592         [ -n "${_basis_execute_args}" ] || tostring _basis_execute_args "$@"
00593         echo
00594         echo "Command ${_basis_execute_exec} ${_basis_execute_args} failed" 1>&2
00595         exit 1
00596     }
00597     # return exit code
00598     return ${_basis_execute_status}
00599 }
00600 
00601 
00602 ## @}
00603 # end of Doxygen group
00604 
00605 # ============================================================================
00606 # private
00607 # ============================================================================
00608 
00609 # ----------------------------------------------------------------------------
00610 # @brief Sanitize string for use in variable name.
00611 #
00612 # @param [out] out Sanitized string.
00613 # @param [in]  str String to be sanitized.
00614 #
00615 # @returns Nothing.
00616 #
00617 # @retval 0 On success.
00618 # @retval 1 On failure.
00619 _basis_executabletargetinfo_sanitize()
00620 {
00621     [ $# -eq 2 ] || return 1
00622     [ -n "$2" ] || {
00623         upvar $1 ''
00624         return 0
00625     }
00626     local sane="`printf -- "$2" | tr '[:space:]' '_' | tr -c '[:alnum:]' '_'`"
00627     [ -n "${sane}" ] || {
00628         echo "_basis_executabletargetinfo_sanitize(): Failed to sanitize string '$2'" 1>&2
00629         exit 1
00630     }
00631     local "$1" && upvar $1 "${sane}"
00632 }
00633 
00634 # ----------------------------------------------------------------------------
00635 # @brief Add (key, value) pair to executable target info "hash".
00636 #
00637 # @param [in] key   Hash key.
00638 # @param [in] name  Name of the hash table.
00639 # @param [in] value Value associated with the given hash key.
00640 #
00641 # @returns Sets a readonly variable that represents the (key, value) entry.
00642 #
00643 # @sa _basis_executabletargetinfo_get()
00644 _basis_executabletargetinfo_add()
00645 {
00646     [ $# -eq 3 ] || return 1
00647 
00648     local key  && _basis_executabletargetinfo_sanitize key  "$1"
00649     local name && _basis_executabletargetinfo_sanitize name "$2"
00650     [ -n "${key}" ] && [ -n "${name}" ] || {
00651         if [ -z "${key}" ] && [ -z "${name}" ]; then
00652             echo "_basis_executabletargetinfo_add(): Neither lookup table nor key specified" 1>&2
00653         elif [ -z "${key}" ]; then
00654             echo "_basis_executabletargetinfo_add(): No key specified for addition to hash table '${name}'" 1>&2
00655         else
00656             echo "_basis_executabletargetinfo_add(): No lookup table given for addition of key '${key}'" 1>&2
00657         fi
00658         exit 1
00659     }
00660     eval "readonly __BASIS_EXECUTABLETARGETINFO_${name}_${key}='$3'"
00661     if [ $? -ne 0 ]; then
00662         echo "Failed to add ${name} of key ${key} to executable target info map!" 1>&2
00663         echo "This may be caused by two CMake build target names being converted to the same key." 1>&2
00664         exit 1
00665     fi
00666 }
00667 
00668 # ----------------------------------------------------------------------------
00669 # @brief Get value from executable target info "hash".
00670 #
00671 # @param [out] value Value corresponding to given @p key
00672 #                    or an empty string if key is unknown.
00673 # @param [in]  key   Hash key.
00674 # @param [in]  name  Name of the hash table.
00675 #
00676 # @returns Nothing.
00677 #
00678 # @retval 0 On success.
00679 # @retval 1 On failure.
00680 #
00681 # @sa _basis_executabletargetinfo_add()
00682 _basis_executabletargetinfo_get()
00683 {
00684     [ $# -eq 3 ] || return 1
00685 
00686     local key  && _basis_executabletargetinfo_sanitize key  "$2"
00687     local name && _basis_executabletargetinfo_sanitize name "$3"
00688     [ -n "${key}" ] && [ -n "${name}" ] || {
00689         if [ -z "${key}" ] && [ -z "${name}" ]; then
00690             echo "_basis_executabletargetinfo_get(): Neither lookup table nor key specified" 1>&2
00691         elif [ -z "${key}" ]; then
00692             echo "_basis_executabletargetinfo_get(): No key specified for lookup in hash table '${name}'" 1>&2
00693         else
00694             echo "_basis_executabletargetinfo_get(): No lookup table given for lookup of key '${key}'" 1>&2
00695         fi
00696         exit 1
00697     }
00698     eval "local value=\${__BASIS_EXECUTABLETARGETINFO_${name}_${key}}"
00699 
00700     local "$1" && upvar $1 "${value}"
00701 }
00702 
00703 # initialize table of executable target information upon first use
00704 # this function is redefined by the basis.sh module, see targetuid()
00705 _basis_executabletargetinfo_initialize()
00706 {
00707     [ $# -eq 0 ] || return 1
00708     _BASIS_EXECUTABLETARGETINFO_INITIALIZED='true'
00709     return 0
00710 }
00711 
00712 
00713 } # _BASIS_UTILITIES_INCLUDED