BASIS  r3148
MatlabTools.cmake
Go to the documentation of this file.
00001 ##############################################################################
00002 # @file  MatlabTools.cmake
00003 # @brief Enables use of MATLAB Compiler and build of MEX-files.
00004 #
00005 # Copyright (c) 2011, 2012, 2013 University of Pennsylvania. All rights reserved.<br />
00006 # See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.
00007 #
00008 # Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00009 #
00010 # @ingroup CMakeTools
00011 ##############################################################################
00012 
00013 if (__BASIS_MATLABTOOLS_INCLUDED)
00014   return ()
00015 else ()
00016   set (__BASIS_MATLABTOOLS_INCLUDED TRUE)
00017 endif ()
00018 
00019 
00020 # ============================================================================
00021 # modules
00022 # ============================================================================
00023 
00024 # Note: Required because generate_matlab_executable.cmake uses this module.
00025 
00026 include (CMakeParseArguments)
00027 include ("${CMAKE_CURRENT_LIST_DIR}/CommonTools.cmake")
00028 include ("${CMAKE_CURRENT_LIST_DIR}/UtilitiesTools.cmake")
00029 
00030 # ============================================================================
00031 # options
00032 # ============================================================================
00033 
00034 ## @addtogroup BasisSettings
00035 #  @{
00036 
00037 
00038 ## @brief Enable/Disable compilation of MATLAB sources if the MATLAB Compiler is available.
00039 option (BASIS_COMPILE_MATLAB "Enable compilation of MATLAB sources if MATLAB Compiler (mcc) is available." ON)
00040 
00041 ## @brief Enable/Disable invocation of MATLAB Compiler in MATLAB mode.
00042 option (
00043   BASIS_MCC_MATLAB_MODE
00044   "Prefer MATLAB mode over standalone mode to invoke MATLAB Compiler."
00045   "ON" # prefer as it releases the license immediately once done
00046 )
00047 
00048 mark_as_advanced (BASIS_COMPILE_MATLAB)
00049 mark_as_advanced (BASIS_MCC_MATLAB_MODE)
00050 
00051 
00052 ## @}
00053 # end of Doxygen group
00054 
00055 
00056 # ============================================================================
00057 # build configuration
00058 # ============================================================================
00059 
00060 ## @addtogroup BasisSettings
00061 #  @{
00062 
00063 
00064 ## @brief Compile flags used to build MATLAB Compiler targets.
00065 set (
00066   BASIS_MCC_FLAGS
00067     "-R -singleCompThread"
00068   CACHE STRING
00069     "Common MATLAB Compiler flags (separated by ' '; use '\\' to mask ' ')."
00070 )
00071 
00072 ## @brief Compile flags used to build MEX-files using the MEX script.
00073 set (
00074   BASIS_MEX_FLAGS
00075     ""
00076   CACHE STRING
00077     "Common MEX switches (separated by ' '; use '\\' to mask ' ')."
00078 )
00079 
00080 ## @brief Timeout for building MATLAB Compiler targets.
00081 set (BASIS_MCC_TIMEOUT "1800" CACHE STRING "Timeout for MATLAB Compiler execution")
00082 ## @brief Maximum number of retries on MATLAB Compiler license checkout.
00083 set (BASIS_MCC_RETRY_ATTEMPTS "4" CACHE STRING "Maximum number of retries on MATLAB Compiler license checkout error.")
00084 ## @brief Delay between retries to build MATLAB Compiler compiled targets on license checkout errors.
00085 set (BASIS_MCC_RETRY_DELAY "30" CACHE STRING "Delay between retries to build MATLAB Compiler compiled targets on license checkout error.")
00086 ## @brief Timeout for building MEX-file targets.
00087 set (BASIS_MEX_TIMEOUT "600" CACHE STRING "Timeout for MEX script execution")
00088 
00089 mark_as_advanced (BASIS_MCC_FLAGS)
00090 mark_as_advanced (BASIS_MCC_TIMEOUT)
00091 mark_as_advanced (BASIS_MCC_RETRY_ATTEMPTS)
00092 mark_as_advanced (BASIS_MCC_RETRY_DELAY)
00093 mark_as_advanced (BASIS_MEX_FLAGS)
00094 mark_as_advanced (BASIS_MEX_TIMEOUT)
00095 
00096 
00097 ## @}
00098 # end of Doxygen group
00099 
00100 
00101 # ============================================================================
00102 # utilities
00103 # ============================================================================
00104 
00105 # ----------------------------------------------------------------------------
00106 ## @brief Determine version of MATLAB installation.
00107 #
00108 # @param [out] VERSION Value returned by the "version" command of MATLAB or
00109 #                      an empty string if execution of MATLAB failed.
00110 #
00111 # @returns Sets the variable named by @p VERSION to the full MATLAB version.
00112 #
00113 # @ingroup CMakeUtilities
00114 function (basis_get_full_matlab_version VERSION)
00115   if (NOT MATLAB_EXECUTABLE)
00116     set (${VERSION} "" PARENT_SCOPE)
00117     return ()
00118   endif ()
00119   set (WORKING_DIR "${CMAKE_BINARY_DIR}/CMakeFiles")
00120   set (OUTPUT_FILE "${WORKING_DIR}/MatlabVersion.txt")
00121   # read MATLAB version from existing output file
00122   set (_MATLAB_VERSION)
00123   if (EXISTS "${OUTPUT_FILE}")
00124     file (READ "${OUTPUT_FILE}" LINES)
00125     string (REGEX REPLACE "\n"    ";" LINES "${LINES}")
00126     string (REGEX REPLACE "^;|;$" ""  LINES "${LINES}")
00127     list (LENGTH LINES NLINES)
00128     if (NLINES EQUAL 2)
00129       list (GET LINES 0 _MATLAB_EXECUTABLE)
00130       list (GET LINES 1 _MATLAB_VERSION)
00131       basis_sanitize_for_regex (RE "${MATLAB_EXECUTABLE}")
00132       if (NOT _MATLAB_EXECUTABLE MATCHES "^${RE}$")
00133         set (_MATLAB_VERSION)
00134       endif ()
00135       unset (RE)
00136     endif ()
00137   endif ()
00138   # run matlab command to write return value of "version" command to text file
00139   if (NOT _MATLAB_VERSION)
00140     message (STATUS "Determining MATLAB version...")
00141     set (CMD "${MATLAB_EXECUTABLE}" -nodesktop -nosplash -nojvm -singleCompThread)
00142     if (WIN32)
00143       list (APPEND CMD -automation)
00144     endif ()
00145     # The following direct command works on Mac OS, but not Cent OS Linux because
00146     # of the surrounding double quotes which are needed on Mac OS, but not Linux.
00147     # Therefore, write MATLAB script first to file and execute it in order to
00148     # have the same CMake code on all platforms. Furthermore, if the quotes might
00149     # no longer be required also on Mac OS, we do not have to worry about it here.
00150     # -schuha
00151     #list (APPEND CMD -r "\"fid = fopen('${OUTPUT_FILE}', 'w'), [...], fclose(fid), quit force\"")
00152     list (APPEND CMD "-r" basis_get_full_matlab_version)
00153     file (WRITE "${WORKING_DIR}/basis_get_full_matlab_version.m" 
00154 "% DO NOT EDIT. Automatically created by BASIS (basis_get_full_matlab_version).
00155 fid = fopen ('${OUTPUT_FILE}', 'w')
00156 if fid == -1, fprintf(2, '??? Error: Failed to open file ${OUTPUT_FILE} for writing!'), quit force, end
00157 fprintf (fid, '${MATLAB_EXECUTABLE}\\n%s\\n', version)
00158 fclose (fid)
00159 quit force
00160 "
00161     )
00162     execute_process (
00163       COMMAND           ${CMD}
00164       WORKING_DIRECTORY "${WORKING_DIR}"
00165       RESULT_VARIABLE   RETVAL
00166       TIMEOUT           600 # MATLAB startup can be *very* slow the first time
00167       ERROR_VARIABLE    STDERR
00168       OUTPUT_QUIET
00169     )
00170     if (NOT RETVAL EQUAL 0 OR STDERR MATCHES "\\?\\?\\? Error")
00171       set (${VERSION} "" PARENT_SCOPE)
00172       if (RETVAL MATCHES "timeout")
00173         set (REASON ": ${RETVAL}")
00174       elseif (NOT RETVAL EQUAL 0)
00175         set (REASON " with exit code: ${RETVAL}")
00176       else ()
00177         set (REASON ": Failed to open file ${OUTPUT_FILE} for writing")
00178       endif ()
00179       message (STATUS "Determining MATLAB version... - failed${REASON}")
00180       return ()
00181     endif ()
00182     # read MATLAB version from text file
00183     file (READ "${OUTPUT_FILE}" LINES)
00184     string (REGEX REPLACE "\n"    ";" LINES "${LINES}")
00185     string (REGEX REPLACE "^;|;$" ""  LINES "${LINES}")
00186     list (LENGTH LINES NLINES)
00187     if (NLINES EQUAL 2)
00188       list (GET LINES 1 _MATLAB_VERSION)
00189     else ()
00190       set (${VERSION} "" PARENT_SCOPE)
00191       message (STATUS "Determining MATLAB version... - failed")
00192       return ()
00193     endif ()
00194     if (BASIS_VERBOSE)
00195       message (STATUS "Determining MATLAB version... - done: ${_MATLAB_VERSION}")
00196     else ()
00197       message (STATUS "Determining MATLAB version... - done")
00198     endif ()
00199   endif ()
00200   # return
00201   set (${VERSION} "${_MATLAB_VERSION}" PARENT_SCOPE)
00202 endfunction ()
00203 
00204 # ----------------------------------------------------------------------------
00205 ## @brief Get version of MATLAB installation.
00206 #
00207 # @param [out] ARGV1 If given, the named variable is set to the version string
00208 #                    ("<major>.<minor>.<patch>") of the MATLAB installation.
00209 #                    Otherwise, the variables @c MATLAB_VERSION_STRING,
00210 #                    @c MATLAB_VERSION_MAJOR, @c MATLAB_VERSION_MINOR,
00211 #                    @c MATLAB_VERSION_PATCH, and @c MATLAB_RELEASE are set
00212 #                    in the scope of the caller.
00213 #
00214 # @ingroup CMakeUtilities
00215 function (basis_get_matlab_version)
00216   if (ARGC GREATER 1)
00217     message (FATAL_ERROR "basis_get_matlab_version(): Too many arguments!")
00218   endif ()
00219   basis_get_full_matlab_version (VERSION)
00220   if (VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)")
00221     set (VERSION_STRING "${CMAKE_MATCH_0}")
00222     set (VERSION_MAJOR  "${CMAKE_MATCH_1}")
00223     set (VERSION_MINOR  "${CMAKE_MATCH_2}")
00224     set (VERSION_PATCH  "${CMAKE_MATCH_3}")
00225   else ()
00226     set (VERSION_STRING "0.0")
00227     set (VERSION_MAJOR  "0")
00228     set (VERSION_MINOR  "0")
00229     set (VERSION_PATCH  "0")
00230   endif ()
00231   if (ARGC EQUAL 1)
00232     set (${ARGV0} "${VERSION_STRING}" PARENT_SCOPE)
00233   else ()
00234     set (MATLAB_VERSION_STRING "${VERSION_STRING}" PARENT_SCOPE)
00235     set (MATLAB_VERSION_MAJOR  "${VERSION_MAJOR}"  PARENT_SCOPE)
00236     set (MATLAB_VERSION_MINOR  "${VERSION_MINOR}"  PARENT_SCOPE)
00237     set (MATLAB_VERSION_PATCH  "${VERSION_PATCH}"  PARENT_SCOPE)
00238     if (VERSION MATCHES ".*\\\((.+)\\\)")
00239       set (MATLAB_RELEASE "${CMAKE_MATCH_1}" PARENT_SCOPE)
00240     else ()
00241       set (MATLAB_RELEASE "" PARENT_SCOPE)
00242     endif ()
00243   endif ()
00244 endfunction ()
00245 
00246 # ----------------------------------------------------------------------------
00247 ## @brief Get release version of MATLAB installation.
00248 #
00249 # @param [out] ARGV1 If given, the named variable is set to the release string
00250 #                    of the MATLAB installation, e.g., "R2009b". Otherwise,
00251 #                    the variable @c MATLAB_RELEASE is set in the scope of the
00252 #                    caller.
00253 #
00254 # @ingroup CMakeUtilities
00255 function (basis_get_matlab_release)
00256   if (ARGC GREATER 1)
00257     message (FATAL_ERROR "basis_get_matlab_release(): Too many arguments!")
00258   endif ()
00259   basis_get_full_matlab_version (VERSION)
00260   if (VERSION MATCHES ".*\\\((.+)\\\)")
00261     set (RELEASE "${CMAKE_MATCH_1}")
00262   else ()
00263     set (RELEASE "")
00264   endif ()
00265   if (ARGC EQUAL 1)
00266     set (${ARGV0} "${RELEASE}" PARENT_SCOPE)
00267   else ()
00268     set (MATLAB_RELEASE "${RELEASE}")
00269   endif ()
00270 endfunction ()
00271 
00272 # ----------------------------------------------------------------------------
00273 ## @brief Determine extension of MEX-files for this architecture.
00274 #
00275 # @param [out] ARGN The first argument ARGV0 is set to the extension of
00276 #                   MEX-files (excluding '.'). If the CMake variable MEX_EXT
00277 #                   is set, its value is returned. Otherwise, this function
00278 #                   tries to determine it from the system information.
00279 #                   If the extension could not be determined, an empty string
00280 #                   is returned. If no argument is given, the extension is
00281 #                   cached as the variable MEX_EXT.
00282 #
00283 # @returns Sets the variable named by the first argument to the
00284 #          platform-specific extension of MEX-files.
00285 #
00286 # @ingroup CMakeUtilities
00287 function (basis_mexext)
00288   # default return value
00289   set (MEXEXT "${MEX_EXT}")
00290   # use MEXEXT if possible
00291   if (NOT MEXEXT AND MATLAB_MEXEXT_EXECUTABLE)
00292     execute_process (
00293       COMMAND         "${MATLAB_MEXEXT_EXECUTABLE}"
00294       RESULT_VARIABLE RETVAL
00295       OUTPUT_VARIABLE MEXEXT
00296       ERROR_QUIET
00297       OUTPUT_STRIP_TRAILING_WHITESPACE
00298     )
00299     if (RETVAL)
00300       set (MEXEXT "")
00301     endif ()
00302   endif ()
00303   # otherwise, determine extension given CMake variables describing the system
00304   if (NOT MEXEXT)
00305     if (CMAKE_SYSTEM_NAME MATCHES "Linux")
00306       if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
00307         set (MEXEXT "mexa64")
00308       elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "x86" OR
00309               CMAKE_SYSTEM_PROCESSOR MATCHES "i686")
00310         set (MEXEXT "mexglx")
00311       endif ()
00312     elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
00313       if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
00314         set (MEXEXT "mexw64")
00315       elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "x86" OR
00316               CMAKE_SYSTEM_PROCESSOR MATCHES "i686")
00317         set (MEXEXT "mexw32")
00318       endif ()
00319     elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin")
00320       if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
00321         set (MEXEXT "mexmaci64")
00322       else ()
00323         set (MEXEXT "mexmaci")
00324       endif ()
00325     elseif (CMAKE_SYSTEM_NAME MATCHES "SunOS")
00326       set (MEXEXT "mexs64")
00327     endif ()
00328   endif ()
00329   # return value
00330   if (ARGC GREATER 0)
00331     set ("${ARGV0}" "${MEXEXT}" PARENT_SCOPE)
00332   else ()
00333     if (NOT DEFINED MEX_EXT)
00334       set (MARKIT 1)
00335     else ()
00336       set (MARKIT 0)
00337     endif ()
00338     set (MEX_EXT "${MEXEXT}" CACHE STRING "The extension of MEX-files for this architecture." FORCE)
00339     if (MARKIT)
00340       mark_as_advanced (MEX_EXT)
00341     endif ()
00342   endif ()
00343 endfunction ()
00344 
00345 # ----------------------------------------------------------------------------
00346 ## @brief This function writes a MATLAB M-file with addpath() statements.
00347 #
00348 # This function writes an MATLAB M-file into the top directory of the build
00349 # tree which contains an addpath() statement for each directory that was added
00350 # via basis_include_directories().
00351 #
00352 # @returns Creates file add_<project>_paths.m in the current binary directory.
00353 #
00354 # @ingroup CMakeUtilities
00355 function (basis_create_addpaths_mfile)
00356   basis_get_project_property (INCLUDE_DIRS PROPERTY PROJECT_INCLUDE_DIRS)
00357   basis_write_addpaths_mfile("${CMAKE_CURRENT_BINARY_DIR}/add_${PROJECT_NAME_L}_paths.m" ${INCLUDE_DIRS})
00358 endfunction ()
00359 
00360 # ----------------------------------------------------------------------------
00361 ## @brief This function writes a MATLAB M-file with addpath() statements.
00362 #
00363 # @param [in] MFILE Name of M-file.
00364 # @param [in] ARGN  The remaining arguments are the paths which should be added
00365 #                   to the search path of MATLAB when this M-file is being
00366 #                   executed. If the option APPEND is given, the paths are
00367 #                   appended to the specified M-file. Otherwise, any existing
00368 #                   file will be overwritten. The given directory paths can
00369 #                   be relative, in which case they are interpreted relative
00370 #                   to the location of the written M-file using the mfilename()
00371 #                   function of MATLAB.
00372 #
00373 # @ingroup CMakeUtilities
00374 macro (basis_write_addpaths_mfile MFILE)
00375   CMAKE_PARSE_ARGUMENTS (ARGN "APPEND" "" "" ${ARGN})
00376   if (NOT ARGN_APPEND)
00377     file (WRITE "${MFILE}" "% DO NOT edit. This file is automatically generated by BASIS.
00378 [mfiledir, ~, ~, ~] = fileparts(mfilename('fullpath'));\n")
00379   endif ()
00380   foreach (P IN LISTS ARGN_UNPARSED_ARGUMENTS)
00381     if (P MATCHES "^\\.?$")
00382       file (APPEND "${MFILE}" "addpath(mfiledir);\n")
00383     elseif (IS_ABSOLUTE "${P}")
00384       file (APPEND "${MFILE}" "addpath('${P}');\n")
00385     else ()
00386       file (APPEND "${MFILE}" "addpath([mfiledir '/${P}']);\n")
00387     endif ()
00388   endforeach ()
00389 endmacro ()
00390 
00391 # ----------------------------------------------------------------------------
00392 ## @brief Generate MATLAB wrapper executable.
00393 #
00394 # This function writes a Bash script on Unix or a Windows Command script on
00395 # Windows platforms which execute the specified MATLAB command using the -r
00396 # option of the matlab executable and the -nodesktop and -nosplash options.
00397 # It is used by the build scripts generated by the basis_build_mcc_target()
00398 # in order to build an executable from MATLAB source files without the use
00399 # of the MATLAB Compiler. In this case, the MATLAB source files are simply
00400 # copied to the installation directory and the wrapper script written by
00401 # this function used to execute the main function with the command-line
00402 # arguments passed on to this executable.
00403 #
00404 # @param [in] OUTPUT_FILE    Name of the output executable file.
00405 # @param [in] ARGN           The remaining options
00406 # @par
00407 # <table border=0>
00408 #   <tr>
00409 #     @tp @b DESTINATION dir @endtp
00410 #     <td>Installation destination. (default: directory of @c OUTPUT_FILE)</td>
00411 #   </tr>
00412 #   <tr>
00413 #     @tp @b COMMAND name @endtp
00414 #     <td>Name of the MATLAB command to execute, i.e.,
00415 #         the name of the main function.</td>
00416 #   </tr>
00417 #   <tr>
00418 #     @tp @b STARTUP mfile @endtp
00419 #     <td>Absolute path of a startup M-file.</td>
00420 #   </tr>
00421 #   <tr>
00422 #     @tp @b MATLABPATH dir1[ dir2...]
00423 #     <td>List of directories to be added to the MATLAB search path.</td>
00424 #   </tr>
00425 #   <tr>
00426 #     @tp @b OPTIONS opt1[ opt2...]
00427 #     <td>Additional options to pass on to the <tt>matlab</tt> executable.</td>
00428 #   </tr>
00429 # </table>
00430 function (basis_generate_matlab_executable OUTPUT_FILE)
00431   CMAKE_PARSE_ARGUMENTS (ARGN "" "COMMAND;STARTUP;DESTINATION" "OPTIONS;MATLABPATH" ${ARGN})
00432   if (NOT MATLAB_EXECUTABLE)
00433     set (MATLAB_EXECUTABLE matlab)
00434   endif ()
00435   if (NOT OUTPUT_FILE)
00436     message ("basis_generate_matlab_executable(): Missing OUTPUT_FILE argument!")
00437   endif ()
00438   if (NOT ARGN_DESTINATION)
00439     get_filename_component (ARGN_DESTINATION "${OUTPUT_FILE}" PATH)
00440   endif ()
00441   # source path
00442   set (MATLABPATH)
00443   foreach (P IN LISTS ARGN_MATLABPATH)
00444     if (P MATCHES "^\\.?$")
00445       set (P "$__DIR__")
00446     elseif (NOT IS_ABSOLUTE "${P}")
00447       set (P "$__DIR__/${P}")
00448     endif ()
00449     list (APPEND MATLABPATH "${P}")
00450   endforeach ()
00451   if (MATLABPATH)
00452     list (REMOVE_DUPLICATES MATLABPATH)
00453   endif ()
00454   # startup script
00455   if (ARGN_STARTUP)
00456     get_filename_component (STARTUP_COMMAND "${ARGN_STARTUP}" NAME_WE)
00457     get_filename_component (STARTUP_DIR     "${ARGN_STARTUP}" PATH)
00458     get_filename_component (STARTUP_PKG     "${STARTUP_DIR}"  NAME)
00459     if (STARTUP_PKG MATCHES "^\\+")
00460       get_filename_component (STARTUP_DIR "${STARTUP_DIR}" PATH)
00461       string (REGEX REPLACE "^\\+" "" STARTUP_PKG "${STARTUP_PKG}")
00462       set (STARTUP_COMMAND "${STARTUP_PKG}.${STARTUP_COMMAND}")
00463     endif ()
00464     if (IS_ABSOLUTE "${STARTUP_DIR}")
00465       file (RELATIVE_PATH STARTUP_DIR "${ARGN_DESTINATION}" "${STARTUP_DIR}")
00466     endif ()
00467     if (STARTUP_DIR)
00468       set (STARTUP_DIR "$__DIR__/${STARTUP_DIR}")
00469     else ()
00470       set (STARTUP_DIR "$__DIR__")
00471     endif ()
00472     list (FIND MATLABPATH "${STARTUP_DIR}" IDX)
00473     if (IDX EQUAL -1)
00474       set (STARTUP_CODE ", addpath('${STARTUP_DIR}')")
00475     else ()
00476       set (STARTUP_CODE)
00477     endif ()
00478     set (STARTUP_CODE "${STARTUP_CODE}, ${STARTUP_COMMAND}")
00479   else ()
00480     set (STARTUP_CODE)
00481   endif ()
00482   # write wrapper executable
00483   if (MATLABPATH)
00484     basis_list_to_delimited_string (MATLABPATH "', '" NOAUTOQUOTE ${MATLABPATH})
00485     set (MATLABPATH ", addpath('${MATLABPATH}', '-begin')")
00486   else ()
00487     set (MATLABPATH)
00488   endif ()
00489   file (WRITE "${OUTPUT_FILE}"
00490     # note that Bash variables within the script are denoted by $var
00491     # instead of ${var} to prevent CMake from substituting these patterns
00492     "#! /bin/bash
00493 
00494 readonly __DIR__=\"${BASIS_BASH___DIR__}\"
00495 
00496 errlog=
00497 finish()
00498 {
00499     local status=0
00500     if [[ -n \"$errlog\" ]]; then
00501         grep '??? Error' \"$errlog\" &> /dev/null
00502         [[ $? -ne 0 ]] || status=1
00503         /bin/rm \"$errlog\"
00504     fi
00505     exit $status
00506 }
00507 
00508 if [[ -d \"$TMPDIR\" ]]; then
00509     tmpdir=$TMPDIR
00510 else
00511     tmpdir=/tmp
00512 fi
00513 
00514 errlog=`mktemp \"$tmpdir/${ARGN_COMMAND}-log.XXXXXX\"`
00515 [[ $? -eq 0 ]] || {
00516     echo \"Failed to create temporary log file in '$tmpdir'!\" 1>&2
00517     exit 1
00518 }
00519 
00520 args=
00521 while [[ $# -gt 0 ]]; do
00522   [[ -z \"$args\" ]] || args=\"$args, \"
00523   args=\"$args'$1'\"
00524   shift
00525 done
00526 
00527 echo 'Launching MATLAB to execute ${ARGN_COMMAND} function...'
00528 trap finish EXIT # DO NOT install trap earlier !
00529 '${MATLAB_EXECUTABLE}' -nodesktop -nosplash ${ARGN_OPTIONS} \\
00530     -r \"try${MATLABPATH}${STARTUP_CODE}, ${ARGN_COMMAND}($args), catch err, fprintf(2, ['??? Error executing ${ARGN_COMMAND}\\n' err.message '\\n']), end, quit force\" \\
00531     2> >(tee \"$errlog\" >&2)"
00532   ) # end of file(WRITE) command
00533   if (UNIX)
00534     execute_process (COMMAND /bin/chmod +x "${OUTPUT_FILE}")
00535   endif ()
00536 endfunction ()
00537 
00538 # ============================================================================
00539 # MEX-file target
00540 # ============================================================================
00541 
00542 # ----------------------------------------------------------------------------
00543 ## @brief Add MEX-file target.
00544 #
00545 # @note This function should not be used directly. Instead, it is called
00546 #       by basis_add_library() if the (detected) programming language
00547 #       of the given source code files is @c CXX (i.e., C/C++) and the @c MEX
00548 #       type option is given.
00549 #
00550 # This function is used to add a shared library target which is built
00551 # using the MATLAB MEX script (mex).
00552 #
00553 # By default, the BASIS C++ utilities library is added as link dependency.
00554 # If none of the BASIS C++ utilities are used by this target, the option
00555 # NO_BASIS_UTILITIES can be given. To enable this option by default, set the
00556 # variable @c BASIS_UTILITIES to @c FALSE, best in the <tt>Settings.cmake</tt>
00557 # file located in the @c PROJECT_CONFIG_DIR (add such file if missing).
00558 # If the use of the BASIS C++ utilities is disabled by default, the
00559 # @c USE_BASIS_UTILITIES option can be used to enable them for this target
00560 # only. Note that the utilities library is a static library and thus the linker
00561 # would simply not include any of the BASIS utility functions in the final
00562 # binary file if not used. The only advantage of setting @c BASIS_UTILITIES to
00563 # @c FALSE or to always specify @c NO_BASIS_UTILITIES if no target uses the
00564 # utilities is that the BASIS utilities library will not be build in this case.
00565 #
00566 # A custom CMake build target with the following properties is added by this
00567 # function to the build system. These properties are used by
00568 # basis_build_mex_target() to generate a build script written in CMake
00569 # code which is executed by a custom CMake command. Before the invokation of
00570 # basis_build_mex_target(), the target properties can be modified using
00571 # basis_set_target_properties().
00572 #
00573 # @note Custom BASIS build targets are finalized by BASIS at the end of
00574 #       basis_project_impl(), i.e., the end of the root CMake configuration file
00575 #       of the (sub-)project.
00576 #
00577 # @par Properties on script library targets
00578 # <table border=0>
00579 #   <tr>
00580 #     @tp @b MFILE file @endtp
00581 #     <td>MATLAB source file with function prototype and documentation of MEX-file.
00582 #         (default: none)</td>
00583 #   </tr>
00584 #   <tr>
00585 #     @tp @b PREFIX prefix @endtp
00586 #     <td>Output prefix of build MEX-file such as package name
00587 #         (the prefix must include the leading + and trailing /).</td>
00588 #   </tr>
00589 # </table>
00590 #
00591 # @attention Properties documented as read-only must not be modified.
00592 #
00593 # An install command for the added library target is added by this function
00594 # as well. The MEX-file will be installed as part of the specified @p COMPONENT
00595 # in the @c INSTALL_LIBRARY_DIR on Unix and @c INSTALL_RUNTIME_DIR on Windows.
00596 #
00597 # @param [in] TARGET_NAME Name of build target.
00598 # @param [in] ARGN        The remaining arguments are parsed and the following
00599 #                         arguments extracted. All unparsed arguments are treated
00600 #                         as the source files of the MEX-file.
00601 # @par
00602 # <table border="0">
00603 #   <tr>
00604 #     @tp @b COMPONENT name @endtp
00605 #     <td>Name of installation component as part of which this MEX-file is being
00606 #         installed if the @c LIBRARY_INSTALL_DIRECTORY property is not "none".
00607 #         (default: @c BASIS_LIBRARY_COMPONENT)</td>
00608 #   </tr>
00609 #   <tr>
00610 #     @tp @b [NO]EXPORT @endtp
00611 #     <td>Whether to export this target. (default: @c TRUE)</td>
00612 #   </tr>
00613 #   <tr>
00614 #     @tp @b NO_BASIS_UTILITIES @endtp
00615 #     <td>Specify that the BASIS utilities are not used by this MEX-file and
00616 #         hence no link dependency on the BASIS utilities shall be added.
00617 #         (default: @c NOT BASIS_UTILITIES)</td>
00618 #   </tr>
00619 #   <tr>
00620 #     @tp @b USE_BASIS_UTILITIES @endtp
00621 #     <td>Specify that the BASIS utilities are used and required by this MEX-file
00622 #         and hence a link dependency on the BASIS utilities must be added.
00623 #         (default: @c BASIS_UTILITIES)</td>
00624 #   </tr>
00625 # </table>
00626 #
00627 # @returns Adds custom target to build MEX-file using the MEX script.
00628 #
00629 # @sa basis_add_library()
00630 #
00631 # @ingroup CMakeUtilities
00632 function (basis_add_mex_file TARGET_NAME)
00633   # check target name
00634   basis_check_target_name ("${TARGET_NAME}")
00635   basis_make_target_uid (TARGET_UID "${TARGET_NAME}")
00636   message (STATUS "Adding MEX-file ${TARGET_UID}...")
00637   # required commands available ?
00638   if (NOT MATLAB_MEX_EXECUTABLE)
00639     message (FATAL_ERROR "MATLAB MEX script (mex) not found! It is required to build target ${TARGET_UID}."
00640                          " Forgot to add MATLAB as dependency? Otherwise, set MATLAB_MEX_EXECUTABLE manually and try again.")
00641   endif ()
00642   # parse arguments
00643   CMAKE_PARSE_ARGUMENTS (
00644     ARGN
00645       "USE_BASIS_UTILITIES;NO_BASIS_UTILITIES;EXPORT;NOEXPORT"
00646       "COMPONENT;DESTINATION"
00647       ""
00648     ${ARGN}
00649   )
00650   set (SOURCES ${ARGN_UNPARSED_ARGUMENTS})
00651   basis_set_flag (ARGN EXPORT ${BASIS_EXPORT})
00652   if (ARGN_USE_BASIS_UTILITIES AND ARGN_NO_BASIS_UTILITIES)
00653     message (FATAL_ERROR "Target ${TARGET_UID}: Options USE_BASIS_UTILITIES and NO_BASIS_UTILITIES are mutually exclusive!")
00654   endif ()
00655   if (ARGN_USE_BASIS_UTILITIES)
00656     set (USES_BASIS_UTILITIES TRUE)
00657   elseif (ARGN_NO_BASIS_UTILITIES)
00658     set (USES_BASIS_UTILITIES FALSE)
00659   else ()
00660     set (USES_BASIS_UTILITIES ${BASIS_UTILITIES})
00661   endif ()
00662   basis_mexext (MEXEXT)
00663   # TEST flag
00664   basis_sanitize_for_regex (RE "${PROJECT_TESTING_DIR}")
00665   if (CMAKE_CURRENT_SOURCE_DIR MATCHES "^${RE}")
00666     set (TEST TRUE)
00667   else ()
00668     set (TEST FALSE)
00669   endif ()
00670   # installation component
00671   if (NOT ARGN_COMPONENT)
00672     set (ARGN_COMPONENT "${BASIS_LIBRARY_COMPONENT}")
00673   endif ()
00674   if (NOT ARGN_COMPONENT)
00675     set (ARGN_COMPONENT "Unspecified")
00676   endif ()
00677   # installation directory
00678   if (ARGN_DESTINATION)
00679     if (ARGN_DESTINATION MATCHES "^[nN][oO][nN][eE]$")
00680       set (ARGN_DESTINATION)
00681     elseif (IS_ABSOLUTE "${ARGN_DESTINATION}")
00682       file (RELATIVE_PATH ARGN_DESTINATION "${CMAKE_INSTALL_PREFIX}" "${ARGN_DESTINATION}")
00683     endif ()
00684   else ()
00685     set (ARGN_DESTINATION "${INSTALL_MATLAB_LIBRARY_DIR}")
00686   endif ()
00687   # configure (.in) source files
00688   basis_configure_sources (SOURCES ${SOURCES})
00689   # link to BASIS utilities
00690   if (USES_BASIS_UTILITIES)
00691     if (NOT TARGET ${BASIS_CXX_UTILITIES_LIBRARY})
00692       message (FATAL_ERROR "Target ${TARGET_UID} makes use of the BASIS C++ utilities"
00693                            " but BASIS was build without C++ utilities enabled."
00694                            " Either specify the option NO_BASIS_UTILITIES, set the global"
00695                            " variable BASIS_UTILITIES to FALSE"
00696                            " (in ${PROJECT_CONFIG_DIR}/Settings.cmake) or"
00697                            " rebuild BASIS with C++ utilities enabled.")
00698     endif ()
00699     # add project-specific library target if not present yet
00700     basis_add_utilities_library (BASIS_UTILITIES_TARGET)
00701     # non-project specific and project-specific utilities
00702     list (APPEND LINK_DEPENDS ${BASIS_CXX_UTILITIES_LIBRARY} ${BASIS_UTILITIES_TARGET})
00703   endif ()
00704   # add custom target
00705   add_custom_target (${TARGET_UID} ALL SOURCES ${SOURCES})
00706   get_directory_property (INCLUDE_DIRS INCLUDE_DIRECTORIES)
00707   get_directory_property (LINK_DIRS    LINK_DIRECTORIES)
00708   _set_target_properties (
00709     ${TARGET_UID}
00710     PROPERTIES
00711       LANGUAGE                  "CXX"
00712       BASIS_TYPE                MEX
00713       BASIS_UTILITIES           ${USES_BASIS_UTILITIES}
00714       BASIS_INCLUDE_DIRECTORIES "${INCLUDE_DIRS}"
00715       BASIS_LINK_DIRECTORIES    "${LINK_DIRS}"
00716       SOURCE_DIRECTORY          "${CMAKE_CURRENT_SOURCE_DIR}"
00717       BINARY_DIRECTORY          "${CMAKE_CURRENT_BINARY_DIR}"
00718       LIBRARY_OUTPUT_DIRECTORY  "${BINARY_MATLAB_LIBRARY_DIR}"
00719       LIBRARY_INSTALL_DIRECTORY "${ARGN_DESTINATION}"
00720       LIBRARY_COMPONENT         "${ARGN_COMPONENT}"
00721       COMPILE_FLAGS             "${BASIS_MEX_FLAGS}"
00722       LINK_FLAGS                ""
00723       LINK_DEPENDS              "${LINK_DEPENDS}"
00724       PREFIX                    ""
00725       OUTPUT_NAME               ""
00726       SUFFIX                    ".${MEXEXT}"
00727       MFILE                     ""
00728       TEST                      ${TEST}
00729       EXPORT                    ${EXPORT}
00730   )
00731   # add target to list of targets
00732   basis_set_project_property (APPEND PROPERTY TARGETS "${TARGET_UID}")
00733   message (STATUS "Adding MEX-file ${TARGET_UID}... - done")
00734 endfunction ()
00735 
00736 # ============================================================================
00737 # MATLAB Compiler target
00738 # ============================================================================
00739 
00740 # ----------------------------------------------------------------------------
00741 ## @brief Add MATLAB Compiler target.
00742 #
00743 # @note This function should not be used directly. Instead, it is called
00744 #       by either basis_add_executable() or basis_add_library() if the
00745 #       (detected) programming language of the given source code files is
00746 #       @c MATLAB.
00747 #
00748 # This function is used to add an executable or shared library target which is
00749 # built using the MATLAB Compiler (MCC).
00750 #
00751 # A custom CMake build target with the following properties is added by this
00752 # function to the build system. These properties are used by
00753 # basis_build_mcc_target() to generate a build script written in CMake
00754 # code which is executed by a custom CMake command. Before the invokation of
00755 # basis_build_mcc_target(), the target properties can be modified using
00756 # basis_set_target_properties().
00757 #
00758 # @note Custom BASIS build targets are finalized by BASIS at the end of
00759 #       basis_project_impl(), i.e., the end of the root CMake configuration file
00760 #       of the (sub-)project.
00761 #
00762 # @par Properties on MATLAB Compiler targets
00763 # <table border=0>
00764 #   <tr><td>TODO</td></tr>
00765 # </table>
00766 #
00767 # An install command for the added executable or library target is added by
00768 # this function as well. The executable will be installed as part of the
00769 # @p RUNTIME_COMPONENT in the directory @c INSTALL_RUNTIME_DIR. The runtime
00770 # library will be installed as part of the @p RUNTIME_COMPONENT in the directory
00771 # @c INSTALL_LIBRARY_DIR on Unix and @c INSTALL_RUNTIME_DIR on Windows.
00772 # Static/import libraries will be installed as part of the @p LIBRARY_COMPONENT
00773 # in the directory @c INSTALL_ARCHIVE_DIR.
00774 #
00775 # @note If this function is used within the @c PROJECT_TESTING_DIR, the built
00776 #       executable is output to the @c BINARY_TESTING_DIR directory tree instead.
00777 #       Moreover, no installation rules are added. Test executables are further
00778 #       not exported, regardless of the value of the @c EXPORT property.
00779 #
00780 # @param [in] TARGET_NAME Name of build target.
00781 # @param [in] ARGN        The remaining arguments are parsed and the following
00782 #                         arguments extracted. All unparsed arguments are treated
00783 #                         as the MATLAB or C/C++ source files, respectively.
00784 # @par
00785 # <table border="0">
00786 #   <tr>
00787 #     @tp <b>EXECUTABLE</b>|<b>LIBEXEC</b>|<b>SHARED</b> @endtp
00788 #     <td>Type of the MATLAB Compiler target which can be either a stand-alone
00789 #         executable, an auxiliary executable, or a shared library.
00790 #         (default: @c EXECUTABLE)</td>
00791 #   </tr>
00792 #   <tr>
00793 #     @tp @b COMPONENT name @endtp
00794 #     <td>Name of component as part of which this executable or library will be
00795 #         installed if the @c RUNTIME_INSTALL_DIRECTORY or @c LIBRARY_INSTALL_DIRECTORY
00796 #         property is not "none". Used only if @p RUNTIME_COMPONENT or
00797 #         @p LIBRARY_COMPONENT not specified.
00798 #         (default: see @p RUNTIME_COMPONENT and @p LIBRARY_COMPONENT arguments)</td>
00799 #   </tr>
00800 #   <tr>
00801 #     @tp @b DESTINATION dir @endtp
00802 #     <td>Installation directory for executable or runtime and library component
00803 #         of shared library relative to @c CMAKE_INSTALL_PREFIX. Used only if
00804 #         @p RUNTIME_DESTINATION or @p LIBRARY_DESTINATION not specified.
00805 #         If "none" (case-insensitive) is given as argument, no default installation
00806 #         rules are added. (default: see @p RUNTIME_DESTINATION and
00807 #         @p LIBRARY_DESTINATION arguments)</td>
00808 #   </tr>
00809 #   <tr>
00810 #     @tp @b LIBRARY_COMPONENT name @endtp
00811 #     <td>Name of component as part of which import/static library will be intalled
00812 #         if a shared library is build and the @c LIBRARY_INSTALL_DIRECTORY property is
00813 #         not "none". (default: @c COMPONENT if specified or @c BASIS_LIBRARY_COMPONENT
00814 #         otherwise)</td>
00815 #   </tr>
00816 #   <tr>
00817 #     @tp @b LIBRARY_DESTINATION dir @endtp
00818 #     <td>Installation directory of the library component relative to
00819 #         @c CMAKE_INSTALL_PREFIX. If "none" (case-insensitive) is given as argument or
00820 #         an executable is build, no installation rule for the library component is added.
00821 #         (default: @c INSTALL_ARCHIVE_DIR)</td>
00822 #   </tr>
00823 #   <tr>
00824 #     @tp @b RUNTIME_COMPONENT name @endtp
00825 #     <td>Name of component as part of which executable or runtime library, respectively,
00826 #         will be installed if the @c RUNTIME_INSTALL_DIRECTORY property is not "none".
00827 #         (default: @c COMPONENT if specified or @c BASIS_RUNTIME_COMPONENT otherwise)</td>
00828 #   </tr>
00829 #   <tr>
00830 #     @tp @b RUNTIME_DESTINATION dir @endtp
00831 #     <td>Installation directory of the executable or runtime component of the shared library
00832 #         relative to @c CMAKE_INSTALL_PREFIX. If "none" (case-insensitive) is given as argument,
00833 #         no installation rule for the runtime library is added.
00834 #         (default: @c INSTALL_LIBRARY_DIR for shared libraries on Unix or
00835 #         @c INSTALL_RUNTIME_DIR otherwise)</td>
00836 #   </tr>
00837 #   <tr>
00838 #     @tp @b [NO]EXPORT @endtp
00839 #     <td>Whether to export this target. (default: @c TRUE)</td>
00840 #   </tr>
00841 #   <tr>
00842 #     @tp @b NO_BASIS_UTILITIES @endtp
00843 #     <td>Specify that the BASIS utilities are not used by this executable or shared library
00844 #         and hence no link dependency on the BASIS utilities shall be added.
00845 #         (default: @c NOT BASIS_UTILITIES)</td>
00846 #   </tr>
00847 #   <tr>
00848 #     @tp @b USE_BASIS_UTILITIES @endtp
00849 #     <td>Specify that the BASIS utilities are used and required by this executable
00850 #         or shared library, respectively, and hence a link dependency on the BASIS utilities
00851 #         must be added.
00852 #         (default: @c BASIS_UTILITIES)</td>
00853 #   </tr>
00854 # </table>
00855 #
00856 # @todo Consider NO_BASIS_UTILITIES and USE_BASIS_UTILITIES options after the BASIS
00857 #       utilities for MATLAB have been implemented.
00858 #
00859 # @returns Adds custom target which builds depending on the @p BASIS_TYPE property
00860 #          either an executable or a shared library using the MATLAB Compiler.
00861 #
00862 # @sa basis_add_executable()
00863 # @sa basis_add_library()
00864 #
00865 # @ingroup CMakeUtilities
00866 function (basis_add_mcc_target TARGET_NAME)
00867   # check target name
00868   basis_check_target_name ("${TARGET_NAME}")
00869   basis_make_target_uid (TARGET_UID "${TARGET_NAME}")
00870   # parse arguments
00871   CMAKE_PARSE_ARGUMENTS (
00872     ARGN
00873       "SHARED;EXECUTABLE;LIBEXEC;EXPORT;NOEXPORT"
00874       "COMPONENT;RUNTIME_COMPONENT;LIBRARY_COMPONENT;DESTINATION;RUNTIME_DESTINATION;LIBRARY_DESTINATION"
00875       ""
00876     ${ARGN}
00877   )
00878   set (SOURCES "${ARGN_UNPARSED_ARGUMENTS}")
00879   basis_set_flag (ARGN EXPORT ${BASIS_EXPORT})
00880   if (ARGN_SHARED AND (ARGN_EXECUTABLE OR ARGN_LIBEXEC))
00881     message (FATAL_ERROR "Target ${TARGET_UID}: Options SHARED and EXECUTABLE or LIBEXEC are mutually exclusive!")
00882   endif ()
00883   if (ARGN_SHARED)
00884     message (FATAL_ERROR "Target ${TARGET_UID}: Build of shared MATLAB library not yet supported.")
00885     set (TYPE LIBRARY)
00886   else ()
00887     set (TYPE EXECUTABLE)
00888   endif ()
00889   string (TOLOWER "${TYPE}" type)
00890   message (STATUS "Adding MATLAB ${type} ${TARGET_UID}...")
00891   # TEST flag
00892   basis_sanitize_for_regex (RE "${PROJECT_TESTING_DIR}")
00893   if (CMAKE_CURRENT_SOURCE_DIR MATCHES "^${RE}")
00894     set (TEST TRUE)
00895   else ()
00896     set (TEST FALSE)
00897   endif ()
00898   # output directory
00899   if (TEST)
00900     set (LIBRARY_OUTPUT_DIRECTORY "${TESTING_LIBRARY_DIR}")
00901     if (ARGN_LIBEXEC)
00902       set (RUNTIME_OUTPUT_DIRECTORY "${TESTING_LIBEXEC_DIR}")
00903     else ()
00904       set (RUNTIME_OUTPUT_DIRECTORY "${TESTING_RUNTIME_DIR}")
00905     endif ()
00906   else ()
00907     set (LIBRARY_OUTPUT_DIRECTORY "${BINARY_LIBRARY_DIR}")
00908     if (ARGN_LIBEXEC)
00909       set (RUNTIME_OUTPUT_DIRECTORY "${BINARY_LIBEXEC_DIR}")
00910     else ()
00911       set (RUNTIME_OUTPUT_DIRECTORY "${BINARY_RUNTIME_DIR}")
00912     endif ()
00913   endif ()
00914   # installation component
00915   if (ARGN_COMPONENT)
00916     if (NOT ARGN_LIBRARY_COMPONENT)
00917       set (ARGN_LIBRARY_COMPONENT "${ARGN_COMPONENT}")
00918     endif ()
00919     if (NOT ARGN_RUNTIME_COMPONENT)
00920       set (ARGN_RUNTIME_COMPONENT "${ARGN_COMPONENT}")
00921     endif ()
00922   endif ()
00923   if (NOT ARGN_RUNTIME_COMPONENT)
00924     set (ARGN_RUNTIME_COMPONENT "${BASIS_RUNTIME_COMPONENT}")
00925   endif ()
00926   if (NOT ARGN_RUNTIME_COMPONENT)
00927     set (ARGN_RUNTIME_COMPONENT "Unspecified")
00928   endif ()
00929   if (NOT ARGN_LIBRARY_COMPONENT)
00930     set (ARGN_LIBRARY_COMPONENT "${BASIS_LIBRARY_COMPONENT}")
00931   endif ()
00932   if (NOT ARGN_LIBRARY_COMPONENT)
00933     set (ARGN_LIBRARY_COMPONENT "Unspecified")
00934   endif ()
00935   # installation directories
00936   if (ARGN_DESTINATION)
00937     if (NOT ARGN_RUNTIME_DESTINATION)
00938       set (ARGN_RUNTIME_DESTINATION "${ARGN_DESTINATION}")
00939     endif ()
00940     if (NOT ARGN_LIBRARY_DESTINATION)
00941       set (ARGN_LIBRARY_DESTINATION "${ARGN_DESTINATION}")
00942     endif ()
00943   endif ()
00944   if (NOT ARGN_RUNTIME_DESTINATION)
00945     if (TEST)
00946       set (ARGN_RUNTIME_DESTINATION) # do not install
00947     else ()
00948       if (ARGN_LIBEXEC)
00949         set (ARGN_RUNTIME_DESTINATION "${INSTALL_LIBEXEC_DIR}")
00950       else ()
00951         set (ARGN_RUNTIME_DESTINATION "${INSTALL_RUNTIME_DIR}")
00952       endif ()
00953     endif ()
00954   endif ()
00955   if (NOT ARGN_LIBRARY_DESTINATION)
00956     set (ARGN_LIBRARY_DESTINATION "${INSTALL_LIBRARY_DIR}")
00957   endif ()
00958   if (ARGN_RUNTIME_DESTINATION MATCHES "^[nN][oO][nN][eE]$")
00959     set (ARGN_RUNTIME_DESTINATION)
00960   endif ()
00961   if (ARGN_LIBRARY_DESTINATION MATCHES "^[nN][oO][nN][eE]$")
00962     set (ARGN_LIBRARY_DESTINATION)
00963   endif ()
00964   # whether to compile and compilation flags (for mcc)
00965   if (TYPE MATCHES "LIBRARY" AND NOT MATLAB_MCC_EXECUTABLE)
00966     message (FATAL_ERROR "MATLAB Compiler not found! It is required to build target ${TARGET_UID}."
00967                          " Set MATLAB_DIR and/or MATLAB_MCC_EXECUTABLE manually and try again.")
00968   endif ()
00969   if ((BASIS_COMPILE_MATLAB AND MATLAB_MCC_EXECUTABLE) OR TYPE MATCHES "LIBRARY")
00970     set (COMPILE TRUE)
00971   else ()
00972     if (BASIS_COMPILE_MATLAB)
00973       message (WARNING "MATLAB Compiler not found. Will generate a wrapper script for target"
00974                        " ${TARGET_UID} which executes the MATLAB code using the -r option of"
00975                        " the MATLAB interpreter. It is recommended to compile the MATLAB code"
00976                        " using the MATLAB Compiler if possible, however. Therefore, make sure"
00977                        " that the MATLAB Compiler is available and check the value of the"
00978                        " advanced MATLAB_MCC_EXECUTABLE variable in CMake.")
00979     endif ()
00980     set (COMPILE FALSE)
00981   endif ()
00982   if (WIN32 AND NOT COMPILE)
00983     # TODO implement generation of Windows Command on Windows
00984     set (CONTACT)
00985     if (PROJECT_CONTACT)
00986       set (CONTACT "\n\nYou may further want to contact ${CONTACT} in order to ask for"
00987                    " a binary distribution package which contains pre-build binaries"
00988                    " created using the MATLAB Compiler and download the MATLAB Compiler"
00989                    " Runtime only if no MATLAB Compiler license is available to you.")
00990       basis_list_to_string (CONTACT ${CONTACT})
00991     endif ()
00992     if (NOT BASIS_COMPILE_MATLAB)
00993       basis_update_value (BASIS_COMPILE_MATLAB ON)
00994     endif ()
00995     message (FATAL_ERROR "The optional generation of a Windows Command which executes"
00996                          " the MATLAB code using the -r option of the MATLAB interpreter"
00997                          " as an alternative to the build of the MATLAB sources using"
00998                          " the MATLAB Compiler is not yet implemented. You will have"
00999                          " to obtain a MATLAB Compiler license and set the advanced"
01000                          " MATLAB_MCC_EXECUTABLE variable in CMake or use this package"
01001                          " on a Unix system instead.${CONTACT}")
01002   endif ()
01003   set (COMPILE_FLAGS "${BASIS_MCC_FLAGS}")
01004   if (COMPILE)
01005     if (NOT MATLAB_MCC_EXECUTABLE)
01006       find_package (MATLAB COMPONENTS mcc QUIET)
01007     endif ()
01008   else ()
01009     if (NOT MATLAB_EXECUTABLE)
01010       find_package (MATLAB COMPONENTS matlab QUIET)
01011     endif ()
01012   endif ()
01013   # suffix
01014   if (WIN32 AND EXECUTABLE AND COMPILE_FLAGS MATCHES "^NOMCC$")
01015     set (SUFFIX ".cmd")
01016   else ()
01017     set (SUFFIX)
01018   endif ()
01019   # configure (.in) source files
01020   basis_configure_sources (SOURCES ${SOURCES})
01021   # add custom target
01022   add_custom_target (${TARGET_UID} ALL SOURCES ${SOURCES})
01023   get_directory_property (INCLUDE_DIRS INCLUDE_DIRECTORIES)
01024   get_directory_property (LINK_DIRS    LINK_DIRECTORIES)
01025   _set_target_properties (
01026     ${TARGET_UID}
01027     PROPERTIES
01028       LANGUAGE                  "MATLAB"
01029       BASIS_TYPE                "MCC_${TYPE}"
01030       BASIS_UTILITIES           FALSE # TODO Implement utilities for MATLAB
01031       BASIS_INCLUDE_DIRECTORIES "${INCLUDE_DIRS}"
01032       BASIS_LINK_DIRECTORIES    "${LINK_DIRS}"
01033       SOURCE_DIRECTORY          "${CMAKE_CURRENT_SOURCE_DIR}"
01034       BINARY_DIRECTORY          "${CMAKE_CURRENT_BINARY_DIR}"
01035       LIBRARY_OUTPUT_DIRECTORY  "${LIBRARY_OUTPUT_DIRECTORY}"
01036       LIBRARY_INSTALL_DIRECTORY "${ARGN_LIBRARY_DESTINATION}"
01037       LIBRARY_COMPONENT         "${ARGN_LIBRARY_COMPONENT}"
01038       RUNTIME_OUTPUT_DIRECTORY  "${RUNTIME_OUTPUT_DIRECTORY}"
01039       RUNTIME_INSTALL_DIRECTORY "${ARGN_RUNTIME_DESTINATION}"
01040       RUNTIME_COMPONENT         "${ARGN_RUNTIME_COMPONENT}"
01041       OUTPUT_NAME               "${TARGET_NAME}"
01042       SUFFIX                    "${SUFFIX}"
01043       COMPILE_FLAGS             "${COMPILE_FLAGS}"
01044       COMPILE                   "${COMPILE}"
01045       LINK_DEPENDS              ""
01046       EXPORT                    ${EXPORT}
01047       LIBEXEC                   ${ARGN_LIBEXEC}
01048       TEST                      ${TEST}
01049   )
01050   # add target to list of targets
01051   basis_set_project_property (APPEND PROPERTY TARGETS "${TARGET_UID}")
01052   message (STATUS "Adding MATLAB ${type} ${TARGET_UID}... - done")
01053 endfunction ()
01054 
01055 # ============================================================================
01056 # custom build commands
01057 # ============================================================================
01058 
01059 # ----------------------------------------------------------------------------
01060 ## @brief Add custom command for build of MEX-file.
01061 #
01062 # This function is called by basis_finalize_targets() which in turn is called
01063 # at the end of basis_project_impl(), i.e., the end of the root CMake
01064 # configuration file of the (sub-)project.
01065 #
01066 # @param [in] TARGET_UID Name/UID of custom target added by basis_add_mex_file().
01067 #
01068 # @sa basis_add_mex_file()
01069 #
01070 # @ingroup CMakeUtilities
01071 function (basis_build_mex_file TARGET_UID)
01072   # does this target exist ?
01073   basis_get_target_uid (TARGET_UID "${TARGET_UID}")
01074   if (NOT TARGET "${TARGET_UID}")
01075     message (FATAL_ERROR "Unknown build target: ${TARGET_UID}")
01076   endif ()
01077   if (BASIS_VERBOSE)
01078     message (STATUS "Adding build command for target ${TARGET_UID}...")
01079   endif ()
01080   # get target properties
01081   basis_get_target_name (TARGET_NAME ${TARGET_UID})
01082   set (
01083     PROPERTIES
01084       BASIS_TYPE
01085       BASIS_UTILITIES
01086       BASIS_INCLUDE_DIRECTORIES
01087       BASIS_LINK_DIRECTORIES
01088       SOURCE_DIRECTORY
01089       BINARY_DIRECTORY
01090       LIBRARY_OUTPUT_DIRECTORY
01091       LIBRARY_INSTALL_DIRECTORY
01092       LIBRARY_COMPONENT
01093       PREFIX
01094       OUTPUT_NAME
01095       SUFFIX
01096       COMPILE_FLAGS
01097       LINK_DEPENDS
01098       LINK_FLAGS
01099       MFILE
01100       TEST
01101       EXPORT
01102       SOURCES
01103   )
01104   foreach (PROPERTY ${PROPERTIES})
01105     get_target_property (${PROPERTY} ${TARGET_UID} ${PROPERTY})
01106   endforeach ()
01107   # sanity check of property values
01108   if (NOT BASIS_TYPE MATCHES "^MEX$")
01109     message (FATAL_ERROR "Target ${TARGET_UID}: Invalid BASIS_TYPE: ${BASIS_TYPE}")
01110   endif ()
01111   list (GET SOURCES 0 BUILD_DIR) # strange, but CMake stores path to internal build directory here
01112   list (REMOVE_AT SOURCES 0)
01113   set (BUILD_DIR "${BUILD_DIR}.dir")
01114   if (NOT IS_DIRECTORY "${BUILD_DIR}")
01115   file (MAKE_DIRECTORY "${BUILD_DIR}")
01116   endif ()
01117   if (NOT SOURCES)
01118     message (FATAL_ERROR "Target ${TARGET_UID}: Empty SOURCES list!"
01119                          " Have you accidentally modified this read-only property or"
01120                          " is your (newer) CMake version not compatible with BASIS?")
01121   endif ()
01122   if (NOT LIBRARY_COMPONENT)
01123     set (LIBRARY_COMPONENT "Unspecified")
01124   endif ()
01125   if (MFILE)
01126     if (NOT IS_ABSOLUTE "${MFILE}")
01127       set (MFILE "${SOURCE_DIRECTORY}/${MFILE}")
01128     endif ()
01129     if (NOT EXISTS "${MFILE}")
01130       message (FATAL_ERROR "M-file ${MFILE} of MEX-file target ${TARGET_UID} does not exist!")
01131     endif ()
01132   endif ()
01133   # output name
01134   if (NOT OUTPUT_NAME)
01135     set (OUTPUT_NAME "${TARGET_NAME}")
01136   endif ()
01137   if (SUFFIX)
01138     set (OUTPUT_NAME "${OUTPUT_NAME}${SUFFIX}")
01139   endif ()
01140   string (REGEX REPLACE "/+" "/" PREFIX "${PREFIX}")
01141   string (REGEX REPLACE "/$" ""  PREFIX "${PREFIX}")
01142   if (PREFIX AND NOT PREFIX MATCHES "^/")
01143     set (PREFIX "/${PREFIX}")
01144   endif ()
01145   # initialize dependencies of custom build command
01146   set (DEPENDS ${SOURCES})
01147   # get list of libraries to link to
01148   set (LINK_LIBS)
01149   foreach (LIB ${LINK_DEPENDS})
01150     basis_get_target_uid (UID "${LIB}")
01151     if (TARGET ${UID})
01152       basis_get_target_location (LIB_FILE ${UID} ABSOLUTE)
01153       list (APPEND DEPENDS ${UID})
01154     else ()
01155       set (LIB_FILE "${LIB}")
01156     endif ()
01157     list (APPEND LINK_LIBS "${LIB_FILE}")
01158   endforeach ()
01159   get_filename_component (OUTPUT_NAME_WE "${OUTPUT_NAME}" NAME_WE)
01160   # decompose user supplied MEX switches
01161   macro (extract VAR)
01162     string (REGEX REPLACE "${VAR}=\"([^\"]+)\"|${VAR}=([^\" ])*" "" COMPILE_FLAGS "${COMPILE_FLAGS}")
01163     if (CMAKE_MATCH_1)
01164       set (${VAR} "${CMAKE_MATCH_1}")
01165     elseif (CMAKE_MATCH_2)
01166       set (${VAR} "${CMAKE_MATCH_2}")
01167     else ()
01168       set (${VAR})
01169     endif ()
01170   endmacro ()
01171   if (UNIX)
01172     extract (CC)
01173     extract (CFLAGS)
01174     extract (CXX)
01175     extract (CXXFLAGS)
01176     extract (CLIBS)
01177     extract (CXXLIBS)
01178     extract (LD)
01179     extract (LDXX)
01180     extract (LDFLAGS)
01181     extract (LDCXXFLAGS)
01182     if (LINK_FLAGS)
01183       set (LDFLAGS "${LDFLAGS} ${LINK_FLAGS}")
01184     endif ()
01185     # set defaults for not provided options
01186     if (NOT CC)
01187       set (CC "${CMAKE_C_COMPILER}")
01188     endif ()
01189     if (NOT CFLAGS)
01190       set (CFLAGS "${CMAKE_C_FLAGS}")
01191     endif ()
01192     if (NOT CFLAGS MATCHES "( |^)-fPIC( |$)")
01193       set (CFLAGS "-fPIC ${CFLAGS}")
01194     endif ()
01195     if (NOT CXX)
01196       set (CXX "${CMAKE_CXX_COMPILER}")
01197     endif ()
01198     if (NOT CXXFLAGS)
01199       set (CXXFLAGS "${CMAKE_CXX_FLAGS}")
01200     endif ()
01201     if (NOT CXXFLAGS MATCHES "( |^)-fPIC( |$)")
01202       set (CXXFLAGS "-fPIC ${CXXFLAGS}")
01203     endif ()
01204     if (NOT LD)
01205       set (LD "${CMAKE_CXX_COMPILER}") # do not use CMAKE_LINKER here
01206     endif ()
01207     if (NOT LDFLAGS)
01208       set (LDFLAGS "\$LDFLAGS ${CMAKE_SHARED_LINKER_FLAGS}")
01209     endif ()
01210     # We chose to use CLIBS and CXXLIBS instead of the -L and -l switches
01211     # to add also link libraries added via basis_target_link_libraries()
01212     # because the MEX script will not use these arguments if CLIBS or CXXLIBS
01213     # is set. Moreover, the -l switch can only be used to link to a shared
01214     # library and not a static one (on UNIX).
01215     #foreach (LIB ${LINK_LIBS})
01216     #  if (LIB MATCHES "[/\\\.]")
01217     #    set (CXXLIBS "${CXXLIBS} ${LIB}")
01218     #  endif ()
01219     #endforeach ()
01220   endif ()
01221   # get remaining switches
01222   basis_string_to_list (MEX_USER_ARGS "${COMPILE_FLAGS}")
01223   # assemble MEX switches
01224   set (MEX_ARGS)
01225   if (UNIX)
01226     list (APPEND MEX_ARGS "CC=${CC}" "CFLAGS=${CFLAGS}")           # C compiler and flags
01227     if (CLIBS)
01228       list (APPEND MEX_ARGS "CLIBS=${CLIBS}")                      # C link libraries
01229     endif ()
01230     list (APPEND MEX_ARGS "CXX=${CXX}" "CXXFLAGS=${CXXFLAGS}")     # C++ compiler and flags
01231     if (CXXLIBS)
01232       list (APPEND MEX_ARGS "CXXLIBS=${CXXLIBS}")                  # C++ link libraries
01233     endif ()
01234     if (LD)
01235       list (APPEND MEX_ARGS "LD=${LD}")                            # C linker
01236     endif ()
01237     if (LDFLAGS)
01238       list (APPEND MEX_ARGS "LDFLAGS=${LDFLAGS}")                  # C link flags
01239     endif ()
01240     if (LDCXX)
01241       list (APPEND MEX_ARGS "LDCXX=${LDCXX}")                      # C++ linker
01242     endif ()
01243     if (LDCXXFLAGS)
01244       list (APPEND MEX_ARGS "LDCXXFLAGS=${LDCXXFLAGS}")            # C++ link flags
01245     endif ()
01246   endif ()
01247   list (APPEND MEX_ARGS "-outdir" "${BUILD_DIR}")                # output directory
01248   list (APPEND MEX_ARGS "-output" "${OUTPUT_NAME_WE}")           # output name (w/o extension)
01249   foreach (INCLUDE_PATH ${BASIS_INCLUDE_DIRECTORIES})            # include directories
01250     list (FIND MEX_ARGS "-I${INCLUDE_PATH}" IDX)                 # as specified via
01251     if (INCLUDE_PATH AND IDX EQUAL -1)                           # basis_include_directories()
01252       list (APPEND MEX_ARGS "-I${INCLUDE_PATH}")
01253     endif ()
01254   endforeach ()
01255   set (MEX_LIBPATH)
01256   set (MEX_LIBS)
01257   foreach (LINK_DIR ${BASIS_LINK_DIRECTORIES})                    # link directories
01258     if (WIN32)
01259       string (REPLACE "/" "\\" LINK_DIR "${LINK_DIR}")
01260       set (LINK_DIR "/LIBPATH:\\\"${LINK_DIR}\\\"")
01261     else ()
01262       set (LINK_DIR "-L${LINK_DIR}")
01263     endif ()
01264     list (APPEND MEX_LIBPATH "${LINK_DIR}")                      # as specified via basis_link_directories()
01265   endforeach ()
01266   foreach (LIBRARY ${LINK_LIBS})                                # link libraries
01267     get_filename_component (LINK_DIR "${LIBRARY}" PATH)         # as specified via basis_target_link_libraries()
01268     get_filename_component (LINK_LIB "${LIBRARY}" NAME)
01269     string (REGEX REPLACE "\\.(so|a)(\\.[0-9]+)*$" "" LINK_LIB "${LINK_LIB}")
01270     string (REGEX REPLACE "^-l"                    "" LINK_LIB "${LINK_LIB}")
01271     if (WIN32)
01272       string (REPLACE "/" "\\" LINK_DIR "${LINK_DIR}")
01273       set (LINK_DIR "/LIBPATH:\\\"${LINK_DIR}\\\"")
01274       if (NOT LINK_LIB MATCHES "\\.lib$")
01275         set (LINK_LIB "${LINK_LIB}.lib")
01276       endif ()
01277     else ()
01278       string (REGEX REPLACE "^lib" "" LINK_LIB "${LINK_LIB}")
01279       set (LINK_DIR "-L${LINK_DIR}")
01280       set (LINK_LIB "-l${LINK_LIB}")
01281     endif ()
01282     list (APPEND MEX_LIBPATH "${LINK_DIR}")
01283     list (APPEND MEX_LIBS    "${LINK_LIB}")
01284   endforeach ()
01285   if (MEX_LIBPATH)
01286     list (REMOVE_DUPLICATES MEX_LIBPATH)
01287   endif ()
01288   if (MEX_LIBS)
01289     list (REMOVE_DUPLICATES MEX_LIBS)
01290   endif ()
01291   if (MEX_LIBPATH OR MEX_LIBS)
01292     if (WIN32)
01293     basis_list_to_delimited_string (MEX_LIBPATH " " NOAUTOQUOTE ${MEX_LIBPATH})
01294     basis_list_to_delimited_string (MEX_LIBS    " " ${MEX_LIBS})
01295     list (APPEND MEX_ARGS "LINKFLAGS#$LINKFLAGS ${MEX_LIBPATH} ${MEX_LIBS}")
01296     else ()
01297       list (APPEND MEX_ARGS ${MEX_LIBPATH} ${MEX_LIBS})
01298     endif ()
01299   endif ()
01300   # other user switches 
01301   list (APPEND MEX_ARGS ${MEX_USER_ARGS})
01302   # source files
01303   list (APPEND MEX_ARGS ${SOURCES})
01304   # build command for invocation of MEX script
01305   set (BUILD_CMD     "${MATLAB_MEX_EXECUTABLE}" -v ${MEX_ARGS})
01306   set (BUILD_LOG     "${BUILD_DIR}/build.log")
01307   set (BUILD_OUTPUT  "${LIBRARY_OUTPUT_DIRECTORY}${PREFIX}/${OUTPUT_NAME}")
01308   set (BUILD_OUTPUTS "${BUILD_OUTPUT}")
01309   if (MFILE)
01310     set (BUILD_MFILE "${LIBRARY_OUTPUT_DIRECTORY}${PREFIX}/${OUTPUT_NAME_WE}.m")
01311     list (APPEND BUILD_OUTPUTS "${BUILD_MFILE}")
01312   else ()
01313     set (BUILD_MFILE)
01314   endif ()
01315   # configure build script
01316   set (COMMAND "${BUILD_CMD}")
01317   configure_file ("${BASIS_SCRIPT_EXECUTE_PROCESS}" "${BUILD_DIR}/build.cmake" @ONLY)
01318   # relative paths used for comments of commands
01319   file (RELATIVE_PATH REL "${CMAKE_BINARY_DIR}" "${BUILD_OUTPUT}")
01320   # add custom command to build executable using MEX script
01321   add_custom_command (
01322     OUTPUT "${BUILD_OUTPUT}"
01323     # rebuild when input sources were modified
01324     DEPENDS ${DEPENDS}
01325     # invoke MEX script, wrapping the command in CMake execute_process()
01326     # command allows for inspection of command output for error messages
01327     # and specification of timeout
01328     COMMAND "${CMAKE_COMMAND}"
01329           "-DCOMMAND=${COMMAND}"
01330             "-DWORKING_DIRECTORY=${BUILD_DIR}"
01331             "-DTIMEOUT=${BASIS_MEX_TIMEOUT}"
01332             "-DERROR_EXPRESSION=[E|e]rror"
01333             "-DOUTPUT_FILE=${BUILD_LOG}"
01334             "-DERROR_FILE=${BUILD_LOG}"
01335             "-DVERBOSE=OFF"
01336             "-DLOG_ARGS=ON"
01337             "-P" "${BASIS_SCRIPT_EXECUTE_PROCESS}"
01338     # post-build command
01339     COMMAND "${CMAKE_COMMAND}" -E copy   "${BUILD_DIR}/${OUTPUT_NAME}" "${BUILD_OUTPUT}"
01340     COMMAND "${CMAKE_COMMAND}" -E remove "${BUILD_DIR}/${OUTPUT_NAME}"
01341     # inform user where build log can be found
01342     COMMAND "${CMAKE_COMMAND}" -E echo "Build log written to ${BUILD_LOG}"
01343     # comment
01344     COMMENT "Building MEX-file ${REL}..."
01345     VERBATIM
01346   )
01347   if (BUILD_MFILE)
01348     add_custom_command (
01349       OUTPUT  "${BUILD_MFILE}"
01350       DEPENDS "${MFILE}"
01351       COMMAND "${CMAKE_COMMAND}" -E copy "${MFILE}" "${BUILD_MFILE}"
01352       COMMENT "Copying M-file of ${REL}..."
01353     )
01354   endif ()
01355   # add custom target
01356   add_custom_target (_${TARGET_UID} DEPENDS ${BUILD_OUTPUTS} SOURCES ${SOURCES})
01357   if (TARGET __${TARGET_UID}) # re-glob source files
01358     add_dependencies (_${TARGET_UID} __${TARGET_UID})
01359   endif ()
01360   add_dependencies (${TARGET_UID} _${TARGET_UID})
01361   # cleanup on "make clean"
01362   set_property (
01363     DIRECTORY
01364     APPEND PROPERTY
01365       ADDITIONAL_MAKE_CLEAN_FILES
01366         "${BUILD_DIR}/${OUTPUT_NAME}"
01367         "${BUILD_OUTPUTS}"
01368         "${BUILD_LOG}"
01369   )
01370   # export target
01371   if (EXPORT)
01372     if (TEST)
01373       basis_set_project_property (APPEND PROPERTY TEST_EXPORT_TARGETS "${TARGET_UID}")
01374     else ()
01375       basis_set_project_property (APPEND PROPERTY CUSTOM_EXPORT_TARGETS "${TARGET_UID}")
01376     endif ()
01377   endif ()
01378   # install MEX-file
01379   if (LIBRARY_INSTALL_DIRECTORY)
01380     install (
01381       FILES       ${BUILD_OUTPUTS}
01382       DESTINATION "${LIBRARY_INSTALL_DIRECTORY}${PREFIX}"
01383       COMPONENT   "${LIBRARY_COMPONENT}"
01384     )
01385   endif ()
01386   if (BASIS_VERBOSE)
01387     message (STATUS "Adding build command for target ${TARGET_UID}... - done")
01388   endif ()
01389 endfunction ()
01390 
01391 # ----------------------------------------------------------------------------
01392 ## @brief Add custom command for build of MATLAB Compiler target.
01393 #
01394 # This function is called by basis_finalize_targets() which in turn is called
01395 # at the end of basis_project_impl(), i.e., the end of the root CMake
01396 # configuration file of the (sub-)project.
01397 #
01398 # @param [in] TARGET_UID Name/UID of custom target added by basis_add_mcc_target().
01399 #
01400 # @sa basis_add_mcc_target()
01401 #
01402 # @ingroup CMakeUtilities
01403 function (basis_build_mcc_target TARGET_UID)
01404   # does this target exist ?
01405   basis_get_target_uid (TARGET_UID "${TARGET_UID}")
01406   if (NOT TARGET "${TARGET_UID}")
01407     message (FATAL_ERROR "Unknown target ${TARGET_UID}!")
01408   endif ()
01409   if (BASIS_VERBOSE)
01410     message (STATUS "Adding build command for target ${TARGET_UID}...")
01411   endif ()
01412   # get target properties
01413   basis_get_target_name (TARGET_NAME ${TARGET_UID})
01414   set (
01415     PROPERTIES
01416       BASIS_TYPE
01417       BASIS_UTILITIES
01418       BASIS_INCLUDE_DIRECTORIES
01419       BASIS_LINK_DIRECTORIES
01420       SOURCE_DIRECTORY
01421       BINARY_DIRECTORY
01422       LIBRARY_OUTPUT_DIRECTORY
01423       LIBRARY_INSTALL_DIRECTORY
01424       LIBRARY_COMPONENT
01425       RUNTIME_OUTPUT_DIRECTORY
01426       RUNTIME_INSTALL_DIRECTORY
01427       RUNTIME_COMPONENT
01428       PREFIX
01429       OUTPUT_NAME
01430       SUFFIX
01431       SOURCES
01432       COMPILE_FLAGS
01433       COMPILE
01434       LINK_DEPENDS
01435       TEST
01436       EXPORT
01437   )
01438   foreach (PROPERTY ${PROPERTIES})
01439     get_target_property (${PROPERTY} ${TARGET_UID} ${PROPERTY})
01440   endforeach ()
01441   # sanity checks of property values
01442   set (EXECUTABLE FALSE)
01443   set (LIBEXEC    FALSE)
01444   set (LIBRARY    FALSE)
01445   if (BASIS_TYPE MATCHES "^MCC_(EXECUTABLE|LIBEXEC|LIBRARY)$")
01446     set (${CMAKE_MATCH_1} TRUE)
01447     if (LIBEXEC)
01448       set (EXECUTABLE TRUE)
01449     endif ()
01450   else ()
01451     message (FATAL_ERROR "Target ${TARGET_UID}: Invalid BASIS_TYPE: ${BASIS_TYPE}")
01452   endif ()
01453   list (GET SOURCES 0 BUILD_DIR) # strange, but CMake stores path to internal build directory here
01454   list (REMOVE_AT SOURCES 0)
01455   set (BUILD_DIR "${BUILD_DIR}.dir")
01456   if (NOT SOURCES)
01457     message (FATAL_ERROR "Target ${TARGET_UID}: Empty SOURCES list!"
01458                          " Have you accidentally modified this read-only property or"
01459                          " is your (newer) CMake version not compatible with BASIS?")
01460   endif ()
01461   list (GET SOURCES 0 MAIN_SOURCE) # entry point
01462   if (NOT RUNTIME_COMPONENT)
01463     set (RUNTIME_COMPONENT "Unspecified")
01464   endif ()
01465   if (NOT LIBRARY_COMPONENT)
01466     set (LIBRARY_COMPONENT "Unspecified")
01467   endif ()
01468   # output name
01469   if (NOT OUTPUT_NAME)
01470     set (OUTPUT_NAME "${TARGET_NAME}")
01471   endif ()
01472   if (PREFIX)
01473     set (OUTPUT_NAME "${PREFIX}${OUTPUT_NAME}")
01474   endif ()
01475   if (SUFFIX)
01476     set (OUTPUT_NAME "${OUTPUT_NAME}${SUFFIX}")
01477   endif ()
01478   # split compile flags at spaces into list
01479   basis_string_to_list (MCC_USER_FLAGS "${COMPILE_FLAGS}")
01480   # initialize dependencies of custom build command
01481   set (DEPENDS ${SOURCES})
01482   # build output file and comment
01483   file (RELATIVE_PATH REL "${CMAKE_BINARY_DIR}" "${BUILD_DIR}/${OUTPUT_NAME}")
01484   if (LIBRARY)
01485     set (BUILD_OUTPUT "${LIBRARY_OUTPUT_DIRECTORY}/${OUTPUT_NAME}")
01486     set (BUILD_COMMENT "Building MATLAB library ${REL}...")
01487   else ()
01488     set (BUILD_OUTPUT "${RUNTIME_OUTPUT_DIRECTORY}/${OUTPUT_NAME}")
01489     set (BUILD_COMMENT "Building MATLAB executable ${REL}...")
01490   endif ()
01491   # --------------------------------------------------------------------------
01492   # assemble build command for build of executable wrapper script
01493   if (EXECUTABLE AND NOT COMPILE)
01494     # used to recognize source files which are located in the build tree
01495     basis_sanitize_for_regex (BINARY_CODE_DIR_RE "${BINARY_CODE_DIR}")
01496     # main MATLAB function and search path
01497     get_filename_component (MATLAB_COMMAND "${MAIN_SOURCE}" NAME_WE)
01498     get_filename_component (SOURCE_DIR     "${MAIN_SOURCE}" PATH)
01499     get_filename_component (SOURCE_PACKAGE "${SOURCE_DIR}"       NAME)
01500     if (SOURCE_PACKAGE MATCHES "^\\+")
01501       get_filename_component (SOURCE_DIR "${SOURCE_DIR}" PATH)
01502       set (MATLAB_COMMAND "${SOURCE_PACKAGE}.${MATLAB_COMMAND}")
01503       string (REGEX REPLACE "^\\+" "" MATLAB_COMMAND "${MATLAB_COMMAND}")
01504     else ()
01505       set (SOURCE_PACKAGE "${MATLAB_COMMAND}")
01506     endif ()
01507     basis_get_relative_path (DIR "${PROJECT_SOURCE_DIR}" "${SOURCE_DIR}")
01508     set (BINARY_DIR "${PROJECT_BINARY_DIR}/${DIR}") # location of configured sources
01509     # output file
01510     set (OUTPUT_FILE "${BUILD_OUTPUT}")
01511     get_filename_component (OUTPUT_DIR "${OUTPUT_FILE}" PATH)
01512     # installation
01513     set (INSTALL_FILE       "${BUILD_DIR}/${OUTPUT_NAME}")                          # file to be installed
01514     set (INSTALL_DIR        "${CMAKE_INSTALL_PREFIX}/${RUNTIME_INSTALL_DIRECTORY}") # location of installed wrapper
01515     set (INSTALL_SOURCE_DIR "${INSTALL_MATLAB_LIBRARY_DIR}")                        # location of installed MATLAB sources
01516     if (NOT SOURCE_PACKAGE MATCHES "^\\+")
01517       set (INSTALL_SOURCE_DIR "${INSTALL_SOURCE_DIR}/${SOURCE_PACKAGE}")
01518     endif ()
01519     # startup file
01520     if (SOURCE_PACKAGE MATCHES "^\\+")
01521       set (BUILD_STARTUP_FILE   "${BINARY_DIR}/${SOURCE_PACKAGE}/startup.m")
01522       set (INSTALL_STARTUP_FILE "${BUILD_DIR}/startup.m")
01523       set (INSTALL_STARTUP_DIR  "${CMAKE_INSTALL_PREFIX}/${INSTALL_SOURCE_DIR}/${SOURCE_PACKAGE}")
01524     else ()
01525       set (BUILD_STARTUP_FILE   "${BINARY_DIR}/startup.m")
01526       set (INSTALL_STARTUP_FILE "${BUILD_DIR}/startup.m")
01527       set (INSTALL_STARTUP_DIR  "${CMAKE_INSTALL_PREFIX}/${INSTALL_SOURCE_DIR}")
01528     endif ()
01529     get_filename_component (BUILD_STARTUP_DIR "${BUILD_STARTUP_FILE}" PATH)
01530     list (APPEND BUILD_OUTPUT "${BUILD_STARTUP_FILE}")
01531     list (APPEND BUILD_OUTPUT "${INSTALL_STARTUP_FILE}")
01532     # MATLAB search path
01533     #
01534     # The following paths are written to the startup M-file such
01535     # that they can conveniently be added to the search path within an
01536     # interactive MATLAB sesssion. The path to this startup file is
01537     # added to the search path by the wrapper executable first, and
01538     # then this startup file is evaluated which adds all additional
01539     # paths. If no startup file is specified, all paths are added
01540     # to the search path on the command-line in the wrapper script.
01541     set (BUILD_MATLABPATH)
01542     set (INSTALL_MATLABPATH)
01543     # if any source file was configured and hence is located in the
01544     # build tree instead of the source tree, add corresponding build
01545     # tree path to BUILD_MATLABPATH as well
01546     if (SOURCES MATCHES "^${BINARY_CODE_DIR_RE}")
01547       file (RELATIVE_PATH REL "${BUILD_STARTUP_DIR}" "${BINARY_DIR}")
01548       if (REL)
01549         list (APPEND BUILD_MATLABPATH "${REL}")
01550       else ()
01551         list (APPEND BUILD_MATLABPATH ".")
01552       endif ()
01553     endif ()
01554     list (APPEND BUILD_MATLABPATH "${SOURCE_DIR}")
01555     # The following is not required because startup script is located
01556     # in the same directory as the other sources and basis_generate_matlab_executable()
01557     # adds this path automatically in order to run the startup script.
01558     # Moreover, if anyone wants to run the startup script, they have to
01559     # add this path manually or make it the current directory first.
01560     #file (RELATIVE_PATH REL "${INSTALL_STARTUP_DIR}" "${CMAKE_INSTALL_PREFIX}/${INSTALL_SOURCE_DIR}")
01561     #if (REL)
01562     #  list (APPEND INSTALL_MATLABPATH "${REL}")
01563     #else ()
01564     #  list (APPEND INSTALL_MATLABPATH ".")
01565     #endif ()
01566     # link dependencies, i.e., MEX-files
01567     foreach (LINK_DEPEND ${LINK_DEPENDS})
01568       basis_get_target_uid (UID "${LINK_DEPEND}")
01569       if (TARGET ${UID})
01570         basis_get_target_location (LINK_DEPEND ${UID} ABSOLUTE)
01571         if (LINK_DEPEND MATCHES "\\.mex")
01572           get_filename_component (LINK_PATH "${LINK_DEPEND}" PATH)
01573           list (APPEND BUILD_MATLABPATH "${LINK_PATH}")
01574           list (APPEND DEPENDS ${UID})
01575         endif ()
01576         basis_get_target_location (LINK_DEPEND ${UID} POST_INSTALL)
01577         basis_get_target_property (BUNDLED     ${UID} BUNDLED)
01578         basis_get_target_property (IMPORTED    ${UID} IMPORTED)
01579         if (NOT IMPORTED OR BUNDLED)
01580           file (RELATIVE_PATH REL "${INSTALL_STARTUP_DIR}" "${LINK_DEPEND}")
01581           if (REL)
01582             set (LINK_DEPEND "${REL}")
01583           else ()
01584             set (LINK_DEPEND ".")
01585           endif ()
01586         endif ()
01587         if (LINK_DEPEND MATCHES "\\.mex")
01588           get_filename_component (LINK_PATH "${LINK_DEPEND}" PATH)
01589           list (APPEND INSTALL_MATLABPATH "${LINK_PATH}")
01590         endif ()
01591       elseif (IS_ABSOLUTE "${LINK_DEPEND}")
01592         if (IS_DIRECTORY "${LINK_DEPEND}")
01593           list (APPEND BUILD_MATLABPATH   "${LINK_DEPEND}")
01594           list (APPEND INSTALL_MATLABPATH "${LINK_DEPEND}")
01595         elseif (EXISTS "${LINK_DEPEND}" AND LINK_DEPEND MATCHES "\\.mex")
01596           get_filename_component (LINK_PATH "${LINK_DEPEND}" PATH)
01597           list (APPEND BUILD_MATLABPATH   "${LINK_PATH}")
01598           list (APPEND INSTALL_MATLABPATH "${LINK_PATH}")
01599         endif ()
01600       endif ()
01601     endforeach ()
01602     # cleanup directory paths
01603     string (REGEX REPLACE "/+"     "/"    BUILD_MATLABPATH   "${BUILD_MATLABPATH}")
01604     string (REGEX REPLACE "/+"     "/"    INSTALL_MATLABPATH "${INSTALL_MATLABPATH}")
01605     string (REGEX REPLACE "/(;|$)" "\\1"  BUILD_MATLABPATH   "${BUILD_MATLABPATH}")
01606     string (REGEX REPLACE "/(;|$)" "\\1"  INSTALL_MATLABPATH "${INSTALL_MATLABPATH}")
01607     # remove duplicates
01608     if (BUILD_MATLABPATH)
01609       list (REMOVE_DUPLICATES BUILD_MATLABPATH)
01610     endif ()
01611     if (INSTALL_MATLABPATH)
01612       list (REMOVE_DUPLICATES INSTALL_MATLABPATH)
01613     endif ()
01614     # configure build script
01615     set (BUILD_SCRIPT "${BUILD_DIR}/build.cmake")
01616     configure_file ("${BASIS_MODULE_PATH}/generate_matlab_executable.cmake.in" "${BUILD_SCRIPT}" @ONLY)
01617     # add custom command to build wrapper executable
01618     add_custom_command (
01619       OUTPUT          ${BUILD_OUTPUT}
01620       # rebuild when input sources were modified
01621       MAIN_DEPENDENCY "${MAIN_SOURCE}"
01622       DEPENDS         "${BUILD_SCRIPT}" "${CMAKE_CURRENT_LIST_FILE}" ${DEPENDS}
01623       # invoke MATLAB Compiler in either MATLAB or standalone mode
01624       # wrapping command in CMake execute_process () command allows for inspection
01625       # parsing of command output for error messages and specification of timeout
01626       COMMAND         "${CMAKE_COMMAND}" "-P" "${BUILD_SCRIPT}"
01627       # comment
01628       COMMENT         "${BUILD_COMMENT}"
01629     )
01630     # install source files - preserving relative paths in SOURCE_DIR
01631     foreach (SOURCE IN LISTS SOURCES)
01632       get_filename_component  (REL "${SOURCE}" PATH)
01633       if (SOURCE MATCHES "^${BINARY_CODE_DIR_RE}")
01634         basis_get_relative_path (REL "${BINARY_DIR}" "${REL}")
01635       else ()
01636         basis_get_relative_path (REL "${SOURCE_DIR}" "${REL}")
01637       endif ()
01638       if (REL MATCHES "^\\.?$|^\\.\\./")
01639         install (
01640           FILES       "${SOURCE}"
01641           DESTINATION "${INSTALL_SOURCE_DIR}"
01642           COMPONENT   "${RUNTIME_COMPONENT}"
01643         )
01644       else ()
01645         install (
01646           FILES       "${SOURCE}"
01647           DESTINATION "${INSTALL_SOURCE_DIR}/${REL}"
01648           COMPONENT   "${RUNTIME_COMPONENT}"
01649         )
01650       endif ()
01651     endforeach ()
01652   # --------------------------------------------------------------------------
01653   # assemble build command for build using MATLAB Compiler
01654   else ()
01655     set (INSTALL_FILE "${BUILD_OUTPUT}") # file to be installed
01656     # get list of libraries to link to (e.g., MEX-file)
01657     set (LINK_LIBS)
01658     foreach (LIB ${LINK_DEPENDS})
01659       basis_get_target_uid (UID "${LIB}")
01660       if (TARGET ${UID})
01661         basis_get_target_location (LIB_FILE ${UID} ABSOLUTE)
01662         list (APPEND DEPENDS ${UID})
01663       else ()
01664         set (LIB_FILE "${LIB}")
01665       endif ()
01666       list (APPEND LINK_LIBS "${LIB_FILE}")
01667     endforeach ()
01668     # MATLAB search path
01669     foreach (P ${SOURCES})
01670       get_filename_component (P "${P}" PATH)
01671       list (APPEND MATLABPATH "${P}")
01672     endforeach ()
01673     list (APPEND MATLABPATH ${BASIS_INCLUDE_DIRECTORIES})
01674     # MATLAB Compiler arguments
01675     string (REGEX REPLACE " +" ";" MCC_ARGS "${COMPILE_FLAGS}") # user specified flags
01676     foreach (P IN LISTS MATLABPATH)                             # search path, -I options
01677       string (REGEX REPLACE "/(\\+|@).*$" "" P "${P}")          # remove package/class subdirectories
01678       list (FIND MCC_ARGS "${P}" IDX)                           # skip if already added
01679       if (IDX GREATER 0)
01680         math (EXPR IDX "${IDX} - 1")
01681         list (GET MCC_ARGS ${IDX} OPT)
01682         if (NOT OPT MATCHES "^-I$")
01683           set (IDX -1)
01684         endif ()
01685       endif ()
01686       if (EXISTS "${P}" AND IDX EQUAL -1)
01687         list (APPEND MCC_ARGS -I "${P}")
01688       endif ()
01689     endforeach ()
01690     if (LIBRARY)
01691       list (APPEND MCC_ARGS -l)                                 # build library
01692     else ()                                                     #      or
01693       list (APPEND MCC_ARGS -m)                                 # build standalone application
01694     endif ()
01695     list (APPEND MCC_ARGS -d "${BUILD_DIR}")                    # (temp) output directory
01696     list (APPEND MCC_ARGS -o "${OUTPUT_NAME}")                  # output name
01697     list (APPEND MCC_ARGS ${MAIN_SOURCE})                       # main source M-file
01698     foreach (LIB ${LINK_LIBS})                                  # link libraries, e.g. MEX-files
01699       list (FIND MCC_ARGS "${LIB}" IDX)
01700       if (LIB AND IDX EQUAL -1)
01701         list (APPEND MCC_ARGS "-a" "${LIB}")
01702       endif ()
01703     endforeach ()
01704     # build command for invocation of MATLAB Compiler in standalone mode
01705     set (BUILD_CMD   "${MATLAB_MCC_EXECUTABLE}" ${MCC_USER_ARGS} ${MCC_ARGS})
01706     set (BUILD_LOG   "${BUILD_DIR}/build.log")
01707     set (WORKING_DIR "${SOURCE_DIRECTORY}")
01708     set (MATLAB_MODE OFF)
01709     # build command for invocation of MATLAB Compiler in MATLAB mode
01710     if (BASIS_MCC_MATLAB_MODE)
01711       set (MATLAB_MODE ON)
01712       if (NOT MATLAB_EXECUTABLE)
01713         message (WARNING "MATLAB executable not found. It is required to build target ${TARGET_UID} in MATLAB mode."
01714                          " Forgot to MATLAB as dependency? Otherwise, set MATLAB_EXECUTABLE manually and try again or set BASIS_MCC_MATLAB_MODE to OFF."
01715                          " Will build target ${TARGET_UID} in standalone mode instead.")
01716         set (MATLAB_MODE OFF)
01717       endif ()
01718       if (MATLAB_MODE)
01719         basis_list_to_delimited_string (ARGS "', '" NOAUTOQUOTE ${MCC_USER_ARGS} ${MCC_ARGS})
01720         set (
01721           BUILD_CMD
01722             "${MATLAB_EXECUTABLE}" # run MATLAB
01723             "-nosplash"            # do not display splash screen on start up
01724             "-nodesktop"           # run in command line mode
01725             "-nojvm"               # we do not need the Java Virtual Machine
01726             "-r" "try, mcc('-v', '${ARGS}'), catch err, fprintf(2, err.message), end, quit force"
01727         )
01728       endif ()
01729     endif ()
01730     # post-build command
01731     if (LIBRARY)
01732       set (
01733         POST_BUILD_COMMAND "${CMAKE_COMMAND}" -E copy
01734                            "${BUILD_DIR}/${OUTPUT_NAME}"
01735                            "${LIBRARY_OUTPUT_DIRECTORY}/${OUTPUT_NAME}"
01736       )
01737     else ()
01738       set (
01739         POST_BUILD_COMMAND "${CMAKE_COMMAND}" -E copy
01740                            "${BUILD_DIR}/${OUTPUT_NAME}"
01741                            "${RUNTIME_OUTPUT_DIRECTORY}/${OUTPUT_NAME}"
01742       )
01743     endif ()
01744     # add custom command to build executable using MATLAB Compiler
01745     add_custom_command (
01746       OUTPUT ${BUILD_OUTPUT}
01747       # rebuild when input sources were modified
01748       MAIN_DEPENDENCY "${MAIN_SOURCE}"
01749       DEPENDS         ${DEPENDS}
01750       # invoke MATLAB Compiler in either MATLAB or standalone mode
01751       # wrapping command in CMake execute_process() command allows for inspection
01752       # of command output for error messages and specification of timeout
01753       COMMAND "${CMAKE_COMMAND}"
01754               "-DCOMMAND=${BUILD_CMD}"
01755               "-DWORKING_DIRECTORY=${WORKING_DIR}"
01756               "-DTIMEOUT=${BASIS_MCC_TIMEOUT}"
01757               "-DRETRY_EXPRESSION=License checkout failed"
01758               "-DRETRY_ATTEMPTS=${BASIS_MCC_RETRY_ATTEMPTS}"
01759               "-DRETRY_DELAY=${BASIS_MCC_RETRY_DELAY}"
01760               "-DERROR_EXPRESSION=[E|e]rror"
01761               "-DOUTPUT_FILE=${BUILD_LOG}"
01762               "-DERROR_FILE=${BUILD_LOG}"
01763               "-DVERBOSE=OFF"
01764               "-DLOG_ARGS=ON"
01765               "-P" "${BASIS_SCRIPT_EXECUTE_PROCESS}"
01766       # post build command(s)
01767       COMMAND ${POST_BUILD_COMMAND}
01768       # inform user where build log can be found
01769       COMMAND "${CMAKE_COMMAND}" -E echo "Build log written to ${BUILD_LOG}"
01770       # comment
01771       COMMENT "${BUILD_COMMENT}"
01772       VERBATIM
01773     )
01774   endif ()
01775   # --------------------------------------------------------------------------
01776   # add custom target
01777   add_custom_target (_${TARGET_UID} DEPENDS ${BUILD_OUTPUT} SOURCES ${SOURCES})
01778   if (TARGET __${TARGET_UID}) # re-glob source files
01779     add_dependencies (_${TARGET_UID} __${TARGET_UID})
01780   endif ()
01781   add_dependencies (${TARGET_UID} _${TARGET_UID})
01782   # cleanup on "make clean"
01783   set_property (DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${BUILD_OUTPUT})
01784   if (COMPILE)
01785     set_property (
01786       DIRECTORY
01787       APPEND PROPERTY
01788         ADDITIONAL_MAKE_CLEAN_FILES
01789           "${BUILD_DIR}/${OUTPUT_NAME}.prj"
01790           "${BUILD_DIR}/mccExcludedFiles.log"
01791           "${BUILD_DIR}/mccBuild.log"
01792           "${BUILD_DIR}/readme.txt"
01793     )
01794     if (LIBRARY)
01795       # TODO
01796     else ()
01797       set_property (
01798         DIRECTORY
01799         APPEND PROPERTY
01800           ADDITIONAL_MAKE_CLEAN_FILES
01801             "${BUILD_DIR}/${OUTPUT_NAME}"
01802             "${BUILD_DIR}/run_${OUTPUT_NAME}.sh"
01803             "${BUILD_DIR}/${OUTPUT_NAME}_main.c"
01804             "${BUILD_DIR}/${OUTPUT_NAME}_mcc_component_data.c"
01805       )
01806     endif ()
01807   endif ()
01808   # export target
01809   if (EXPORT)
01810     if (TEST)
01811       basis_set_project_property (APPEND PROPERTY TEST_EXPORT_TARGETS "${TARGET_UID}")
01812     else ()
01813       basis_set_project_property (APPEND PROPERTY CUSTOM_EXPORT_TARGETS "${TARGET_UID}")
01814     endif ()
01815   endif ()
01816   # install executable or library
01817   if (LIBRARY)
01818     # TODO
01819   else ()
01820     if (RUNTIME_INSTALL_DIRECTORY)
01821       install (
01822         PROGRAMS    "${INSTALL_FILE}"
01823         DESTINATION "${RUNTIME_INSTALL_DIRECTORY}"
01824         COMPONENT   "${RUNTIME_COMPONENT}"
01825       )
01826     endif ()
01827   endif ()
01828   # done
01829   if (BASIS_VERBOSE)
01830     message (STATUS "Adding build command for target ${TARGET_UID}... - done")
01831   endif ()
01832 endfunction ()