浏览代码

FindMatlab: `matlab_add_mex` has new options `R2017b` and `R2018a`.

- These options are equivalent to `mex` command options `-R2017b` and `-R2018a`.
- `R2017b` is the default, and selects the compatability API.
- `R2018a` is the alternative, and selects the new complex-interleaved API.
- For versions of MATLAB before R2018a, these options are ignored.
- `matlab_add_mex` now works correctly with newer MATLABs.
Cris Luengo 7 年之前
父节点
当前提交
d7e19032d5
共有 1 个文件被更改,包括 87 次插入70 次删除
  1. 87 70
      Modules/FindMatlab.cmake

+ 87 - 70
Modules/FindMatlab.cmake

@@ -884,6 +884,7 @@ endfunction()
          [OUTPUT_NAME output_name]
          [DOCUMENTATION file.txt]
          [LINK_TO target1 target2 ...]
+         [R2017b | R2018a]
          [...]
      )
 
@@ -904,6 +905,12 @@ endfunction()
     the same folder without any processing, with the same name as the final
     mex file, and with extension `.m`. In that case, typing ``help <name>``
     in Matlab prints the documentation contained in this file.
+  ``R2017b`` or ``R2018a`` may be given to specify the version of the C API
+    to use: ``R2017b`` specifies the traditional (separate complex) C API,
+    and corresponds to the ``-R2017b`` flag for the `mex` command. ``R2018a``
+    specifies the new interleaved complex C API, and corresponds to the
+    ``-R2018a`` flag for the `mex` command. Ignored if MATLAB version prior
+    to R2018a. Defaults to ``R2017b``.
   ``MODULE`` or ``SHARED`` may be given to specify the type of library to be
     created. ``EXECUTABLE`` may be given to create an executable instead of
     a library. If no type is given explicitly, the type is ``SHARED``.
@@ -933,7 +940,7 @@ function(matlab_add_mex)
 
   endif()
 
-  set(options EXECUTABLE MODULE SHARED)
+  set(options EXECUTABLE MODULE SHARED R2017b R2018a)
   set(oneValueArgs NAME DOCUMENTATION OUTPUT_NAME)
   set(multiValueArgs LINK_TO SRC)
 
@@ -948,9 +955,25 @@ function(matlab_add_mex)
     set(${prefix}_OUTPUT_NAME ${${prefix}_NAME})
   endif()
 
+  if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, add version source file
+    # TODO: check the file extensions in ${${prefix}_SRC} to see if they're C or C++ files
+    # Currently, the C and C++ versions of the version files are identical, so this doesn't matter.
+    set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/c_mexapi_version.c")
+    #set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/cpp_mexapi_version.cpp")
+  endif()
+
+  if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.4") # For 9.4 (R2018a) and newer, add API macro
+    if(${${prefix}_R2018a})
+      set(MEX_API_MACRO "MATLAB_DEFAULT_RELEASE=R2018a")
+    else()
+      set(MEX_API_MACRO "MATLAB_DEFAULT_RELEASE=R2017b")
+    endif()
+  endif()
+
   if(${prefix}_EXECUTABLE)
     add_executable(${${prefix}_NAME}
       ${${prefix}_SRC}
+      ${MEX_VERSION_FILE}
       ${${prefix}_DOCUMENTATION}
       ${${prefix}_UNPARSED_ARGUMENTS})
   else()
@@ -963,31 +986,25 @@ function(matlab_add_mex)
     add_library(${${prefix}_NAME}
       ${type}
       ${${prefix}_SRC}
+      ${MEX_VERSION_FILE}
       ${${prefix}_DOCUMENTATION}
       ${${prefix}_UNPARSED_ARGUMENTS})
   endif()
 
   target_include_directories(${${prefix}_NAME} PRIVATE ${Matlab_INCLUDE_DIRS})
 
