# ============================================================================ # 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: # # # @tp @b BASIS_MEX_TIMEOUT @endtp # # # # @tp @b BASIS_MEX_FLAGS @endtp # # #
Timeout for MEX script execution.
Compile flags used to build MEX-files using the MEX script.
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: # # # @tp @b BASIS_MCC_MATLAB_MODE @endtp # # # # @tp @b BASIS_MCC_FLAGS @endtp # # # # @tp @b BASIS_MCC_TIMEOUT @endtp # # # # @tp @b BASIS_MCC_RETRY_ATTEMPTS @endtp # # # # @tp @b BASIS_MCC_RETRY_DELAY @endtp # # #
Enable/Disable invocation of MATLAB Compiler in MATLAB mode.
Compile flags used to build MATLAB Compiler targets.
Timeout for building MATLAB Compiler targets.
Maximum number of retries on MATLAB Compiler license checkout.
Delay between retries to build MATLAB Compiler compiled targets on license checkout errors.
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 # ("..") 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_\_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 # # # @tp @b DESTINATION dir @endtp # # # # @tp @b COMMAND name @endtp # # # # @tp @b STARTUP mfile @endtp # # # # @tp @b MATLABPATH dir1[ dir2...] # # # # @tp @b OPTIONS opt1[ opt2...] # # #
Installation destination. (default: directory of @c OUTPUT_FILE)
Name of the MATLAB command to execute, i.e., # the name of the main function.
Absolute path of a startup M-file.
List of directories to be added to the MATLAB search path.
Additional options to pass on to the matlab executable.
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 Settings.cmake # 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 # # # @tp @b MFILE file @endtp # # # # @tp @b PREFIX prefix @endtp # # #
MATLAB source file with function prototype and documentation of MEX-file. # (default: none)
Output prefix of build MEX-file such as package name # (the prefix must include the leading + and trailing /).
# # @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 # # # @tp @b COMPONENT name @endtp # # # # @tp @b [NO]EXPORT @endtp # # # # @tp @b NO_BASIS_UTILITIES @endtp # # # # @tp @b USE_BASIS_UTILITIES @endtp # # #
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)
Whether to export this target. (default: @c TRUE)
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)
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)
# # @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 # # #
TODO
# # 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 # # # @tp EXECUTABLE|LIBEXEC|SHARED @endtp # # # # @tp @b COMPONENT name @endtp # # # # @tp @b DESTINATION dir @endtp # # # # @tp @b LIBRARY_COMPONENT name @endtp # # # # @tp @b LIBRARY_DESTINATION dir @endtp # # # # @tp @b HEADER_DESTINATION dir @endtp # # # # @tp @b RUNTIME_COMPONENT name @endtp # # # # @tp @b RUNTIME_DESTINATION dir @endtp # # # # @tp @b [NO]EXPORT @endtp # # # # @tp @b NO_BASIS_UTILITIES @endtp # # # # @tp @b USE_BASIS_UTILITIES @endtp # # #
Type of the MATLAB Compiler target which can be either a stand-alone # executable, an auxiliary executable, or a shared library. # (default: @c EXECUTABLE)
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)
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)
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)
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)
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)
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)
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)
Whether to export this target. (default: @c TRUE)
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)
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)
# # @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 ()