# ============================================================================ # Copyright (c) 2011-2012 University of Pennsylvania # Copyright (c) 2013-2014 Andreas Schuh # All rights reserved. # # See COPYING file for license information or visit # http://opensource.andreasschuh.com/cmake-basis/download.html#license # ============================================================================ ############################################################################## # @file MatlabTools.cmake # @brief Enables use of MATLAB Compiler and build of MEX-files. # # @ingroup CMakeTools ############################################################################## if (__BASIS_MATLABTOOLS_INCLUDED) return () else () set (__BASIS_MATLABTOOLS_INCLUDED TRUE) endif () # ============================================================================ # modules # ============================================================================ # Note: Required because generate_matlab_executable.cmake uses this module. include (CMakeParseArguments) include ("${CMAKE_CURRENT_LIST_DIR}/CommonTools.cmake") include ("${CMAKE_CURRENT_LIST_DIR}/UtilitiesTools.cmake") # ============================================================================ # options # ============================================================================ ## @addtogroup BasisSettings # @{ ## @brief Enable/Disable compilation of MATLAB sources if the MATLAB Compiler is available. option (BASIS_COMPILE_MATLAB "Enable compilation of MATLAB sources if MATLAB Compiler (mcc) is available." ON) mark_as_advanced (BASIS_COMPILE_MATLAB) # ---------------------------------------------------------------------------- ## @brief Add global MATLAB MEX-script options to CMake cache # # @par MATLAB MEX-script options: # <table border="0"> # <tr> # @tp @b BASIS_MEX_TIMEOUT @endtp # <td>Timeout for MEX script execution.</td> # </tr> # <tr> # @tp @b BASIS_MEX_FLAGS @endtp # <td>Compile flags used to build MEX-files using the MEX script.</td> # </tr> # </table> macro(basis_add_mex_options) if (MATLAB_MEX_EXECUTABLE) set (BASIS_MEX_TIMEOUT "600" CACHE STRING "Timeout for MEX script execution") set (BASIS_MEX_FLAGS "" CACHE STRING "Common MEX switches (separated by ' '; use '\\' to mask ' ').") mark_as_advanced (BASIS_MEX_FLAGS) mark_as_advanced (BASIS_MEX_TIMEOUT) endif () endmacro () # ---------------------------------------------------------------------------- ## @brief Add global MATLAB Compiler (mcc) options to CMake cache # # @par MATLAB Compiler options: # <table border="0"> # <tr> # @tp @b BASIS_MCC_MATLAB_MODE @endtp # <td>Enable/Disable invocation of MATLAB Compiler in MATLAB mode.</td> # </tr> # <tr> # @tp @b BASIS_MCC_FLAGS @endtp # <td>Compile flags used to build MATLAB Compiler targets.</td> # </tr> # <tr> # @tp @b BASIS_MCC_TIMEOUT @endtp # <td>Timeout for building MATLAB Compiler targets.</td> # </tr> # <tr> # @tp @b BASIS_MCC_RETRY_ATTEMPTS @endtp # <td>Maximum number of retries on MATLAB Compiler license checkout.</td> # </tr> # <tr> # @tp @b BASIS_MCC_RETRY_DELAY @endtp # <td>Delay between retries to build MATLAB Compiler compiled targets on license checkout errors.</td> # </tr> # </table> macro(basis_add_mcc_options) option ( BASIS_MCC_MATLAB_MODE "Prefer MATLAB mode over standalone mode to invoke MATLAB Compiler to release MCC licence ASAP." OFF # using MATLAB mode is preferred when the license is shared # among users as it releases the license immediately once done ) set ( BASIS_MCC_FLAGS "-R -singleCompThread" CACHE STRING "Common MATLAB Compiler flags (separated by ' '; use '\\' to mask ' ')." ) set (BASIS_MCC_TIMEOUT "1800" CACHE STRING "Timeout for MATLAB Compiler execution") set (BASIS_MCC_RETRY_ATTEMPTS "4" CACHE STRING "Maximum number of retries on MATLAB Compiler license checkout error.") set (BASIS_MCC_RETRY_DELAY "30" CACHE STRING "Delay between retries to build MATLAB Compiler compiled targets on license checkout error.") mark_as_advanced (BASIS_MCC_MATLAB_MODE) mark_as_advanced (BASIS_MCC_FLAGS) mark_as_advanced (BASIS_MCC_TIMEOUT) mark_as_advanced (BASIS_MCC_RETRY_ATTEMPTS) mark_as_advanced (BASIS_MCC_RETRY_DELAY) endmacro () ## @} # end of Doxygen group # ============================================================================ # utilities # ============================================================================ # ---------------------------------------------------------------------------- ## @brief Determine version of MATLAB installation. # # @param [out] VERSION Value returned by the "version" command of MATLAB or # an empty string if execution of MATLAB failed. # # @returns Sets the variable named by @p VERSION to the full MATLAB version. # # @ingroup CMakeUtilities function (basis_get_full_matlab_version VERSION) if (NOT MATLAB_EXECUTABLE) set (${VERSION} "" PARENT_SCOPE) return () endif () set (WORKING_DIR "${CMAKE_BINARY_DIR}/CMakeFiles") set (OUTPUT_FILE "${WORKING_DIR}/MatlabVersion.txt") # read MATLAB version from existing output file set (_MATLAB_VERSION) if (EXISTS "${OUTPUT_FILE}") file (READ "${OUTPUT_FILE}" LINES) string (REGEX REPLACE "\n" ";" LINES "${LINES}") string (REGEX REPLACE "^;|;$" "" LINES "${LINES}") list (LENGTH LINES NLINES) if (NLINES EQUAL 2) list (GET LINES 0 _MATLAB_EXECUTABLE) list (GET LINES 1 _MATLAB_VERSION) basis_sanitize_for_regex (RE "${MATLAB_EXECUTABLE}") if (NOT _MATLAB_EXECUTABLE MATCHES "^${RE}$") set (_MATLAB_VERSION) endif () unset (RE) endif () endif () # run matlab command to write return value of "version" command to text file if (NOT _MATLAB_VERSION) message (STATUS "Determining MATLAB version...") set (CMD "${MATLAB_EXECUTABLE}" -nodesktop -nosplash -singleCompThread) if (WIN32) list (APPEND CMD -automation) else () list (APPEND CMD -nojvm) endif () file (WRITE "${WORKING_DIR}/basis_get_full_matlab_version.m" "% DO NOT EDIT. Automatically created by BASIS (basis_get_full_matlab_version). fid = fopen ('${OUTPUT_FILE}', 'w') if fid == -1, fprintf(2, '??? Error: Failed to open file ${OUTPUT_FILE} for writing!'), quit force, end fprintf (fid, '${MATLAB_EXECUTABLE}\\n%s\\n', version) fclose (fid) quit force " ) execute_process ( COMMAND ${CMD} -r "cd('${WORKING_DIR}');basis_get_full_matlab_version;" WORKING_DIRECTORY "${WORKING_DIR}" RESULT_VARIABLE RETVAL TIMEOUT 600 # MATLAB startup can be *very* slow the first time ERROR_VARIABLE STDERR OUTPUT_QUIET ) if (NOT RETVAL EQUAL 0 OR STDERR MATCHES "\\?\\?\\? Error") set (${VERSION} "" PARENT_SCOPE) if (RETVAL MATCHES "timeout") set (REASON ": ${RETVAL}") elseif (NOT RETVAL EQUAL 0) set (REASON " with exit code: ${RETVAL}") else () set (REASON ": Failed to open file ${OUTPUT_FILE} for writing") endif () message (STATUS "Determining MATLAB version... - failed${REASON}") return () endif () # wait until MATLAB process terminated entirely and wrote the (buffered?) file set (nsleep_count 0) set (nsleep_max 30) while (NOT EXISTS "${OUTPUT_FILE}") math (EXPR nsleep_count "${nsleep_count}+1") if (nsleep_count GREATER nsleep_max) message (STATUS "Determining MATLAB version... - failed: File ${OUTPUT_FILE} still non-existent after ${nsleep_max}s of successful MATLAB termination") return () endif () if (WIN32) execute_process (COMMAND ping 1.1.1.1 -n 1 -w 1000 OUTPUT_QUIET) else () execute_process (COMMAND sleep 1 OUTPUT_QUIET) endif () endwhile () # read MATLAB version from text file file (READ "${OUTPUT_FILE}" LINES) string (REGEX REPLACE "\n" ";" LINES "${LINES}") string (REGEX REPLACE "^;|;$" "" LINES "${LINES}") list (LENGTH LINES NLINES) if (NLINES EQUAL 2) list (GET LINES 1 _MATLAB_VERSION) else () set (${VERSION} "" PARENT_SCOPE) message (STATUS "Determining MATLAB version... - failed") return () endif () if (BASIS_VERBOSE) message (STATUS "Determining MATLAB version... - done: ${_MATLAB_VERSION}") else () message (STATUS "Determining MATLAB version... - done") endif () endif () # return set (${VERSION} "${_MATLAB_VERSION}" PARENT_SCOPE) endfunction () # ---------------------------------------------------------------------------- ## @brief Get version of MATLAB installation. # # @param [out] ARGV1 If given, the named variable is set to the version string # ("<major>.<minor>.<patch>") of the MATLAB installation. # Otherwise, the variables @c MATLAB_VERSION_STRING, # @c MATLAB_VERSION_MAJOR, @c MATLAB_VERSION_MINOR, # @c MATLAB_VERSION_PATCH, and @c MATLAB_RELEASE are set # in the scope of the caller. # # @ingroup CMakeUtilities function (basis_get_matlab_version) if (ARGC GREATER 1) message (FATAL_ERROR "basis_get_matlab_version(): Too many arguments!") endif () basis_get_full_matlab_version (VERSION) if (VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") set (VERSION_STRING "${CMAKE_MATCH_0}") set (VERSION_MAJOR "${CMAKE_MATCH_1}") set (VERSION_MINOR "${CMAKE_MATCH_2}") set (VERSION_PATCH "${CMAKE_MATCH_3}") else () set (VERSION_STRING "0.0") set (VERSION_MAJOR "0") set (VERSION_MINOR "0") set (VERSION_PATCH "0") endif () if (ARGC EQUAL 1) set (${ARGV0} "${VERSION_STRING}" PARENT_SCOPE) else () set (MATLAB_VERSION_STRING "${VERSION_STRING}" PARENT_SCOPE) set (MATLAB_VERSION_MAJOR "${VERSION_MAJOR}" PARENT_SCOPE) set (MATLAB_VERSION_MINOR "${VERSION_MINOR}" PARENT_SCOPE) set (MATLAB_VERSION_PATCH "${VERSION_PATCH}" PARENT_SCOPE) if (VERSION MATCHES ".*\\\((.+)\\\)") set (MATLAB_RELEASE "${CMAKE_MATCH_1}" PARENT_SCOPE) else () set (MATLAB_RELEASE "" PARENT_SCOPE) endif () endif () endfunction () # ---------------------------------------------------------------------------- ## @brief Get release version of MATLAB installation. # # @param [out] ARGV1 If given, the named variable is set to the release string # of the MATLAB installation, e.g., "R2009b". Otherwise, # the variable @c MATLAB_RELEASE is set in the scope of the # caller. # # @ingroup CMakeUtilities function (basis_get_matlab_release) if (ARGC GREATER 1) message (FATAL_ERROR "basis_get_matlab_release(): Too many arguments!") endif () basis_get_full_matlab_version (VERSION) if (VERSION MATCHES ".*\\\((.+)\\\)") set (RELEASE "${CMAKE_MATCH_1}") else () set (RELEASE "") endif () if (ARGC EQUAL 1) set (${ARGV0} "${RELEASE}" PARENT_SCOPE) else () set (MATLAB_RELEASE "${RELEASE}") endif () endfunction () # ---------------------------------------------------------------------------- ## @brief Determine extension of MEX-files for this architecture. # # @param [out] ARGN The first argument ARGV0 is set to the extension of # MEX-files (excluding '.'). If the CMake variable MEX_EXT # is set, its value is returned. Otherwise, this function # tries to determine it from the system information. # If the extension could not be determined, an empty string # is returned. If no argument is given, the extension is # cached as the variable MEX_EXT. # # @returns Sets the variable named by the first argument to the # platform-specific extension of MEX-files. # # @ingroup CMakeUtilities function (basis_mexext) # default return value set (MEXEXT "${MEX_EXT}") # use MEXEXT if possible if (NOT MEXEXT AND MATLAB_MEXEXT_EXECUTABLE) execute_process ( COMMAND "${MATLAB_MEXEXT_EXECUTABLE}" RESULT_VARIABLE RETVAL OUTPUT_VARIABLE MEXEXT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE ) if (RETVAL) set (MEXEXT "") endif () endif () # otherwise, determine extension given CMake variables describing the system if (NOT MEXEXT) if (CMAKE_SYSTEM_NAME MATCHES "Linux") if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64" OR CMAKE_SIZEOF_VOID_P MATCHES 8) set (MEXEXT "mexa64") elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "x86" OR CMAKE_SYSTEM_PROCESSOR MATCHES "i686") set (MEXEXT "mexglx") endif () elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64" OR CMAKE_SIZEOF_VOID_P MATCHES 8) set (MEXEXT "mexw64") elseif (CMAKE_SYSTEM_PROCESSOR MATCHES "x86" OR CMAKE_SYSTEM_PROCESSOR MATCHES "i686") set (MEXEXT "mexw32") endif () elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64" OR CMAKE_SIZEOF_VOID_P MATCHES 8) set (MEXEXT "mexmaci64") else () set (MEXEXT "mexmaci") endif () elseif (CMAKE_SYSTEM_NAME MATCHES "SunOS") set (MEXEXT "mexs64") endif () endif () # return value if (ARGC GREATER 0) set ("${ARGV0}" "${MEXEXT}" PARENT_SCOPE) else () if (NOT DEFINED MEX_EXT) set (MARKIT 1) else () set (MARKIT 0) endif () set (MEX_EXT "${MEXEXT}" CACHE STRING "The extension of MEX-files for this architecture." FORCE) if (MARKIT) mark_as_advanced (MEX_EXT) endif () endif () endfunction () # ---------------------------------------------------------------------------- ## @brief This function writes a MATLAB M-file with addpath() statements. # # This function writes an MATLAB M-file into the top directory of the build # tree which contains an addpath() statement for each directory that was added # via basis_include_directories(). # # @returns Creates file add_\<project\>_paths.m in the current binary directory. # # @ingroup CMakeUtilities function (basis_create_addpaths_mfile) basis_get_project_property (INCLUDE_DIRS PROPERTY PROJECT_INCLUDE_DIRS) basis_write_addpaths_mfile ("${CMAKE_CURRENT_BINARY_DIR}/add_${PROJECT_NAME_L}_paths.m" ${INCLUDE_DIRS}) endfunction () # ---------------------------------------------------------------------------- ## @brief This function writes a MATLAB M-file with addpath() statements. # # @param [in] MFILE Name of M-file. # @param [in] ARGN The remaining arguments are the paths which should be added # to the search path of MATLAB when this M-file is being # executed. If the option APPEND is given, the paths are # appended to the specified M-file. Otherwise, any existing # file will be overwritten. The given directory paths can # be relative, in which case they are interpreted relative # to the location of the written M-file using the mfilename() # function of MATLAB. # # @ingroup CMakeUtilities macro (basis_write_addpaths_mfile MFILE) CMAKE_PARSE_ARGUMENTS (ARGN "APPEND" "" "" ${ARGN}) if (NOT ARGN_APPEND) file (WRITE "${MFILE}" "% DO NOT edit. This file is automatically generated by BASIS. [mfiledir, ~, ~, ~] = fileparts(mfilename('fullpath'));\n") endif () foreach (P IN LISTS ARGN_UNPARSED_ARGUMENTS) if (P MATCHES "^\\.?$") file (APPEND "${MFILE}" "addpath(mfiledir);\n") elseif (IS_ABSOLUTE "${P}") file (APPEND "${MFILE}" "addpath('${P}');\n") else () file (APPEND "${MFILE}" "addpath([mfiledir '/${P}']);\n") endif () endforeach () endmacro () # ---------------------------------------------------------------------------- ## @brief Generate MATLAB wrapper executable. # # This function writes a Bash script on Unix or a Windows Command script on # Windows platforms which execute the specified MATLAB command using the -r # option of the matlab executable and the -nodesktop and -nosplash options. # It is used by the build scripts generated by the basis_build_mcc_target() # in order to build an executable from MATLAB source files without the use # of the MATLAB Compiler. In this case, the MATLAB source files are simply # copied to the installation directory and the wrapper script written by # this function used to execute the main function with the command-line # arguments passed on to this executable. # # @param [in] OUTPUT_FILE Name of the output executable file. # @param [in] ARGN The remaining options # @par # <table border=0> # <tr> # @tp @b DESTINATION dir @endtp # <td>Installation destination. (default: directory of @c OUTPUT_FILE)</td> # </tr> # <tr> # @tp @b COMMAND name @endtp # <td>Name of the MATLAB command to execute, i.e., # the name of the main function.</td> # </tr> # <tr> # @tp @b STARTUP mfile @endtp # <td>Absolute path of a startup M-file.</td> # </tr> # <tr> # @tp @b MATLABPATH dir1[ dir2...] # <td>List of directories to be added to the MATLAB search path.</td> # </tr> # <tr> # @tp @b OPTIONS opt1[ opt2...] # <td>Additional options to pass on to the <tt>matlab</tt> executable.</td> # </tr> # </table> function (basis_generate_matlab_executable OUTPUT_FILE) CMAKE_PARSE_ARGUMENTS (ARGN "" "COMMAND;STARTUP;DESTINATION" "OPTIONS;MATLABPATH" ${ARGN}) if (NOT MATLAB_EXECUTABLE) set (MATLAB_EXECUTABLE matlab) endif () if (NOT OUTPUT_FILE) message ("basis_generate_matlab_executable(): Missing OUTPUT_FILE argument!") endif () if (NOT ARGN_DESTINATION) get_filename_component (ARGN_DESTINATION "${OUTPUT_FILE}" PATH) endif () # source path set (MATLABPATH) foreach (P IN LISTS ARGN_MATLABPATH) if (P MATCHES "^\\.?$") set (P "$__DIR__") elseif (NOT IS_ABSOLUTE "${P}") set (P "$__DIR__/${P}") endif () list (APPEND MATLABPATH "${P}") endforeach () if (MATLABPATH) list (REMOVE_DUPLICATES MATLABPATH) endif () # startup script if (ARGN_STARTUP) get_filename_component (STARTUP_COMMAND "${ARGN_STARTUP}" NAME_WE) get_filename_component (STARTUP_DIR "${ARGN_STARTUP}" PATH) get_filename_component (STARTUP_PKG "${STARTUP_DIR}" NAME) if (STARTUP_PKG MATCHES "^\\+") get_filename_component (STARTUP_DIR "${STARTUP_DIR}" PATH) string (REGEX REPLACE "^\\+" "" STARTUP_PKG "${STARTUP_PKG}") set (STARTUP_COMMAND "${STARTUP_PKG}.${STARTUP_COMMAND}") endif () if (IS_ABSOLUTE "${STARTUP_DIR}") file (RELATIVE_PATH STARTUP_DIR "${ARGN_DESTINATION}" "${STARTUP_DIR}") endif () if (STARTUP_DIR) set (STARTUP_DIR "$__DIR__/${STARTUP_DIR}") else () set (STARTUP_DIR "$__DIR__") endif () list (FIND MATLABPATH "${STARTUP_DIR}" IDX) if (IDX EQUAL -1) set (STARTUP_CODE ", addpath('${STARTUP_DIR}')") else () set (STARTUP_CODE) endif () set (STARTUP_CODE "${STARTUP_CODE}, ${STARTUP_COMMAND}") else () set (STARTUP_CODE) endif () # write wrapper executable if (MATLABPATH) basis_list_to_delimited_string (MATLABPATH "', '" NOAUTOQUOTE ${MATLABPATH}) set (MATLABPATH ", addpath('${MATLABPATH}', '-begin')") else () set (MATLABPATH) endif () file (WRITE "${OUTPUT_FILE}" # note that Bash variables within the script are denoted by $var # instead of ${var} to prevent CMake from substituting these patterns "#! /bin/bash readonly __DIR__=\"${BASIS_BASH___DIR__}\" errlog= finish() { local status=0 if [[ -n \"$errlog\" ]]; then grep '??? Error' \"$errlog\" &> /dev/null [[ $? -ne 0 ]] || status=1 /bin/rm \"$errlog\" fi exit $status } if [[ -d \"$TMPDIR\" ]]; then tmpdir=$TMPDIR else tmpdir=/tmp fi errlog=`mktemp \"$tmpdir/${ARGN_COMMAND}-log.XXXXXX\"` [[ $? -eq 0 ]] || { echo \"Failed to create temporary log file in '$tmpdir'!\" 1>&2 exit 1 } args= while [[ $# -gt 0 ]]; do [[ -z \"$args\" ]] || args=\"$args, \" args=\"$args'$1'\" shift done echo 'Launching MATLAB to execute ${ARGN_COMMAND} function...' trap finish EXIT # DO NOT install trap earlier ! '${MATLAB_EXECUTABLE}' -nodesktop -nosplash ${ARGN_OPTIONS} \\ -r \"try${MATLABPATH}${STARTUP_CODE}, ${ARGN_COMMAND}($args), catch err, fprintf(2, ['??? Error executing ${ARGN_COMMAND}\\n' err.message '\\n']), end, quit force\" \\ 2> >(tee \"$errlog\" >&2)" ) # end of file(WRITE) command if (UNIX) execute_process (COMMAND /bin/chmod +x "${OUTPUT_FILE}") endif () endfunction () # ============================================================================ # MEX-file target # ============================================================================ # ---------------------------------------------------------------------------- ## @brief Add MEX-file target. # # @note This function should not be used directly. Instead, it is called # by basis_add_library() if the (detected) programming language # of the given source code files is @c CXX (i.e., C/C++) and the @c MEX # type option is given. # # This function is used to add a shared library target which is built # using the MATLAB MEX script (mex). # # By default, the BASIS C++ utilities library is added as link dependency. # If none of the BASIS C++ utilities are used by this target, the option # NO_BASIS_UTILITIES can be given. To enable this option by default, set the # variable @c BASIS_UTILITIES to @c FALSE, best in the <tt>Settings.cmake</tt> # file located in the @c PROJECT_CONFIG_DIR (add such file if missing). # If the use of the BASIS C++ utilities is disabled by default, the # @c USE_BASIS_UTILITIES option can be used to enable them for this target # only. Note that the utilities library is a static library and thus the linker # would simply not include any of the BASIS utility functions in the final # binary file if not used. The only advantage of setting @c BASIS_UTILITIES to # @c FALSE or to always specify @c NO_BASIS_UTILITIES if no target uses the # utilities is that the BASIS utilities library will not be build in this case. # # A custom CMake build target with the following properties is added by this # function to the build system. These properties are used by # basis_build_mex_target() to generate a build script written in CMake # code which is executed by a custom CMake command. Before the invokation of # basis_build_mex_target(), the target properties can be modified using # basis_set_target_properties(). # # @note Custom BASIS build targets are finalized by BASIS using basis_project_end(), # i.e., the end of the root CMake configuration file of the (sub-)project. # # @par Properties on script library targets # <table border=0> # <tr> # @tp @b MFILE file @endtp # <td>MATLAB source file with function prototype and documentation of MEX-file. # (default: none)</td> # </tr> # <tr> # @tp @b PREFIX prefix @endtp # <td>Output prefix of build MEX-file such as package name # (the prefix must include the leading + and trailing /).</td> # </tr> # </table> # # @attention Properties documented as read-only must not be modified. # # An install command for the added library target is added by this function # as well. The MEX-file will be installed as part of the specified @p COMPONENT # in the @c INSTALL_LIBRARY_DIR on Unix and @c INSTALL_RUNTIME_DIR on Windows. # # @param [in] TARGET_NAME Name of build target. # @param [in] ARGN The remaining arguments are parsed and the following # arguments extracted. All unparsed arguments are treated # as the source files of the MEX-file. # @par # <table border="0"> # <tr> # @tp @b COMPONENT name @endtp # <td>Name of installation component as part of which this MEX-file is being # installed if the @c LIBRARY_INSTALL_DIRECTORY property is not "none". # (default: @c BASIS_LIBRARY_COMPONENT)</td> # </tr> # <tr> # @tp @b [NO]EXPORT @endtp # <td>Whether to export this target. (default: @c TRUE)</td> # </tr> # <tr> # @tp @b NO_BASIS_UTILITIES @endtp # <td>Specify that the BASIS utilities are not used by this MEX-file and # hence no link dependency on the BASIS utilities shall be added. # (default: @c NOT BASIS_UTILITIES)</td> # </tr> # <tr> # @tp @b USE_BASIS_UTILITIES @endtp # <td>Specify that the BASIS utilities are used and required by this MEX-file # and hence a link dependency on the BASIS utilities must be added. # (default: @c BASIS_UTILITIES)</td> # </tr> # </table> # # @returns Adds custom target to build MEX-file using the MEX script. # # @sa basis_add_library() # # @ingroup CMakeUtilities function (basis_add_mex_file TARGET_NAME) # check target name basis_check_target_name ("${TARGET_NAME}") basis_make_target_uid (TARGET_UID "${TARGET_NAME}") message (STATUS "Adding MEX-file ${TARGET_UID}...") # required commands available ? if (NOT MATLAB_MEX_EXECUTABLE) message (FATAL_ERROR "MATLAB MEX script (mex) not found! It is required to build target ${TARGET_UID}." " Forgot to add MATLAB{mex} as dependency? Otherwise, set MATLAB_MEX_EXECUTABLE manually and try again.") endif () basis_add_mex_options() # parse arguments CMAKE_PARSE_ARGUMENTS ( ARGN "USE_BASIS_UTILITIES;NO_BASIS_UTILITIES;EXPORT;NOEXPORT" "COMPONENT;DESTINATION" "" ${ARGN} ) set (SOURCES ${ARGN_UNPARSED_ARGUMENTS}) basis_set_flag (ARGN EXPORT ${BASIS_EXPORT_DEFAULT}) if (ARGN_USE_BASIS_UTILITIES AND ARGN_NO_BASIS_UTILITIES) message (FATAL_ERROR "Target ${TARGET_UID}: Options USE_BASIS_UTILITIES and NO_BASIS_UTILITIES are mutually exclusive!") endif () if (ARGN_USE_BASIS_UTILITIES) set (USES_BASIS_UTILITIES TRUE) elseif (ARGN_NO_BASIS_UTILITIES) set (USES_BASIS_UTILITIES FALSE) else () set (USES_BASIS_UTILITIES ${BASIS_UTILITIES}) endif () basis_mexext (MEXEXT) # IS_TEST flag basis_sanitize_for_regex (RE "${PROJECT_TESTING_DIR}") if (CMAKE_CURRENT_SOURCE_DIR MATCHES "^${RE}") set (IS_TEST TRUE) else () set (IS_TEST FALSE) endif () # installation component if (NOT ARGN_COMPONENT) set (ARGN_COMPONENT "${BASIS_LIBRARY_COMPONENT}") endif () if (NOT ARGN_COMPONENT) set (ARGN_COMPONENT "Unspecified") endif () # installation directory if (ARGN_DESTINATION) if (ARGN_DESTINATION MATCHES "^[nN][oO][nN][eE]$") set (ARGN_DESTINATION) elseif (IS_ABSOLUTE "${ARGN_DESTINATION}") file (RELATIVE_PATH ARGN_DESTINATION "${CMAKE_INSTALL_PREFIX}" "${ARGN_DESTINATION}") endif () else () set (ARGN_DESTINATION "${INSTALL_MATLAB_LIBRARY_DIR}") endif () # configure (.in) source files basis_configure_sources (SOURCES ${SOURCES}) # add custom target add_custom_target (${TARGET_UID} ALL SOURCES ${SOURCES}) get_directory_property (INCLUDE_DIRS INCLUDE_DIRECTORIES) get_directory_property (LINK_DIRS LINK_DIRECTORIES) if (MATLAB_LIBRARY_DIR) list (INSERT LINK_DIRS 0 "${MATLAB_LIBRARY_DIR}") endif () set_target_properties ( ${TARGET_UID} PROPERTIES LANGUAGE "CXX" BASIS_TYPE MEX BASIS_UTILITIES ${USES_BASIS_UTILITIES} BASIS_INCLUDE_DIRECTORIES "${INCLUDE_DIRS}" BASIS_LINK_DIRECTORIES "${LINK_DIRS}" BUILD_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TARGET_UID}" SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" BINARY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" LIBRARY_OUTPUT_DIRECTORY "${BINARY_MATLAB_LIBRARY_DIR}" LIBRARY_INSTALL_DIRECTORY "${ARGN_DESTINATION}" LIBRARY_COMPONENT "${ARGN_COMPONENT}" COMPILE_FLAGS "${BASIS_MEX_FLAGS}" LINK_FLAGS "" LINK_DEPENDS "${LINK_DEPENDS}" PREFIX "" OUTPUT_NAME "" SUFFIX ".${MEXEXT}" MFILE "" TEST ${IS_TEST} EXPORT ${EXPORT} ) # link to BASIS utilities if (USES_BASIS_UTILITIES) basis_target_link_libraries (.${TARGET_UID} basis) endif () # add target to list of targets basis_set_project_property (APPEND PROPERTY TARGETS "${TARGET_UID}") message (STATUS "Adding MEX-file ${TARGET_UID}... - done") endfunction () # ============================================================================ # MATLAB Compiler target # ============================================================================ # ---------------------------------------------------------------------------- ## @brief Add MATLAB Compiler target. # # @note This function should not be used directly. Instead, it is called # by either basis_add_executable() or basis_add_library() if the # (detected) programming language of the given source code files is # @c MATLAB. # # This function is used to add an executable or shared library target which is # built using the MATLAB Compiler (MCC). # # A custom CMake build target with the following properties is added by this # function to the build system. These properties are used by # basis_build_mcc_target() to generate a build script written in CMake # code which is executed by a custom CMake command. Before the invokation of # basis_build_mcc_target(), the target properties can be modified using # basis_set_target_properties(). # # @note Custom BASIS build targets are finalized by BASIS using basis_project_end(), # i.e., the end of the root CMake configuration file of the (sub-)project. # # @par Properties on MATLAB Compiler targets # <table border=0> # <tr><td>TODO</td></tr> # </table> # # An install command for the added executable or library target is added by # this function as well. The executable will be installed as part of the # @p RUNTIME_COMPONENT in the directory @c INSTALL_RUNTIME_DIR. The runtime # library will be installed as part of the @p RUNTIME_COMPONENT in the directory # @c INSTALL_LIBRARY_DIR on Unix and @c INSTALL_RUNTIME_DIR on Windows. # Static/import libraries will be installed as part of the @p LIBRARY_COMPONENT # in the directory @c INSTALL_ARCHIVE_DIR. # # @note If this function is used within the @c PROJECT_TESTING_DIR, the built # executable is output to the @c BINARY_TESTING_DIR directory tree instead. # Moreover, no installation rules are added. Test executables are further # not exported, regardless of the value of the @c EXPORT property. # # @param [in] TARGET_NAME Name of build target. # @param [in] ARGN The remaining arguments are parsed and the following # arguments extracted. All unparsed arguments are treated # as the MATLAB or C/C++ source files, respectively. # @par # <table border="0"> # <tr> # @tp <b>EXECUTABLE</b>|<b>LIBEXEC</b>|<b>SHARED</b> @endtp # <td>Type of the MATLAB Compiler target which can be either a stand-alone # executable, an auxiliary executable, or a shared library. # (default: @c EXECUTABLE)</td> # </tr> # <tr> # @tp @b COMPONENT name @endtp # <td>Name of component as part of which this executable or library will be # installed if the @c RUNTIME_INSTALL_DIRECTORY or @c LIBRARY_INSTALL_DIRECTORY # property is not "none". Used only if @p RUNTIME_COMPONENT or # @p LIBRARY_COMPONENT not specified. # (default: see @p RUNTIME_COMPONENT and @p LIBRARY_COMPONENT arguments)</td> # </tr> # <tr> # @tp @b DESTINATION dir @endtp # <td>Installation directory for executable or runtime and library component # of shared library relative to @c CMAKE_INSTALL_PREFIX. Used only if # @p RUNTIME_DESTINATION or @p LIBRARY_DESTINATION not specified. # If "none" (case-insensitive) is given as argument, no default installation # rules are added. (default: see @p RUNTIME_DESTINATION and # @p LIBRARY_DESTINATION arguments)</td> # </tr> # <tr> # @tp @b LIBRARY_COMPONENT name @endtp # <td>Name of component as part of which import/static library will be intalled # if a shared library is build and the @c LIBRARY_INSTALL_DIRECTORY property is # not "none". (default: @c COMPONENT if specified or @c BASIS_LIBRARY_COMPONENT # otherwise)</td> # </tr> # <tr> # @tp @b LIBRARY_DESTINATION dir @endtp # <td>Installation directory of the library component relative to # @c CMAKE_INSTALL_PREFIX. If "none" (case-insensitive) is given as argument or # an executable is build, no installation rule for the library component is added. # (default: @c INSTALL_ARCHIVE_DIR)</td> # </tr> # <tr> # @tp @b HEADER_DESTINATION dir @endtp # <td>Installation directory of the library header file relative to # @c INSTALL_INCLUDE_DIR. If "none" (case-insensitive) is given as argument or # an executable is build, no installation rule for the library header file is added. # (default: @c INSTALL_INCLUDE_DIR)</td> # </tr> # <tr> # @tp @b RUNTIME_COMPONENT name @endtp # <td>Name of component as part of which executable or runtime library, respectively, # will be installed if the @c RUNTIME_INSTALL_DIRECTORY property is not "none". # (default: @c COMPONENT if specified or @c BASIS_RUNTIME_COMPONENT otherwise)</td> # </tr> # <tr> # @tp @b RUNTIME_DESTINATION dir @endtp # <td>Installation directory of the executable or runtime component of the shared library # relative to @c CMAKE_INSTALL_PREFIX. If "none" (case-insensitive) is given as argument, # no installation rule for the runtime library is added. # (default: @c INSTALL_LIBRARY_DIR for shared libraries on Unix or # @c INSTALL_RUNTIME_DIR otherwise)</td> # </tr> # <tr> # @tp @b [NO]EXPORT @endtp # <td>Whether to export this target. (default: @c TRUE)</td> # </tr> # <tr> # @tp @b NO_BASIS_UTILITIES @endtp # <td>Specify that the BASIS utilities are not used by this executable or shared library # and hence no link dependency on the BASIS utilities shall be added. # (default: @c NOT BASIS_UTILITIES)</td> # </tr> # <tr> # @tp @b USE_BASIS_UTILITIES @endtp # <td>Specify that the BASIS utilities are used and required by this executable # or shared library, respectively, and hence a link dependency on the BASIS utilities # must be added. # (default: @c BASIS_UTILITIES)</td> # </tr> # </table> # # @todo Consider NO_BASIS_UTILITIES and USE_BASIS_UTILITIES options after the BASIS # utilities for MATLAB have been implemented. # # @returns Adds custom target which builds depending on the @p BASIS_TYPE property # either an executable or a shared library using the MATLAB Compiler. # # @sa basis_add_executable() # @sa basis_add_library() # # @ingroup CMakeUtilities function (basis_add_mcc_target TARGET_NAME) # check target name basis_check_target_name ("${TARGET_NAME}") basis_make_target_uid (TARGET_UID "${TARGET_NAME}") # parse arguments CMAKE_PARSE_ARGUMENTS ( ARGN "SHARED;EXECUTABLE;LIBEXEC;USE_BASIS_UTILITIES;NO_BASIS_UTILITIES;EXPORT;NOEXPORT" "COMPONENT;RUNTIME_COMPONENT;LIBRARY_COMPONENT;DESTINATION;RUNTIME_DESTINATION;LIBRARY_DESTINATION;HEADER_DESTINATION" "" ${ARGN} ) set (SOURCES "${ARGN_UNPARSED_ARGUMENTS}") basis_set_flag (ARGN EXPORT ${BASIS_EXPORT_DEFAULT}) if (ARGN_USE_BASIS_UTILITIES AND ARGN_NO_BASIS_UTILITIES) message (FATAL_ERROR "Target ${TARGET_UID}: Options USE_BASIS_UTILITIES and NO_BASIS_UTILITIES are mutually exclusive!") endif () if (ARGN_USE_BASIS_UTILITIES) set (USES_BASIS_UTILITIES TRUE) elseif (ARGN_NO_BASIS_UTILITIES) set (USES_BASIS_UTILITIES FALSE) else () set (USES_BASIS_UTILITIES ${BASIS_UTILITIES}) endif () if (ARGN_SHARED AND (ARGN_EXECUTABLE OR ARGN_LIBEXEC)) message (FATAL_ERROR "Target ${TARGET_UID}: Options SHARED and EXECUTABLE or LIBEXEC are mutually exclusive!") endif () if (ARGN_SHARED) set (TYPE LIBRARY) else () set (TYPE EXECUTABLE) endif () string (TOLOWER "${TYPE}" type) message (STATUS "Adding MATLAB ${type} ${TARGET_UID}...") # IS_TEST flag basis_sanitize_for_regex (RE "${PROJECT_TESTING_DIR}") if (CMAKE_CURRENT_SOURCE_DIR MATCHES "^${RE}") set (IS_TEST TRUE) else () set (IS_TEST FALSE) endif () # output directory if (IS_TEST) set (LIBRARY_OUTPUT_DIRECTORY "${TESTING_LIBRARY_DIR}") if (ARGN_LIBEXEC) set (RUNTIME_OUTPUT_DIRECTORY "${TESTING_LIBEXEC_DIR}") else () set (RUNTIME_OUTPUT_DIRECTORY "${TESTING_RUNTIME_DIR}") endif () else () set (LIBRARY_OUTPUT_DIRECTORY "${BINARY_LIBRARY_DIR}") if (ARGN_LIBEXEC) set (RUNTIME_OUTPUT_DIRECTORY "${BINARY_LIBEXEC_DIR}") else () set (RUNTIME_OUTPUT_DIRECTORY "${BINARY_RUNTIME_DIR}") endif () endif () # installation component if (ARGN_COMPONENT) if (NOT ARGN_LIBRARY_COMPONENT) set (ARGN_LIBRARY_COMPONENT "${ARGN_COMPONENT}") endif () if (NOT ARGN_RUNTIME_COMPONENT) set (ARGN_RUNTIME_COMPONENT "${ARGN_COMPONENT}") endif () endif () if (NOT ARGN_RUNTIME_COMPONENT) set (ARGN_RUNTIME_COMPONENT "${BASIS_RUNTIME_COMPONENT}") endif () if (NOT ARGN_RUNTIME_COMPONENT) set (ARGN_RUNTIME_COMPONENT "Unspecified") endif () if (NOT ARGN_LIBRARY_COMPONENT) set (ARGN_LIBRARY_COMPONENT "${BASIS_LIBRARY_COMPONENT}") endif () if (NOT ARGN_LIBRARY_COMPONENT) set (ARGN_LIBRARY_COMPONENT "Unspecified") endif () # installation directories if (ARGN_DESTINATION) if (NOT ARGN_RUNTIME_DESTINATION) set (ARGN_RUNTIME_DESTINATION "${ARGN_DESTINATION}") endif () if (NOT ARGN_LIBRARY_DESTINATION) set (ARGN_LIBRARY_DESTINATION "${ARGN_DESTINATION}") endif () if (NOT ARGN_HEADER_DESTINATION) set (ARGN_HEADER_DESTINATION "${ARGN_DESTINATION}") endif () endif () if (NOT ARGN_RUNTIME_DESTINATION AND NOT IS_TEST) if (ARGN_LIBEXEC) set (ARGN_RUNTIME_DESTINATION "${INSTALL_LIBEXEC_DIR}") else () set (ARGN_RUNTIME_DESTINATION "${INSTALL_RUNTIME_DIR}") endif () endif () if (NOT ARGN_LIBRARY_DESTINATION AND NOT IS_TEST) set (ARGN_LIBRARY_DESTINATION "${INSTALL_LIBRARY_DIR}") endif () if (NOT ARGN_HEADER_DESTINATION AND NOT IS_TEST) set (ARGN_HEADER_DESTINATION ".") endif () if (ARGN_RUNTIME_DESTINATION MATCHES "^[nN][oO][nN][eE]$") set (ARGN_RUNTIME_DESTINATION) endif () if (ARGN_LIBRARY_DESTINATION MATCHES "^[nN][oO][nN][eE]$") set (ARGN_LIBRARY_DESTINATION) endif () if (ARGN_HEADER_DESTINATION MATCHES "^[nN][oO][nN][eE]$") set (ARGN_HEADER_DESTINATION) elseif (NOT IS_ABSOLUTE ARGN_HEADER_DESTINATION) set (ARGN_HEADER_DESTINATION "${BINARY_INCLUDE_DIR}/${ARGN_HEADER_DESTINATION}") endif () # whether to compile and compilation flags (for mcc) if ("^${TYPE}$" STREQUAL "^LIBRARY$") set (COMPILE TRUE) elseif (BASIS_COMPILE_MATLAB) if (MATLAB_MCC_EXECUTABLE) set (COMPILE TRUE) else () set (COMPILE FALSE) message (WARNING "MATLAB Compiler not found. Will generate a wrapper script for target" " ${TARGET_UID} which executes the MATLAB code using the -r option of" " the MATLAB interpreter. It is recommended to compile the MATLAB code" " using the MATLAB Compiler if possible, however. Therefore, make sure" " that the MATLAB Compiler is available and check the value of the" " advanced MATLAB_MCC_EXECUTABLE variable in CMake." "\nMake sure to include MATLAB{mcc} as project dependency.") endif () else () set (COMPILE FALSE) endif () if (WIN32 AND NOT COMPILE) # TODO implement generation of Windows Command on Windows set (CONTACT) if (PROJECT_CONTACT) set (CONTACT "\n\nYou may further want to contact ${PROJECT_CONTACT} in order to ask" " for a binary distribution package which contains pre-build binaries" " created using the MATLAB Compiler and download the MATLAB Compiler" " Runtime only if no MATLAB Compiler license is available to you.") basis_list_to_string (CONTACT ${CONTACT}) endif () if (NOT BASIS_COMPILE_MATLAB) basis_update_value (BASIS_COMPILE_MATLAB ON) endif () message (FATAL_ERROR "The optional generation of a Windows Command which executes" " the MATLAB code using the -r option of the MATLAB interpreter" " as an alternative to the build of the MATLAB sources using" " the MATLAB Compiler is not yet implemented. You will have" " to obtain a MATLAB Compiler license and set the advanced" " MATLAB_MCC_EXECUTABLE variable in CMake or use this package" " on a Unix system instead.${CONTACT}") endif () if (COMPILE) if (NOT MATLAB_MCC_EXECUTABLE) message (FATAL_ERROR "MATLAB Compiler not found! It is required to build target ${TARGET_UID}." " Ensure that MATLAB{mcc} is declared as project dependency" " and check the setting of MATLAB_DIR and/or MATLAB_MCC_EXECUTABLE.") endif () basis_add_mcc_options() else () if (NOT MATLAB_EXECUTABLE) message (FATAL_ERROR "MATLAB not found! It is required to build target ${TARGET_UID}." " Ensure that MATLAB{matlab} is declared as project dependency" " and check the setting of MATLAB_DIR and/or MATLAB_EXECUTABLE.") endif () endif () if ("^${TYPE}$" STREQUAL "^EXECUTABLE$") set (COMPILE_FLAGS "${BASIS_MCC_FLAGS}") else () set (COMPILE_FLAGS "") endif () # output file name prefix/suffix if ("^${TYPE}$" STREQUAL "^EXECUTABLE$") set (PREFIX) if (WIN32 AND "^${COMPILE_FLAGS}$" STREQUAL "^NOMCC$") set (SUFFIX ".cmd") else () set (SUFFIX) endif () else () if (WIN32) set (PREFIX) set (SUFFIX .lib) # link library file extension elseif (APPLE) set (PREFIX lib) set (SUFFIX .dylib) else () set (PREFIX lib) set (SUFFIX .so) endif () endif () # configure (.in) source files basis_configure_sources (SOURCES ${SOURCES}) # add custom target add_custom_target (${TARGET_UID} ALL SOURCES ${SOURCES}) basis_get_target_name (OUTPUT_NAME "${TARGET_UID}") get_directory_property (INCLUDE_DIRS INCLUDE_DIRECTORIES) get_directory_property (LINK_DIRS LINK_DIRECTORIES) set_target_properties ( ${TARGET_UID} PROPERTIES LANGUAGE "MATLAB" BASIS_TYPE "MCC_${TYPE}" BASIS_UTILITIES FALSE # TODO Implement utilities for MATLAB BASIS_INCLUDE_DIRECTORIES "${INCLUDE_DIRS}" BASIS_LINK_DIRECTORIES "${LINK_DIRS}" BUILD_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TARGET_UID}" SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" BINARY_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" LIBRARY_OUTPUT_DIRECTORY "${LIBRARY_OUTPUT_DIRECTORY}" LIBRARY_INSTALL_DIRECTORY "${ARGN_LIBRARY_DESTINATION}" LIBRARY_HEADER_DIRECTORY "${ARGN_HEADER_DESTINATION}" LIBRARY_COMPONENT "${ARGN_LIBRARY_COMPONENT}" RUNTIME_OUTPUT_DIRECTORY "${RUNTIME_OUTPUT_DIRECTORY}" RUNTIME_INSTALL_DIRECTORY "${ARGN_RUNTIME_DESTINATION}" RUNTIME_COMPONENT "${ARGN_RUNTIME_COMPONENT}" PREFIX "${PREFIX}" OUTPUT_NAME "${OUTPUT_NAME}" SUFFIX "${SUFFIX}" COMPILE_FLAGS "${COMPILE_FLAGS}" COMPILE "${COMPILE}" LINK_DEPENDS "" EXPORT ${EXPORT} LIBEXEC ${ARGN_LIBEXEC} TEST ${IS_TEST} ) # add target to list of targets basis_set_project_property (APPEND PROPERTY TARGETS "${TARGET_UID}") message (STATUS "Adding MATLAB ${type} ${TARGET_UID}... - done") endfunction () # ============================================================================ # custom build commands # ============================================================================ # ---------------------------------------------------------------------------- ## @brief Add custom command for build of MEX-file. # # This function is called by basis_finalize_targets() which in turn is called # by basis_project_end(), i.e., the end of the root CMake configuration file # of the (sub-)project. # # @param [in] TARGET_UID Name/UID of custom target added by basis_add_mex_file(). # # @sa basis_add_mex_file() # # @ingroup CMakeUtilities function (basis_build_mex_file TARGET_UID) # does this target exist ? basis_get_target_uid (TARGET_UID "${TARGET_UID}") if (NOT TARGET "${TARGET_UID}") message (FATAL_ERROR "Unknown build target: ${TARGET_UID}") endif () if (BASIS_VERBOSE) message (STATUS "Adding build command for target ${TARGET_UID}...") endif () # get target properties basis_get_target_name (TARGET_NAME ${TARGET_UID}) set ( PROPERTIES BASIS_TYPE BASIS_UTILITIES BASIS_INCLUDE_DIRECTORIES BASIS_LINK_DIRECTORIES BUILD_DIRECTORY SOURCE_DIRECTORY BINARY_DIRECTORY LIBRARY_OUTPUT_DIRECTORY LIBRARY_INSTALL_DIRECTORY LIBRARY_COMPONENT PREFIX OUTPUT_NAME SUFFIX COMPILE_FLAGS LINK_DEPENDS LINK_FLAGS MFILE EXPORT SOURCES ) get_target_property (IS_TEST ${TARGET_UID} TEST) foreach (PROPERTY ${PROPERTIES}) get_target_property (${PROPERTY} ${TARGET_UID} ${PROPERTY}) if (NOT ${PROPERTY}) set (${PROPERTY}) endif () endforeach () # sanity check of property values if (NOT BASIS_TYPE MATCHES "^MEX$") message (FATAL_ERROR "Target ${TARGET_UID}: Invalid BASIS_TYPE: ${BASIS_TYPE}") endif () list (GET SOURCES 0 BUILD_DIR) # CMake <3.1 stores path to internal build directory here if (BUILD_DIR MATCHES "CMakeFiles") list (REMOVE_AT SOURCES 0) endif () set (BUILD_DIR "${BUILD_DIRECTORY}.dir") if (NOT SOURCES) message (FATAL_ERROR "Target ${TARGET_UID}: Empty SOURCES list!" " Have you accidentally modified this read-only property or" " is your (newer) CMake version not compatible with BASIS?") endif () if (NOT LIBRARY_COMPONENT) set (LIBRARY_COMPONENT "Unspecified") endif () if (MFILE) if (NOT IS_ABSOLUTE "${MFILE}") set (MFILE "${SOURCE_DIRECTORY}/${MFILE}") endif () if (NOT EXISTS "${MFILE}") message (FATAL_ERROR "M-file ${MFILE} of MEX-file target ${TARGET_UID} does not exist!") endif () endif () # output name if (NOT OUTPUT_NAME) set (OUTPUT_NAME "${TARGET_NAME}") endif () if (SUFFIX) set (OUTPUT_NAME "${OUTPUT_NAME}${SUFFIX}") endif () string (REGEX REPLACE "/+" "/" PREFIX "${PREFIX}") string (REGEX REPLACE "/$" "" PREFIX "${PREFIX}") if (PREFIX AND NOT PREFIX MATCHES "^/") set (PREFIX "/${PREFIX}") endif () # initialize dependencies of custom build command set (DEPENDS ${SOURCES}) # get list of libraries to link to set (LINK_LIBS) foreach (LIB ${LINK_DEPENDS}) basis_get_target_uid (UID "${LIB}") if (TARGET ${UID}) basis_get_target_location (LIB_FILE ${UID} ABSOLUTE) string (REPLACE "<${BASIS_GE_CONFIG}>" "{CONFIG}" LIB_FILE "${LIB_FILE}") list (APPEND DEPENDS ${UID}) else () set (LIB_FILE "${LIB}") endif () list (APPEND LINK_LIBS "${LIB_FILE}") endforeach () get_filename_component (OUTPUT_NAME_WE "${OUTPUT_NAME}" NAME_WE) # decompose user supplied MEX switches macro (extract VAR) string (REGEX REPLACE "${VAR}=\"([^\"]+)\"|${VAR}=([^\" ])*" "" COMPILE_FLAGS "${COMPILE_FLAGS}") if (CMAKE_MATCH_1) set (${VAR} "${CMAKE_MATCH_1}") elseif (CMAKE_MATCH_2) set (${VAR} "${CMAKE_MATCH_2}") else () set (${VAR}) endif () endmacro () if (UNIX) extract (CC) extract (CFLAGS) extract (CXX) extract (CXXFLAGS) extract (CLIBS) extract (CXXLIBS) extract (LD) extract (LDXX) extract (LDFLAGS) extract (LDCXXFLAGS) if (LINK_FLAGS) set (LDFLAGS "${LDFLAGS} ${LINK_FLAGS}") endif () # set defaults for not provided options if (NOT CC) set (CC "${CMAKE_C_COMPILER}") endif () if (NOT CFLAGS) set (CFLAGS "${CMAKE_C_FLAGS}") endif () if (NOT CFLAGS MATCHES "( |^)-fPIC( |$)") set (CFLAGS "-fPIC ${CFLAGS}") endif () if (NOT CXX) set (CXX "${CMAKE_CXX_COMPILER}") endif () if (NOT CXXFLAGS) set (CXXFLAGS "${CMAKE_CXX_FLAGS}") endif () if (NOT CXXFLAGS MATCHES "( |^)-fPIC( |$)") set (CXXFLAGS "-fPIC ${CXXFLAGS}") endif () if (NOT LD) set (LD "${CMAKE_CXX_COMPILER}") # do not use CMAKE_LINKER here endif () if (NOT LDFLAGS) set (LDFLAGS "\$LDFLAGS ${CMAKE_SHARED_LINKER_FLAGS}") endif () # We chose to use CLIBS and CXXLIBS instead of the -L and -l switches # to add also link libraries added via basis_target_link_libraries() # because the MEX script will not use these arguments if CLIBS or CXXLIBS # is set. Moreover, the -l switch can only be used to link to a shared # library and not a static one (on UNIX). #foreach (LIB ${LINK_LIBS}) # if (LIB MATCHES "[/\\\.]") # set (CXXLIBS "${CXXLIBS} ${LIB}") # endif () #endforeach () endif () # get remaining switches basis_string_to_list (MEX_USER_ARGS "${COMPILE_FLAGS}") # assemble MEX switches set (MEX_ARGS) if (UNIX) list (APPEND MEX_ARGS "CC=${CC}" "CFLAGS=${CFLAGS}") # C compiler and flags if (CLIBS) list (APPEND MEX_ARGS "CLIBS=${CLIBS}") # C link libraries endif () list (APPEND MEX_ARGS "CXX=${CXX}" "CXXFLAGS=${CXXFLAGS}") # C++ compiler and flags if (CXXLIBS) list (APPEND MEX_ARGS "CXXLIBS=${CXXLIBS}") # C++ link libraries endif () if (LD) list (APPEND MEX_ARGS "LD=${LD}") # C linker endif () if (LDFLAGS) list (APPEND MEX_ARGS "LDFLAGS=${LDFLAGS}") # C link flags endif () if (LDCXX) list (APPEND MEX_ARGS "LDCXX=${LDCXX}") # C++ linker endif () if (LDCXXFLAGS) list (APPEND MEX_ARGS "LDCXXFLAGS=${LDCXXFLAGS}") # C++ link flags endif () endif () list (APPEND MEX_ARGS "-outdir" "${BUILD_DIR}") # output directory list (APPEND MEX_ARGS "-output" "${OUTPUT_NAME_WE}") # output name (w/o extension) foreach (INCLUDE_PATH ${BASIS_INCLUDE_DIRECTORIES}) # include directories list (FIND MEX_ARGS "-I${INCLUDE_PATH}" IDX) # as specified via if (INCLUDE_PATH AND IDX EQUAL -1) # basis_include_directories() list (APPEND MEX_ARGS "-I${INCLUDE_PATH}") endif () endforeach () set (MEX_LIBPATH) set (MEX_LIBS) foreach (LINK_DIR ${BASIS_LINK_DIRECTORIES}) # link directories if (WIN32) string (REPLACE "/" "\\" LINK_DIR "${LINK_DIR}") set (LINK_DIR "/LIBPATH:\\\"${LINK_DIR}\\\"") else () set (LINK_DIR "-L${LINK_DIR}") endif () list (APPEND MEX_LIBPATH "${LINK_DIR}") # as specified via basis_link_directories() endforeach () foreach (LIBRARY ${LINK_LIBS}) # link libraries get_filename_component (LINK_DIR "${LIBRARY}" PATH) # as specified via basis_target_link_libraries() get_filename_component (LINK_LIB "${LIBRARY}" NAME) string (REGEX REPLACE "\\.(so|dylib)(\\.[0-9]+)*$" "" LINK_LIB "${LINK_LIB}") if (CMAKE_SYSTEM_NAME MATCHES "Darwin") # cf. https://github.com/schuhschuh/cmake-basis/issues/443 string (REGEX REPLACE "\\.a(\\.[0-9]+)*$" "" LINK_LIB "${LINK_LIB}") endif () string (REGEX REPLACE "^-l" "" LINK_LIB "${LINK_LIB}") if (LINK_DIR) if (WIN32) string (REPLACE "/" "\\" LINK_DIR "${LINK_DIR}") set (LINK_DIR "/LIBPATH:\\\"${LINK_DIR}\\\"") else () set (LINK_DIR "-L${LINK_DIR}") endif () list (APPEND MEX_LIBPATH "${LINK_DIR}") endif () if (WIN32) if (NOT LINK_LIB MATCHES "\\.lib$") set (LINK_LIB "${LINK_LIB}.lib") endif () else () string (REGEX REPLACE "^lib" "" LINK_LIB "${LINK_LIB}") set (LINK_LIB "-l${LINK_LIB}") endif () list (APPEND MEX_LIBS "${LINK_LIB}") endforeach () if (MEX_LIBPATH) list (REMOVE_DUPLICATES MEX_LIBPATH) endif () # do not remove duplicate entries in MEX_LIBS which may be needed # to resolve (cyclic) dependencies between statically linked libraries # (cf. https://github.com/schuhschuh/cmake-basis/issues/444) if (MEX_LIBPATH OR MEX_LIBS) if (WIN32) basis_list_to_delimited_string (MEX_LIBPATH " " NOAUTOQUOTE ${MEX_LIBPATH}) basis_list_to_delimited_string (MEX_LIBS " " ${MEX_LIBS}) list (APPEND MEX_ARGS "LINKFLAGS#$LINKFLAGS ${MEX_LIBPATH} ${MEX_LIBS}") else () list (APPEND MEX_ARGS ${MEX_LIBPATH} ${MEX_LIBS}) endif () endif () # other user switches list (APPEND MEX_ARGS ${MEX_USER_ARGS}) # source files list (APPEND MEX_ARGS ${SOURCES}) # build command for invocation of MEX script set (BUILD_CMD "${MATLAB_MEX_EXECUTABLE}" -v ${MEX_ARGS}) set (BUILD_SCRIPT "${BUILD_DIR}/build.cmake") set (BUILD_LOG "${BUILD_DIR}/build.log") set (BUILD_OUTPUT "${LIBRARY_OUTPUT_DIRECTORY}${PREFIX}/${OUTPUT_NAME}") set (BUILD_OUTPUTS "${BUILD_OUTPUT}") if (MFILE) set (BUILD_MFILE "${LIBRARY_OUTPUT_DIRECTORY}${PREFIX}/${OUTPUT_NAME_WE}.m") list (APPEND BUILD_OUTPUTS "${BUILD_MFILE}") else () set (BUILD_MFILE) endif () # configure build script configure_file ("${BASIS_SCRIPT_EXECUTE_PROCESS}" "${BUILD_SCRIPT}" @ONLY) # relative paths used for comments of commands file (RELATIVE_PATH REL "${CMAKE_BINARY_DIR}" "${BUILD_OUTPUT}") # add custom command to build executable using MEX script add_custom_command ( OUTPUT "${BUILD_OUTPUT}" # rebuild when input sources were modified DEPENDS "${BUILD_SCRIPT}" "${CMAKE_CURRENT_LIST_FILE}" ${DEPENDS} # invoke MEX script, wrapping the command in CMake execute_process() # command allows for inspection of command output for error messages # and specification of timeout COMMAND "${CMAKE_COMMAND}" "-DCONFIG=$<${BASIS_GE_CONFIG}>" "-DWORKING_DIRECTORY=${BUILD_DIR}" "-DTIMEOUT=${BASIS_MEX_TIMEOUT}" "-DERROR_EXPRESSION=[E|e]rror:" "-DOUTPUT_FILE=${BUILD_LOG}" "-DERROR_FILE=${BUILD_LOG}" "-DVERBOSE=OFF" "-DLOG_ARGS=ON" "-P" "${BUILD_SCRIPT}" # post-build command COMMAND "${CMAKE_COMMAND}" -E copy "${BUILD_DIR}/${OUTPUT_NAME}" "${BUILD_OUTPUT}" COMMAND "${CMAKE_COMMAND}" -E remove "${BUILD_DIR}/${OUTPUT_NAME}" # inform user where build log can be found COMMAND "${CMAKE_COMMAND}" -E echo "Build log written to ${BUILD_LOG}" # comment COMMENT "Building MEX-file ${REL}..." VERBATIM ) if (BUILD_MFILE) add_custom_command ( OUTPUT "${BUILD_MFILE}" DEPENDS "${MFILE}" COMMAND "${CMAKE_COMMAND}" -E copy "${MFILE}" "${BUILD_MFILE}" COMMENT "Copying M-file of ${REL}..." ) endif () # add custom target add_custom_target (_${TARGET_UID} DEPENDS ${BUILD_OUTPUTS} SOURCES ${SOURCES}) add_dependencies (${TARGET_UID} _${TARGET_UID}) # cleanup on "make clean" set_property ( DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${BUILD_DIR}/${OUTPUT_NAME}" "${BUILD_OUTPUTS}" "${BUILD_LOG}" ) # export target if (EXPORT) basis_add_custom_export_target (${TARGET_UID} "${IS_TEST}") endif () # install MEX-file if (LIBRARY_INSTALL_DIRECTORY) install ( FILES ${BUILD_OUTPUTS} DESTINATION "${LIBRARY_INSTALL_DIRECTORY}${PREFIX}" COMPONENT "${LIBRARY_COMPONENT}" ) endif () if (BASIS_VERBOSE) message (STATUS "Adding build command for target ${TARGET_UID}... - done") endif () endfunction () # ---------------------------------------------------------------------------- ## @brief Add custom command for build of MATLAB Compiler target. # # This function is called by basis_finalize_targets() which in turn is called # by basis_project_end(), i.e., the end of the root CMake configuration file # of the (sub-)project. # # @param [in] TARGET_UID Name/UID of custom target added by basis_add_mcc_target(). # # @sa basis_add_mcc_target() # # @ingroup CMakeUtilities function (basis_build_mcc_target TARGET_UID) # does this target exist ? basis_get_target_uid (TARGET_UID "${TARGET_UID}") if (NOT TARGET "${TARGET_UID}") message (FATAL_ERROR "Unknown target ${TARGET_UID}!") endif () if (BASIS_VERBOSE) message (STATUS "Adding build command for target ${TARGET_UID}...") endif () # get target properties basis_get_target_name (TARGET_NAME ${TARGET_UID}) set ( PROPERTIES BASIS_TYPE BASIS_UTILITIES BASIS_INCLUDE_DIRECTORIES BASIS_LINK_DIRECTORIES BUILD_DIRECTORY SOURCE_DIRECTORY BINARY_DIRECTORY LIBRARY_OUTPUT_DIRECTORY LIBRARY_INSTALL_DIRECTORY LIBRARY_HEADER_DIRECTORY LIBRARY_COMPONENT RUNTIME_OUTPUT_DIRECTORY RUNTIME_INSTALL_DIRECTORY RUNTIME_COMPONENT PREFIX OUTPUT_NAME SUFFIX SOURCES COMPILE_FLAGS COMPILE LINK_DEPENDS EXPORT ) get_target_property (IS_TEST ${TARGET_UID} TEST) foreach (PROPERTY ${PROPERTIES}) get_target_property (${PROPERTY} ${TARGET_UID} ${PROPERTY}) if (NOT ${PROPERTY}) set (${PROPERTY}) endif () endforeach () # sanity checks of property values set (EXECUTABLE FALSE) set (LIBEXEC FALSE) set (LIBRARY FALSE) if (BASIS_TYPE MATCHES "^MCC_(EXECUTABLE|LIBEXEC|LIBRARY)$") set (${CMAKE_MATCH_1} TRUE) if (LIBEXEC) set (EXECUTABLE TRUE) endif () else () message (FATAL_ERROR "Target ${TARGET_UID}: Invalid BASIS_TYPE: ${BASIS_TYPE}") endif () list (GET SOURCES 0 BUILD_DIR) # CMake <3.1 stores path to internal build directory here if (BUILD_DIR MATCHES "CMakeFiles") list (REMOVE_AT SOURCES 0) endif () set (BUILD_DIR "${BUILD_DIRECTORY}.dir") if (NOT SOURCES) message (FATAL_ERROR "Target ${TARGET_UID}: Empty SOURCES list!" " Have you accidentally modified this read-only property or" " is your (newer) CMake version not compatible with BASIS?") endif () list (GET SOURCES 0 MAIN_SOURCE) # entry point if (NOT RUNTIME_COMPONENT) set (RUNTIME_COMPONENT "Unspecified") endif () if (NOT LIBRARY_COMPONENT) set (LIBRARY_COMPONENT "Unspecified") endif () # output name if (NOT OUTPUT_NAME) set (OUTPUT_NAME "${TARGET_NAME}") endif () if (PREFIX) set (OUTPUT_NAME "${PREFIX}${OUTPUT_NAME}") endif () if (SUFFIX) set (OUTPUT_NAME "${OUTPUT_NAME}${SUFFIX}") endif () get_filename_component (OUTPUT_NAME_WE "${OUTPUT_NAME}" NAME_WE) # MCC only allows alpha-numeric characters and underscores # TODO: Figure out how to build a shared library without this restriction # (cf. https://github.com/schuhschuh/cmake-basis/issues/410). if (NOT OUTPUT_NAME MATCHES "[a-zA-Z][_a-zA-Z0-9]*") message (FATAL_ERROR "Target ${TARGET_UID} has invalid output name ${OUTPUT_NAME}." "MCC only allows alpha-numeric characters and underscores. " "See GitHub issue #410 for updates on this restriction at\n" "https://github.com/schuhschuh/cmake-basis/issues/410") endif () set (MCC_OUTPUT_NAME "${OUTPUT_NAME}") set (MCC_OUTPUT_NAME_WE "${OUTPUT_NAME_WE}") #string (REGEX REPLACE "\\+|-" "_" MCC_OUTPUT_NAME "${OUTPUT_NAME}") #get_filename_component (MCC_OUTPUT_NAME_WE "${MCC_OUTPUT_NAME}" NAME_WE) # initialize dependencies of custom build command set (DEPENDS ${SOURCES}) # build output file and comment file (RELATIVE_PATH REL "${CMAKE_BINARY_DIR}" "${BUILD_DIR}/${OUTPUT_NAME}") if (LIBRARY) set (BUILD_OUTPUT "${LIBRARY_OUTPUT_DIRECTORY}/${OUTPUT_NAME}") set (BUILD_COMMENT "Building MATLAB library ${REL}...") else () set (BUILD_OUTPUT "${RUNTIME_OUTPUT_DIRECTORY}/${OUTPUT_NAME}") set (BUILD_COMMENT "Building MATLAB executable ${REL}...") endif () # -------------------------------------------------------------------------- # assemble build command for build of executable wrapper script if (EXECUTABLE AND NOT COMPILE) # used to recognize source files which are located in the build tree basis_sanitize_for_regex (BINARY_CODE_DIR_RE "${BINARY_CODE_DIR}") # main MATLAB function and search path get_filename_component (MATLAB_COMMAND "${MAIN_SOURCE}" NAME_WE) get_filename_component (SOURCE_DIR "${MAIN_SOURCE}" PATH) get_filename_component (SOURCE_PACKAGE "${SOURCE_DIR}" NAME) if (SOURCE_PACKAGE MATCHES "^\\+") get_filename_component (SOURCE_DIR "${SOURCE_DIR}" PATH) set (MATLAB_COMMAND "${SOURCE_PACKAGE}.${MATLAB_COMMAND}") string (REGEX REPLACE "^\\+" "" MATLAB_COMMAND "${MATLAB_COMMAND}") else () set (SOURCE_PACKAGE "${MATLAB_COMMAND}") endif () basis_get_relative_path (DIR "${PROJECT_SOURCE_DIR}" "${SOURCE_DIR}") set (BINARY_DIR "${PROJECT_BINARY_DIR}/${DIR}") # location of configured sources # output file set (OUTPUT_FILE "${BUILD_OUTPUT}") get_filename_component (OUTPUT_DIR "${OUTPUT_FILE}" PATH) # installation set (INSTALL_FILE "${BUILD_DIR}/${OUTPUT_NAME}") # file to be installed set (INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${RUNTIME_INSTALL_DIRECTORY}") # location of installed wrapper set (INSTALL_SOURCE_DIR "${INSTALL_MATLAB_LIBRARY_DIR}") # location of installed MATLAB sources if (NOT SOURCE_PACKAGE MATCHES "^\\+") set (INSTALL_SOURCE_DIR "${INSTALL_SOURCE_DIR}/${SOURCE_PACKAGE}") endif () # startup file if (SOURCE_PACKAGE MATCHES "^\\+") set (BUILD_STARTUP_FILE "${BINARY_DIR}/${SOURCE_PACKAGE}/startup.m") set (INSTALL_STARTUP_FILE "${BUILD_DIR}/startup.m") set (INSTALL_STARTUP_DIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_SOURCE_DIR}/${SOURCE_PACKAGE}") else () set (BUILD_STARTUP_FILE "${BINARY_DIR}/startup.m") set (INSTALL_STARTUP_FILE "${BUILD_DIR}/startup.m") set (INSTALL_STARTUP_DIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_SOURCE_DIR}") endif () get_filename_component (BUILD_STARTUP_DIR "${BUILD_STARTUP_FILE}" PATH) list (APPEND BUILD_OUTPUT "${BUILD_STARTUP_FILE}") list (APPEND BUILD_OUTPUT "${INSTALL_STARTUP_FILE}") # MATLAB search path # # The following paths are written to the startup M-file such # that they can conveniently be added to the search path within an # interactive MATLAB sesssion. The path to this startup file is # added to the search path by the wrapper executable first, and # then this startup file is evaluated which adds all additional # paths. If no startup file is specified, all paths are added # to the search path on the command-line in the wrapper script. set (BUILD_MATLABPATH) set (INSTALL_MATLABPATH) # if any source file was configured and hence is located in the # build tree instead of the source tree, add corresponding build # tree path to BUILD_MATLABPATH as well if (SOURCES MATCHES "^${BINARY_CODE_DIR_RE}") file (RELATIVE_PATH REL "${BUILD_STARTUP_DIR}" "${BINARY_DIR}") if (REL) list (APPEND BUILD_MATLABPATH "${REL}") else () list (APPEND BUILD_MATLABPATH ".") endif () endif () list (APPEND BUILD_MATLABPATH "${SOURCE_DIR}") # The following is not required because startup script is located # in the same directory as the other sources and basis_generate_matlab_executable() # adds this path automatically in order to run the startup script. # Moreover, if anyone wants to run the startup script, they have to # add this path manually or make it the current directory first. #file (RELATIVE_PATH REL "${INSTALL_STARTUP_DIR}" "${CMAKE_INSTALL_PREFIX}/${INSTALL_SOURCE_DIR}") #if (REL) # list (APPEND INSTALL_MATLABPATH "${REL}") #else () # list (APPEND INSTALL_MATLABPATH ".") #endif () # link dependencies, i.e., MEX-files foreach (LINK_DEPEND ${LINK_DEPENDS}) basis_get_target_uid (UID "${LINK_DEPEND}") if (TARGET ${UID}) basis_get_target_location (LINK_DEPEND ${UID} ABSOLUTE) if (LINK_DEPEND MATCHES "\\.mex") get_filename_component (LINK_PATH "${LINK_DEPEND}" PATH) list (APPEND BUILD_MATLABPATH "${LINK_PATH}") list (APPEND DEPENDS ${UID}) endif () basis_get_target_location (LINK_DEPEND ${UID} POST_INSTALL) basis_get_target_property (BUNDLED ${UID} BUNDLED) basis_get_target_property (IMPORTED ${UID} IMPORTED) if (NOT IMPORTED OR BUNDLED) file (RELATIVE_PATH REL "${INSTALL_STARTUP_DIR}" "${LINK_DEPEND}") if (REL) set (LINK_DEPEND "${REL}") else () set (LINK_DEPEND ".") endif () endif () if (LINK_DEPEND MATCHES "\\.mex") get_filename_component (LINK_PATH "${LINK_DEPEND}" PATH) list (APPEND INSTALL_MATLABPATH "${LINK_PATH}") endif () elseif (IS_ABSOLUTE "${LINK_DEPEND}") if (IS_DIRECTORY "${LINK_DEPEND}") list (APPEND BUILD_MATLABPATH "${LINK_DEPEND}") list (APPEND INSTALL_MATLABPATH "${LINK_DEPEND}") elseif (EXISTS "${LINK_DEPEND}" AND LINK_DEPEND MATCHES "\\.mex") get_filename_component (LINK_PATH "${LINK_DEPEND}" PATH) list (APPEND BUILD_MATLABPATH "${LINK_PATH}") list (APPEND INSTALL_MATLABPATH "${LINK_PATH}") endif () endif () endforeach () # cleanup directory paths string (REGEX REPLACE "/+" "/" BUILD_MATLABPATH "${BUILD_MATLABPATH}") string (REGEX REPLACE "/+" "/" INSTALL_MATLABPATH "${INSTALL_MATLABPATH}") string (REGEX REPLACE "/(;|$)" "\\1" BUILD_MATLABPATH "${BUILD_MATLABPATH}") string (REGEX REPLACE "/(;|$)" "\\1" INSTALL_MATLABPATH "${INSTALL_MATLABPATH}") # remove duplicates if (BUILD_MATLABPATH) list (REMOVE_DUPLICATES BUILD_MATLABPATH) endif () if (INSTALL_MATLABPATH) list (REMOVE_DUPLICATES INSTALL_MATLABPATH) endif () # configure build script set (BUILD_SCRIPT "${BUILD_DIR}/build.cmake") configure_file ("${BASIS_MODULE_PATH}/generate_matlab_executable.cmake.in" "${BUILD_SCRIPT}" @ONLY) # add custom command to build wrapper executable add_custom_command ( OUTPUT ${BUILD_OUTPUT} # rebuild when input sources were modified MAIN_DEPENDENCY "${MAIN_SOURCE}" DEPENDS "${BUILD_SCRIPT}" "${CMAKE_CURRENT_LIST_FILE}" ${DEPENDS} # invoke MATLAB Compiler in either MATLAB or standalone mode # wrapping command in CMake execute_process () command allows for inspection # parsing of command output for error messages and specification of timeout COMMAND "${CMAKE_COMMAND}" "-P" "${BUILD_SCRIPT}" # comment COMMENT "${BUILD_COMMENT}" ) # install source files - preserving relative paths in SOURCE_DIR foreach (SOURCE IN LISTS SOURCES) get_filename_component (REL "${SOURCE}" PATH) if (SOURCE MATCHES "^${BINARY_CODE_DIR_RE}") basis_get_relative_path (REL "${BINARY_DIR}" "${REL}") else () basis_get_relative_path (REL "${SOURCE_DIR}" "${REL}") endif () if (REL MATCHES "^\\.?$|^\\.\\./") install ( FILES "${SOURCE}" DESTINATION "${INSTALL_SOURCE_DIR}" COMPONENT "${RUNTIME_COMPONENT}" ) else () install ( FILES "${SOURCE}" DESTINATION "${INSTALL_SOURCE_DIR}/${REL}" COMPONENT "${RUNTIME_COMPONENT}" ) endif () endforeach () # -------------------------------------------------------------------------- # assemble build command for build using MATLAB Compiler else () set (INSTALL_FILE "${BUILD_OUTPUT}") # file to be installed # get list of libraries to link to (e.g., MEX-file) set (LINK_LIBS) foreach (LIB ${LINK_DEPENDS}) basis_get_target_uid (UID "${LIB}") if (TARGET ${UID}) basis_get_target_location (LIB_FILE ${UID} ABSOLUTE) string (REPLACE "<${BASIS_GE_CONFIG}>" "{CONFIG}" LIB_FILE "${LIB_FILE}") list (APPEND DEPENDS ${UID}) else () set (LIB_FILE "${LIB}") endif () list (APPEND LINK_LIBS "${LIB_FILE}") endforeach () # MATLAB search path foreach (P ${SOURCES}) get_filename_component (P "${P}" PATH) list (APPEND MATLABPATH "${P}") endforeach () list (APPEND MATLABPATH ${BASIS_INCLUDE_DIRECTORIES}) # MATLAB Compiler arguments string (REGEX REPLACE " +" ";" MCC_ARGS "${COMPILE_FLAGS}") # user specified flags foreach (P IN LISTS MATLABPATH) # search path, -I options string (REGEX REPLACE "/(\\+|@).*$" "" P "${P}") # remove package/class subdirectories list (FIND MCC_ARGS "${P}" IDX) # skip if already added if (IDX GREATER 0) math (EXPR IDX "${IDX} - 1") list (GET MCC_ARGS ${IDX} OPT) if (NOT OPT MATCHES "^-I$") set (IDX -1) endif () endif () if (EXISTS "${P}" AND IDX EQUAL -1) list (APPEND MCC_ARGS -I "${P}") endif () endforeach () if (LIBRARY) list (APPEND MCC_ARGS -W "cpplib:${MCC_OUTPUT_NAME_WE}" -T link:lib) # build library else () # or list (APPEND MCC_ARGS -m -o "${MCC_OUTPUT_NAME_WE}") # build standalone application endif () list (APPEND MCC_ARGS -d "${BUILD_DIR}") # (temp) output directory list (APPEND MCC_ARGS ${SOURCES}) # source M-files foreach (LIB ${LINK_LIBS}) # link libraries, e.g. MEX-files list (FIND MCC_ARGS "${LIB}" IDX) if (LIB AND IDX EQUAL -1) list (APPEND MCC_ARGS "-a" "${LIB}") endif () endforeach () # build command for invocation of MATLAB Compiler in standalone mode set (BUILD_CMD "${MATLAB_MCC_EXECUTABLE}" ${MCC_USER_ARGS} ${MCC_ARGS}) set (BUILD_LOG "${BUILD_DIR}/build.log") set (BUILD_SCRIPT "${BUILD_DIR}/build.cmake") set (WORKING_DIR "${SOURCE_DIRECTORY}") set (MATLAB_MODE OFF) # build command for invocation of MATLAB Compiler in MATLAB mode if (BASIS_MCC_MATLAB_MODE) set (MATLAB_MODE ON) if (NOT MATLAB_EXECUTABLE) message (WARNING "MATLAB executable not found. It is required to build target ${TARGET_UID} in MATLAB mode." " Either set the advanced option BASIS_MCC_MATLAB_MODE to OFF or add MATLAB{matlab} as dependency." " Will build target ${TARGET_UID} in standalone mode instead.") set (MATLAB_MODE OFF) endif () if (MATLAB_MODE) basis_list_to_delimited_string (ARGS "', '" NOAUTOQUOTE ${MCC_USER_ARGS} ${MCC_ARGS}) set ( BUILD_CMD "${MATLAB_EXECUTABLE}" # run MATLAB "-nosplash" # do not display splash screen on start up "-nodesktop" # run in command line mode "-nojvm" # we do not need the Java Virtual Machine "-r" "try, mcc('-v', '${ARGS}'), catch err, fprintf(2, err.message), end, quit force" ) endif () endif () # post-build command set (POST_BUILD_COMMAND) if (NOT "^${MCC_OUTPUT_NAME}$" STREQUAL "^${OUTPUT_NAME}$") list (APPEND POST_BUILD_COMMAND COMMAND "${CMAKE_COMMAND}" -E copy "${BUILD_DIR}/${MCC_OUTPUT_NAME}" "${BUILD_DIR}/${OUTPUT_NAME}" COMMAND "${CMAKE_COMMAND}" -E copy "${BUILD_DIR}/${MCC_OUTPUT_NAME_WE}.h" "${BUILD_DIR}/${OUTPUT_NAME_WE}.h" ) endif () if (LIBRARY) list (APPEND POST_BUILD_COMMAND COMMAND "${CMAKE_COMMAND}" -E copy "${BUILD_DIR}/${OUTPUT_NAME}" "${LIBRARY_OUTPUT_DIRECTORY}/${OUTPUT_NAME}" COMMAND "${CMAKE_COMMAND}" -E copy "${BUILD_DIR}/${OUTPUT_NAME_WE}.h" "${LIBRARY_HEADER_DIRECTORY}/${OUTPUT_NAME_WE}.h" ) if (WIN32) list (APPEND POST_BUILD_COMMAND COMMAND "${CMAKE_COMMAND}" -E copy "${BUILD_DIR}/${OUTPUT_NAME_WE}.dll" "${LIBRARY_OUTPUT_DIRECTORY}/${OUTPUT_NAME_WE}.dll") endif () else () if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") # TODO: This file should be regenerated if it is missing. file (WRITE "${BUILD_DIR}/${OUTPUT_NAME}" "#!/bin/bash\nexec $(dirname $BASH_SOURCE)/${OUTPUT_NAME_WE}.app/Contents/MacOS/${MCC_OUTPUT_NAME_WE}") execute_process (COMMAND chmod +x "${BUILD_DIR}/${OUTPUT_NAME}") list (APPEND POST_BUILD_COMMAND COMMAND "${CMAKE_COMMAND}" -E copy_directory "${BUILD_DIR}/${OUTPUT_NAME_WE}.app" "${RUNTIME_OUTPUT_DIRECTORY}/${OUTPUT_NAME_WE}.app" COMMAND "${CMAKE_COMMAND}" -E copy "${BUILD_DIR}/${OUTPUT_NAME}" "${RUNTIME_OUTPUT_DIRECTORY}/${OUTPUT_NAME}" ) else () list (APPEND POST_BUILD_COMMAND COMMAND "${CMAKE_COMMAND}" -E copy "${BUILD_DIR}/${OUTPUT_NAME}" "${RUNTIME_OUTPUT_DIRECTORY}/${OUTPUT_NAME}" ) endif () endif () # configure build script configure_file ("${BASIS_SCRIPT_EXECUTE_PROCESS}" "${BUILD_SCRIPT}" @ONLY) # add custom command to build executable using MATLAB Compiler add_custom_command ( OUTPUT ${BUILD_OUTPUT} # rebuild when input sources were modified MAIN_DEPENDENCY "${MAIN_SOURCE}" DEPENDS ${DEPENDS} # invoke MATLAB Compiler in either MATLAB or standalone mode # wrapping command in CMake execute_process() command allows for inspection # of command output for error messages and specification of timeout COMMAND "${CMAKE_COMMAND}" "-DCONFIG=$<${BASIS_GE_CONFIG}>" "-DWORKING_DIRECTORY=${WORKING_DIR}" "-DTIMEOUT=${BASIS_MCC_TIMEOUT}" "-DRETRY_EXPRESSION=License checkout failed" "-DRETRY_ATTEMPTS=${BASIS_MCC_RETRY_ATTEMPTS}" "-DRETRY_DELAY=${BASIS_MCC_RETRY_DELAY}" "-DERROR_EXPRESSION=[E|e]rror:|Illegal output name" "-DOUTPUT_FILE=${BUILD_LOG}" "-DERROR_FILE=${BUILD_LOG}" "-DVERBOSE=OFF" "-DLOG_ARGS=ON" "-P" "${BUILD_SCRIPT}" # post build command(s) ${POST_BUILD_COMMAND} # inform user where build log can be found COMMAND "${CMAKE_COMMAND}" -E echo "Build log written to ${BUILD_LOG}" # comment COMMENT "${BUILD_COMMENT}" VERBATIM ) endif () # -------------------------------------------------------------------------- # add custom target add_custom_target (_${TARGET_UID} DEPENDS ${BUILD_OUTPUT} SOURCES ${SOURCES}) add_dependencies (${TARGET_UID} _${TARGET_UID}) # cleanup on "make clean" set (ADDITIONAL_MAKE_CLEAN_FILES "${BUILD_OUTPUT}") if (COMPILE) list (APPEND ADDITIONAL_MAKE_CLEAN_FILES "${BUILD_DIR}/${MCC_OUTPUT_NAME_WE}.prj" "${BUILD_DIR}/mccExcludedFiles.log" "${BUILD_DIR}/mccBuild.log" "${BUILD_DIR}/readme.txt" ) if (LIBRARY) list (APPEND ADDITIONAL_MAKE_CLEAN_FILES "${BUILD_DIR}/${OUTPUT_NAME_WE}.h" "${BUILD_DIR}/${MCC_OUTPUT_NAME_WE}.h" "${BUILD_DIR}/${MCC_OUTPUT_NAME_WE}.c" "${BUILD_DIR}/${MCC_OUTPUT_NAME_WE}.exports" ) else () list (APPEND ADDITIONAL_MAKE_CLEAN_FILES "${BUILD_DIR}/${OUTPUT_NAME}" "${BUILD_DIR}/${MCC_OUTPUT_NAME}" "${BUILD_DIR}/run_${MCC_OUTPUT_NAME_WE}.sh" "${BUILD_DIR}/${MCC_OUTPUT_NAME_WE}_main.c" "${BUILD_DIR}/${MCC_OUTPUT_NAME_WE}_mcc_component_data.c" ) endif () endif () set_property (DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${ADDITIONAL_MAKE_CLEAN_FILES}") unset (ADDITIONAL_MAKE_CLEAN_FILES) # export target if (EXPORT) basis_add_custom_export_target (${TARGET_UID} "${IS_TEST}") endif () # install executable or library if (LIBRARY) if (LIBRARY_HEADER_DIRECTORY) file (RELATIVE_PATH INCLUDE_DIR_SUFFIX "${BINARY_INCLUDE_DIR}" "${LIBRARY_HEADER_DIRECTORY}") if (NOT INCLUDE_DIR_SUFFIX MATCHES "^../") string (REGEX REPLACE "/$" "" INCLUDE_DIR_SUFFIX "${INCLUDE_DIR_SUFFIX}") if (INCLUDE_DIR_SUFFIX STREQUAL ".") set (INCLUDE_DIR_SUFFIX) else () set (INCLUDE_DIR_SUFFIX "/${INCLUDE_DIR_SUFFIX}") endif () install ( FILES "${BUILD_DIR}/${OUTPUT_NAME_WE}.h" DESTINATION "${INSTALL_INCLUDE_DIR}${INCLUDE_DIR_SUFFIX}" COMPONENT "${LIBRARY_COMPONENT}" ) endif () endif () if (LIBRARY_INSTALL_DIRECTORY) if (WIN32) install ( FILES "${BUILD_DIR}/${OUTPUT_NAME_WE}.dll" DESTINATION "${RUNTIME_INSTALL_DIRECTORY}" COMPONENT "${RUNTIME_COMPONENT}" ) endif () install ( FILES "${INSTALL_FILE}" DESTINATION "${LIBRARY_INSTALL_DIRECTORY}" COMPONENT "${LIBRARY_COMPONENT}" ) endif () else () if (RUNTIME_INSTALL_DIRECTORY) if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") install ( DIRECTORY "${BUILD_DIR}/${OUTPUT_NAME_WE}.app" DESTINATION "${RUNTIME_INSTALL_DIRECTORY}" COMPONENT "${RUNTIME_COMPONENT}" USE_SOURCE_PERMISSIONS ) endif () install ( PROGRAMS "${INSTALL_FILE}" DESTINATION "${RUNTIME_INSTALL_DIRECTORY}" COMPONENT "${RUNTIME_COMPONENT}" ) endif () endif () # done if (BASIS_VERBOSE) message (STATUS "Adding build command for target ${TARGET_UID}... - done") endif () endfunction ()