-  if(DEFINED Matlab_MX_LIBRARY)
-    target_link_libraries(${${prefix}_NAME} ${Matlab_MX_LIBRARY})
-  endif()
-
-  if(DEFINED Matlab_ENGINE_LIBRARY)
-    target_link_libraries(${${prefix}_NAME} ${Matlab_ENGINE_LIBRARY})
-  endif()
-
-  if(DEFINED Matlab_DATAARRAY_LIBRARY)
-    target_link_libraries(${${prefix}_NAME} ${Matlab_DATAARRAY_LIBRARY})
+  if(Matlab_HAS_CPP_API)
+    target_link_libraries(${${prefix}_NAME} ${Matlab_ENGINE_LIBRARY} ${Matlab_DATAARRAY_LIBRARY})
   endif()
 
-  target_link_libraries(${${prefix}_NAME} ${Matlab_MEX_LIBRARY} ${${prefix}_LINK_TO})
+  target_link_libraries(${${prefix}_NAME} ${Matlab_MEX_LIBRARY} ${Matlab_MX_LIBRARY} ${${prefix}_LINK_TO})
   set_target_properties(${${prefix}_NAME}
       PROPERTIES
         PREFIX ""
         OUTPUT_NAME ${${prefix}_OUTPUT_NAME}
         SUFFIX ".${Matlab_MEX_EXTENSION}")
 
+  target_compile_definitions(${${prefix}_NAME} PRIVATE ${MEX_API_MACRO} MATLAB_MEX_FILE)
 
   # documentation
   if(NOT ${${prefix}_DOCUMENTATION} STREQUAL "")
@@ -1001,82 +1018,82 @@ function(matlab_add_mex)
   endif() # documentation
 
   # entry point in the mex file + taking care of visibility and symbol clashes.
-  if (MSVC)
-    get_target_property(
-        _previous_link_flags
-        ${${prefix}_NAME}
-        LINK_FLAGS)
-    if(NOT _previous_link_flags)
-      set(_previous_link_flags)
-    endif()
+  if(WIN32)
 
-    set_target_properties(${${prefix}_NAME}
-      PROPERTIES
-        LINK_FLAGS "${_previous_link_flags} /EXPORT:mexFunction")
-  endif()
+    if (MSVC)
+
+      set(_link_flags "${_link_flags} /EXPORT:${mexFunction}")
+      if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, export version
+        set(_link_flags "${_link_flags} /EXPORT:${mexfilerequiredapiversion}")
+      endif()
+
+      if(Matlab_HAS_CPP_API)
+        set(_link_flags "${_link_flags} /EXPORT:${mexCreateMexFunction} /EXPORT:${mexDestroyMexFunction} /EXPORT:${mexFunctionAdapter}")
+        #TODO: Is this necessary?
+      endif()
+
+      set_property(TARGET ${${prefix}_NAME} APPEND PROPERTY LINK_FLAGS ${_link_flags})
+
+    endif() # TODO: what if there's a different compiler on Windows?
 
-  if(WIN32)
     set_target_properties(${${prefix}_NAME}
       PROPERTIES
         DEFINE_SYMBOL "DLL_EXPORT_SYM=__declspec(dllexport)")
+
   else()
 
-    if(HAS_MINUS_PTHREAD AND NOT APPLE)
-      # Apparently, compiling with -pthread generated the proper link flags
-      # and some defines at compilation
-      target_compile_options(${${prefix}_NAME} PRIVATE "-pthread")
+    if(${Matlab_VERSION_STRING} VERSION_LESS "9.1") # For versions prior to 9.1 (R2016b)
+      set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/mexFunction.map)
+    else()                                          # For 9.1 (R2016b) and newer
+      set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/c_exportsmexfileversion.map)
     endif()
 
+    if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.5") # For 9.5 (R2018b) (and newer?)
+      target_compile_options(${${prefix}_NAME} PRIVATE "-fvisibility=default")
+      # This one is weird, it might be a bug in <mex.h> for R2018b. When compiling with
+      # -fvisibility=hidden, the symbol `mexFunction` cannot be exported. Reading the
+      # source code for <mex.h>, it seems that the preprocessor macro `MW_NEEDS_VERSION_H`
+      # needs to be defined for `__attribute__ ((visibility("default")))` to be added
+      # in front of the declaration of `mexFunction`. In previous versions of MATLAB this
+      # was not the case, there `DLL_EXPORT_SYM` needed to be defined.
+      # Adding `-fvisibility=hidden` to the `mex` command causes the build to fail.
+      # TODO: Check that this is still necessary in R2019a when it comes out.
+    endif()
 
-    # if we do not do that, the symbols linked from eg. boost remain weak and
-    # then clash with the ones defined in the matlab process. So by default
-    # the symbols are hidden.
-    # This also means that for shared libraries (like MEX), the entry point
-    # should be explicitly declared with default visibility, otherwise Matlab
-    # cannot find the entry point.
-    # Note that this is particularly meaningful if the MEX wrapper itself
-    # contains symbols that are clashing with Matlab (that are compiled in the
-    # MEX file). In order to propagate the visibility options to the libraries
-    # to which the MEX file is linked against, the -Wl,--exclude-libs,ALL
-    # option should also be specified.
+    if(APPLE)
 
-    set_target_properties(${${prefix}_NAME}
-      PROPERTIES
-        CXX_VISIBILITY_PRESET "hidden"
-        C_VISIBILITY_PRESET "hidden"
-        VISIBILITY_INLINES_HIDDEN ON
-    )
+      if(Matlab_HAS_CPP_API)
+        list(APPEND _ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/cppMexFunction.map) # This one doesn't exist on Linux
+        set(_link_flags "${_link_flags} -Wl,-U,_mexCreateMexFunction -Wl,-U,_mexDestroyMexFunction -Wl,-U,_mexFunctionAdapter")
+        # On MacOS, the MEX command adds the above, without it the link breaks
+        # because we indiscriminately use "cppMexFunction.map" even for C API MEX-files.
+      endif()
 
-    #  get_target_property(
-    #    _previous_link_flags
-    #    ${${prefix}_NAME}
-    #    LINK_FLAGS)
-    #  if(NOT _previous_link_flags)
-    #    set(_previous_link_flags)
-    #  endif()
-
-    #  if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
-    #    set_target_properties(${${prefix}_NAME}
-    #      PROPERTIES
-    #        LINK_FLAGS "${_previous_link_flags} -Wl,--exclude-libs,ALL"
-    #        # -Wl,--version-script=${_FindMatlab_SELF_DIR}/MatlabLinuxVisibility.map"
-    #    )
-    #  elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
-    #    # in this case, all other symbols become hidden.
-    #    set_target_properties(${${prefix}_NAME}
-    #      PROPERTIES
-    #        LINK_FLAGS "${_previous_link_flags} -Wl,-exported_symbol,_mexFunction"
-    #        #-Wl,-exported_symbols_list,${_FindMatlab_SELF_DIR}/MatlabOSXVisilibity.map"
-    #    )
-    #  endif()
+      set(_export_flag_name -exported_symbols_list)
 
+    else() # Linux
 
+      if(HAS_MINUS_PTHREAD)
+        # Apparently, compiling with -pthread generated the proper link flags
+        # and some defines at compilation
+        target_compile_options(${${prefix}_NAME} PRIVATE "-pthread")
+      endif()
+
+      set(_link_flags "${_link_flags} -Wl,--as-needed")
+
+      set(_export_flag_name --version-script)
+
+    endif()
+
+    foreach(_file ${_ver_map_files})
+      set(_link_flags "${_link_flags} -Wl,${_export_flag_name},${_file}")
+    endforeach()
 
     set_target_properties(${${prefix}_NAME}
       PROPERTIES
         DEFINE_SYMBOL "DLL_EXPORT_SYM=__attribute__ ((visibility (\"default\")))"
-    )
-
+        LINK_FLAGS "${_link_flags}"
+    ) # The `mex` command doesn't add this define. Is it necessary?
 
   endif()