1# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2# file Copyright.txt or https://cmake.org/licensing for details.
3
4#[=======================================================================[.rst:
5FindMatlab
6----------
7
8Finds Matlab or Matlab Compiler Runtime (MCR) and provides Matlab tools,
9libraries and compilers to CMake.
10
11This package primary purpose is to find the libraries associated with Matlab
12or the MCR in order to be able to build Matlab extensions (mex files). It
13can also be used:
14
15* to run specific commands in Matlab in case Matlab is available
16* for declaring Matlab unit test
17* to retrieve various information from Matlab (mex extensions, versions and
18  release queries, ...)
19
20.. versionadded:: 3.12
21  Added Matlab Compiler Runtime (MCR) support.
22
23The module supports the following components:
24
25* ``ENG_LIBRARY`` and ``MAT_LIBRARY``: respectively the ``ENG`` and ``MAT``
26  libraries of Matlab
27* ``MAIN_PROGRAM`` the Matlab binary program. Note that this component is not
28  available on the MCR version, and will yield an error if the MCR is found
29  instead of the regular Matlab installation.
30* ``MEX_COMPILER`` the MEX compiler.
31* ``MCC_COMPILER`` the MCC compiler, included with the Matlab Compiler add-on.
32* ``SIMULINK`` the Simulink environment.
33
34.. versionadded:: 3.7
35  Added the ``MAT_LIBRARY`` component.
36
37.. versionadded:: 3.13
38  Added the ``ENGINE_LIBRARY``, ``DATAARRAY_LIBRARY`` and ``MCC_COMPILER``
39  components.
40
41.. versionchanged:: 3.14
42  Removed the ``MX_LIBRARY``, ``ENGINE_LIBRARY`` and ``DATAARRAY_LIBRARY``
43  components.  These libraries are found unconditionally.
44
45.. note::
46
47  The version given to the :command:`find_package` directive is the Matlab
48  **version**, which should not be confused with the Matlab *release* name
49  (eg. `R2014`).
50  The :command:`matlab_get_version_from_release_name` and
51  :command:`matlab_get_release_name_from_version` provide a mapping
52  between the release name and the version.
53
54The variable :variable:`Matlab_ROOT_DIR` may be specified in order to give
55the path of the desired Matlab version. Otherwise, the behavior is platform
56specific:
57
58* Windows: The installed versions of Matlab/MCR are retrieved from the
59  Windows registry
60* OS X: The installed versions of Matlab/MCR are given by the MATLAB
61  default installation paths in ``/Application``. If no such application is
62  found, it falls back to the one that might be accessible from the ``PATH``.
63* Unix: The desired Matlab should be accessible from the ``PATH``. This does
64  not work for MCR installation and :variable:`Matlab_ROOT_DIR` should be
65  specified on this platform.
66
67Additional information is provided when :variable:`MATLAB_FIND_DEBUG` is set.
68When a Matlab/MCR installation is found automatically and the ``MATLAB_VERSION``
69is not given, the version is queried from Matlab directly (on Windows this
70may pop up a Matlab window) or from the MCR installation.
71
72The mapping of the release names and the version of Matlab is performed by
73defining pairs (name, version).  The variable
74:variable:`MATLAB_ADDITIONAL_VERSIONS` may be provided before the call to
75the :command:`find_package` in order to handle additional versions.
76
77A Matlab scripts can be added to the set of tests using the
78:command:`matlab_add_unit_test`. By default, the Matlab unit test framework
79will be used (>= 2013a) to run this script, but regular ``.m`` files
80returning an exit code can be used as well (0 indicating a success).
81
82Module Input Variables
83^^^^^^^^^^^^^^^^^^^^^^
84
85Users or projects may set the following variables to configure the module
86behavior:
87
88:variable:`Matlab_ROOT_DIR`
89  the root of the Matlab installation.
90:variable:`MATLAB_FIND_DEBUG`
91  outputs debug information
92:variable:`MATLAB_ADDITIONAL_VERSIONS`
93  additional versions of Matlab for the automatic retrieval of the installed
94  versions.
95
96Imported targets
97^^^^^^^^^^^^^^^^
98
99.. versionadded:: 3.22
100
101This module defines the following :prop_tgt:`IMPORTED` targets:
102
103``Matlab::mex``
104  The ``mex`` library, always available.
105
106``Matlab::mx``
107  The mx library of Matlab (arrays), always available.
108
109``Matlab::eng``
110  Matlab engine library. Available only if the ``ENG_LIBRARY`` component
111  is requested.
112
113``Matlab::mat``
114  Matlab matrix library. Available only if the ``MAT_LIBRARY`` component
115  is requested.
116
117``Matlab::MatlabEngine``
118  Matlab C++ engine library, always available for R2018a and newer.
119
120``Matlab::MatlabDataArray``
121  Matlab C++ data array library, always available for R2018a and newer.
122
123Variables defined by the module
124^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
125
126Result variables
127""""""""""""""""
128
129``Matlab_FOUND``
130  ``TRUE`` if the Matlab installation is found, ``FALSE``
131  otherwise. All variable below are defined if Matlab is found.
132``Matlab_ROOT_DIR``
133  the final root of the Matlab installation determined by the FindMatlab
134  module.
135``Matlab_MAIN_PROGRAM``
136  the Matlab binary program. Available only if the component ``MAIN_PROGRAM``
137  is given in the :command:`find_package` directive.
138``Matlab_INCLUDE_DIRS``
139 the path of the Matlab libraries headers
140``Matlab_MEX_LIBRARY``
141  library for mex, always available.
142``Matlab_MX_LIBRARY``
143  mx library of Matlab (arrays), always available.
144``Matlab_ENG_LIBRARY``
145  Matlab engine library. Available only if the component ``ENG_LIBRARY``
146  is requested.
147``Matlab_MAT_LIBRARY``
148  Matlab matrix library. Available only if the component ``MAT_LIBRARY``
149  is requested.
150``Matlab_ENGINE_LIBRARY``
151  .. versionadded:: 3.13
152
153  Matlab C++ engine library, always available for R2018a and newer.
154``Matlab_DATAARRAY_LIBRARY``
155  .. versionadded:: 3.13
156
157  Matlab C++ data array library, always available for R2018a and newer.
158``Matlab_LIBRARIES``
159  the whole set of libraries of Matlab
160``Matlab_MEX_COMPILER``
161  the mex compiler of Matlab. Currently not used.
162  Available only if the component ``MEX_COMPILER`` is requested.
163``Matlab_MCC_COMPILER``
164  .. versionadded:: 3.13
165
166  the mcc compiler of Matlab. Included with the Matlab Compiler add-on.
167  Available only if the component ``MCC_COMPILER`` is requested.
168
169Cached variables
170""""""""""""""""
171
172``Matlab_MEX_EXTENSION``
173  the extension of the mex files for the current platform (given by Matlab).
174``Matlab_ROOT_DIR``
175  the location of the root of the Matlab installation found. If this value
176  is changed by the user, the result variables are recomputed.
177
178Provided macros
179^^^^^^^^^^^^^^^
180
181:command:`matlab_get_version_from_release_name`
182  returns the version from the release name
183:command:`matlab_get_release_name_from_version`
184  returns the release name from the Matlab version
185
186Provided functions
187^^^^^^^^^^^^^^^^^^
188
189:command:`matlab_add_mex`
190  adds a target compiling a MEX file.
191:command:`matlab_add_unit_test`
192  adds a Matlab unit test file as a test to the project.
193:command:`matlab_extract_all_installed_versions_from_registry`
194  parses the registry for all Matlab versions. Available on Windows only.
195  The part of the registry parsed is dependent on the host processor
196:command:`matlab_get_all_valid_matlab_roots_from_registry`
197  returns all the possible Matlab or MCR paths, according to a previously
198  given list. Only the existing/accessible paths are kept. This is mainly
199  useful for the searching all possible Matlab installation.
200:command:`matlab_get_mex_suffix`
201  returns the suffix to be used for the mex files
202  (platform/architecture dependent)
203:command:`matlab_get_version_from_matlab_run`
204  returns the version of Matlab/MCR, given the full directory of the Matlab/MCR
205  installation path.
206
207
208Known issues
209^^^^^^^^^^^^
210
211**Symbol clash in a MEX target**
212  By default, every symbols inside a MEX
213  file defined with the command :command:`matlab_add_mex` have hidden
214  visibility, except for the entry point. This is the default behavior of
215  the MEX compiler, which lowers the risk of symbol collision between the
216  libraries shipped with Matlab, and the libraries to which the MEX file is
217  linking to. This is also the default on Windows platforms.
218
219  However, this is not sufficient in certain case, where for instance your
220  MEX file is linking against libraries that are already loaded by Matlab,
221  even if those libraries have different SONAMES.
222  A possible solution is to hide the symbols of the libraries to which the
223  MEX target is linking to. This can be achieved in GNU GCC compilers with
224  the linker option ``-Wl,--exclude-libs,ALL``.
225
226**Tests using GPU resources**
227  in case your MEX file is using the GPU and
228  in order to be able to run unit tests on this MEX file, the GPU resources
229  should be properly released by Matlab. A possible solution is to make
230  Matlab aware of the use of the GPU resources in the session, which can be
231  performed by a command such as ``D = gpuDevice()`` at the beginning of
232  the test script (or via a fixture).
233
234
235Reference
236^^^^^^^^^
237
238.. variable:: Matlab_ROOT_DIR
239
240   The root folder of the Matlab installation. If set before the call to
241   :command:`find_package`, the module will look for the components in that
242   path. If not set, then an automatic search of Matlab
243   will be performed. If set, it should point to a valid version of Matlab.
244
245.. variable:: MATLAB_FIND_DEBUG
246
247   If set, the lookup of Matlab and the intermediate configuration steps are
248   outputted to the console.
249
250.. variable:: MATLAB_ADDITIONAL_VERSIONS
251
252  If set, specifies additional versions of Matlab that may be looked for.
253  The variable should be a list of strings, organized by pairs of release
254  name and versions, such as follows::
255
256    set(MATLAB_ADDITIONAL_VERSIONS
257        "release_name1=corresponding_version1"
258        "release_name2=corresponding_version2"
259        ...
260        )
261
262  Example::
263
264    set(MATLAB_ADDITIONAL_VERSIONS
265        "R2013b=8.2"
266        "R2013a=8.1"
267        "R2012b=8.0")
268
269  The order of entries in this list matters when several versions of
270  Matlab are installed. The priority is set according to the ordering in
271  this list.
272#]=======================================================================]
273
274cmake_policy(PUSH)
275cmake_policy(SET CMP0057 NEW) # if IN_LIST
276
277set(_FindMatlab_SELF_DIR "${CMAKE_CURRENT_LIST_DIR}")
278
279include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
280include(CheckCXXCompilerFlag)
281include(CheckCCompilerFlag)
282
283
284# The currently supported versions. Other version can be added by the user by
285# providing MATLAB_ADDITIONAL_VERSIONS
286if(NOT MATLAB_ADDITIONAL_VERSIONS)
287  set(MATLAB_ADDITIONAL_VERSIONS)
288endif()
289
290set(MATLAB_VERSIONS_MAPPING
291  "R2021b=9.11"
292  "R2021a=9.10"
293  "R2020b=9.9"
294  "R2020a=9.8"
295  "R2019b=9.7"
296  "R2019a=9.6"
297  "R2018b=9.5"
298  "R2018a=9.4"
299  "R2017b=9.3"
300  "R2017a=9.2"
301  "R2016b=9.1"
302  "R2016a=9.0"
303  "R2015b=8.6"
304  "R2015a=8.5"
305  "R2014b=8.4"
306  "R2014a=8.3"
307  "R2013b=8.2"
308  "R2013a=8.1"
309  "R2012b=8.0"
310  "R2012a=7.14"
311  "R2011b=7.13"
312  "R2011a=7.12"
313  "R2010b=7.11"
314
315  ${MATLAB_ADDITIONAL_VERSIONS}
316  )
317
318
319# temporary folder for all Matlab runs
320set(_matlab_temporary_folder ${CMAKE_BINARY_DIR}/Matlab)
321
322if(NOT EXISTS "${_matlab_temporary_folder}")
323  file(MAKE_DIRECTORY "${_matlab_temporary_folder}")
324endif()
325
326#[=======================================================================[.rst:
327.. command:: matlab_get_version_from_release_name
328
329  Returns the version of Matlab (17.58) from a release name (R2017k)
330#]=======================================================================]
331macro(matlab_get_version_from_release_name release_name version_name)
332
333  string(REGEX MATCHALL "${release_name}=([0-9]+\\.?[0-9]*)" _matched ${MATLAB_VERSIONS_MAPPING})
334
335  set(${version_name} "")
336  if(NOT _matched STREQUAL "")
337    set(${version_name} ${CMAKE_MATCH_1})
338  else()
339    message(WARNING "[MATLAB] The release name ${release_name} is not registered")
340  endif()
341  unset(_matched)
342
343endmacro()
344
345
346
347
348
349#[=======================================================================[.rst:
350.. command:: matlab_get_release_name_from_version
351
352  Returns the release name (R2017k) from the version of Matlab (17.58)
353#]=======================================================================]
354macro(matlab_get_release_name_from_version version release_name)
355
356  set(${release_name} "")
357  foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING)
358    string(REGEX MATCHALL "(.+)=${version}" _matched ${_var})
359    if(NOT _matched STREQUAL "")
360      set(${release_name} ${CMAKE_MATCH_1})
361      break()
362    endif()
363  endforeach(_var)
364
365  unset(_var)
366  unset(_matched)
367  if(${release_name} STREQUAL "")
368    message(WARNING "[MATLAB] The version ${version} is not registered")
369  endif()
370
371endmacro()
372
373
374
375
376
377# extracts all the supported release names (R2017k...) of Matlab
378# internal use
379macro(matlab_get_supported_releases list_releases)
380  set(${list_releases})
381  foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING)
382    string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var})
383    if(NOT _matched STREQUAL "")
384      list(APPEND ${list_releases} ${CMAKE_MATCH_1})
385    endif()
386    unset(_matched)
387    unset(CMAKE_MATCH_1)
388  endforeach(_var)
389  unset(_var)
390endmacro()
391
392
393
394# extracts all the supported versions of Matlab
395# internal use
396macro(matlab_get_supported_versions list_versions)
397  set(${list_versions})
398  foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING)
399    string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var})
400    if(NOT _matched STREQUAL "")
401      list(APPEND ${list_versions} ${CMAKE_MATCH_2})
402    endif()
403    unset(_matched)
404    unset(CMAKE_MATCH_1)
405  endforeach(_var)
406  unset(_var)
407endmacro()
408
409
410#[=======================================================================[.rst:
411.. command:: matlab_extract_all_installed_versions_from_registry
412
413  This function parses the registry and founds the Matlab versions that are
414  installed. The found versions are returned in `matlab_versions`.
415  Set `win64` to `TRUE` if the 64 bit version of Matlab should be looked for
416  The returned list contains all versions under
417  ``HKLM\\SOFTWARE\\Mathworks\\MATLAB`` and
418  ``HKLM\\SOFTWARE\\Mathworks\\MATLAB Runtime`` or an empty list in case an
419  error occurred (or nothing found).
420
421  .. note::
422
423    Only the versions are provided. No check is made over the existence of the
424    installation referenced in the registry,
425
426#]=======================================================================]
427function(matlab_extract_all_installed_versions_from_registry win64 matlab_versions)
428
429  if(NOT CMAKE_HOST_WIN32)
430    message(FATAL_ERROR "[MATLAB] This macro can only be called by a windows host (call to reg.exe)")
431  endif()
432
433  if(${win64} AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "64")
434    set(APPEND_REG "/reg:64")
435  else()
436    set(APPEND_REG "/reg:32")
437  endif()
438
439  set(matlabs_from_registry)
440
441  foreach(_installation_type IN ITEMS "MATLAB" "MATLAB Runtime" "MATLAB Compiler Runtime")
442
443    # /reg:64 should be added on 64 bits capable OSs in order to enable the
444    # redirection of 64 bits applications
445    execute_process(
446      COMMAND reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Mathworks\\${_installation_type}" /f * /k ${APPEND_REG}
447      RESULT_VARIABLE resultMatlab
448      OUTPUT_VARIABLE varMatlab
449      ERROR_VARIABLE errMatlab
450      INPUT_FILE NUL
451      )
452
453
454    if(resultMatlab EQUAL 0)
455
456      string(
457        REGEX MATCHALL "${_installation_type}\\\\([0-9]+(\\.[0-9]+)?)"
458        matlab_versions_regex ${varMatlab})
459
460      foreach(match IN LISTS matlab_versions_regex)
461        string(
462          REGEX MATCH "${_installation_type}\\\\(([0-9]+)(\\.([0-9]+))?)"
463          current_match ${match})
464
465        set(_matlab_current_version ${CMAKE_MATCH_1})
466        set(current_matlab_version_major ${CMAKE_MATCH_2})
467        set(current_matlab_version_minor ${CMAKE_MATCH_4})
468        if(NOT current_matlab_version_minor)
469          set(current_matlab_version_minor "0")
470        endif()
471
472        list(APPEND matlabs_from_registry ${_matlab_current_version})
473        unset(_matlab_current_version)
474      endforeach()
475
476    endif()
477  endforeach()
478
479  if(matlabs_from_registry)
480    list(REMOVE_DUPLICATES matlabs_from_registry)
481    list(SORT matlabs_from_registry COMPARE NATURAL)
482    list(REVERSE matlabs_from_registry)
483  endif()
484
485  set(${matlab_versions} ${matlabs_from_registry} PARENT_SCOPE)
486
487endfunction()
488
489
490
491# (internal)
492macro(extract_matlab_versions_from_registry_brute_force matlab_versions)
493  # get the supported versions
494  set(matlab_supported_versions)
495  matlab_get_supported_versions(matlab_supported_versions)
496
497
498  # this is a manual population of the versions we want to look for
499  # this can be done as is, but preferably with the call to
500  # matlab_get_supported_versions and variable
501
502  # populating the versions we want to look for
503  # set(matlab_supported_versions)
504
505  # # Matlab 7
506  # set(matlab_major 7)
507  # foreach(current_matlab_minor RANGE 4 20)
508    # list(APPEND matlab_supported_versions "${matlab_major}.${current_matlab_minor}")
509  # endforeach(current_matlab_minor)
510
511  # # Matlab 8
512  # set(matlab_major 8)
513  # foreach(current_matlab_minor RANGE 0 5)
514    # list(APPEND matlab_supported_versions "${matlab_major}.${current_matlab_minor}")
515  # endforeach(current_matlab_minor)
516
517  # # taking into account the possible additional versions provided by the user
518  # if(DEFINED MATLAB_ADDITIONAL_VERSIONS)
519    # list(APPEND matlab_supported_versions MATLAB_ADDITIONAL_VERSIONS)
520  # endif()
521
522  # we order from more recent to older
523  if(matlab_supported_versions)
524    list(REMOVE_DUPLICATES matlab_supported_versions)
525    list(SORT matlab_supported_versions COMPARE NATURAL)
526    list(REVERSE matlab_supported_versions)
527  endif()
528
529  set(${matlab_versions} ${matlab_supported_versions})
530endmacro()
531
532
533
534
535#[=======================================================================[.rst:
536.. command:: matlab_get_all_valid_matlab_roots_from_registry
537
538  Populates the Matlab root with valid versions of Matlab or
539  Matlab Runtime (MCR).
540  The returned matlab_roots is organized in triplets
541  ``(type,version_number,matlab_root_path)``, where ``type``
542  indicates either ``MATLAB`` or ``MCR``.
543
544  ::
545
546    matlab_get_all_valid_matlab_roots_from_registry(
547        matlab_versions
548        matlab_roots)
549
550  ``matlab_versions``
551    the versions of each of the Matlab or MCR installations
552  ``matlab_roots``
553    the location of each of the Matlab or MCR installations
554#]=======================================================================]
555function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_roots)
556
557  # The matlab_versions comes either from
558  # extract_matlab_versions_from_registry_brute_force or
559  # matlab_extract_all_installed_versions_from_registry.
560
561  set(_matlab_roots_list )
562  # check for Matlab installations
563  foreach(_matlab_current_version ${matlab_versions})
564    get_filename_component(
565      current_MATLAB_ROOT
566      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${_matlab_current_version};MATLABROOT]"
567      ABSOLUTE)
568
569    if(EXISTS "${current_MATLAB_ROOT}")
570      list(APPEND _matlab_roots_list "MATLAB" ${_matlab_current_version} ${current_MATLAB_ROOT})
571    endif()
572
573  endforeach()
574
575  # Check for MCR installations
576  foreach(_matlab_current_version ${matlab_versions})
577    get_filename_component(
578      current_MATLAB_ROOT
579      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB Runtime\\${_matlab_current_version};MATLABROOT]"
580      ABSOLUTE)
581
582    # remove the dot
583    string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
584
585    if(EXISTS "${current_MATLAB_ROOT}")
586      list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
587    endif()
588
589  endforeach()
590
591  # Check for old MCR installations
592  foreach(_matlab_current_version ${matlab_versions})
593    get_filename_component(
594      current_MATLAB_ROOT
595      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB Compiler Runtime\\${_matlab_current_version};MATLABROOT]"
596      ABSOLUTE)
597
598    # remove the dot
599    string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
600
601    if(EXISTS "${current_MATLAB_ROOT}")
602      list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
603    endif()
604
605  endforeach()
606  set(${matlab_roots} ${_matlab_roots_list} PARENT_SCOPE)
607endfunction()
608
609#[=======================================================================[.rst:
610.. command:: matlab_get_mex_suffix
611
612  Returns the extension of the mex files (the suffixes).
613  This function should not be called before the appropriate Matlab root has
614  been found.
615
616  ::
617
618    matlab_get_mex_suffix(
619        matlab_root
620        mex_suffix)
621
622  ``matlab_root``
623    the root of the Matlab/MCR installation
624  ``mex_suffix``
625    the variable name in which the suffix will be returned.
626#]=======================================================================]
627function(matlab_get_mex_suffix matlab_root mex_suffix)
628
629  # todo setup the extension properly. Currently I do not know if this is
630  # sufficient for all win32 distributions.
631  # there is also CMAKE_EXECUTABLE_SUFFIX that could be tweaked
632  set(mexext_suffix "")
633  if(WIN32)
634    list(APPEND mexext_suffix ".bat")
635  endif()
636
637  # we first try without suffix, since cmake does not understand a list with
638  # one empty string element
639  find_program(
640    Matlab_MEXEXTENSIONS_PROG
641    NAMES mexext
642    PATHS ${matlab_root}/bin
643    DOC "Matlab MEX extension provider"
644    NO_DEFAULT_PATH
645  )
646
647  foreach(current_mexext_suffix IN LISTS mexext_suffix)
648    if(NOT DEFINED Matlab_MEXEXTENSIONS_PROG OR NOT Matlab_MEXEXTENSIONS_PROG)
649      # this call should populate the cache automatically
650      find_program(
651        Matlab_MEXEXTENSIONS_PROG
652        "mexext${current_mexext_suffix}"
653        PATHS ${matlab_root}/bin
654        DOC "Matlab MEX extension provider"
655        NO_DEFAULT_PATH
656      )
657    endif()
658  endforeach(current_mexext_suffix)
659  if(MATLAB_FIND_DEBUG)
660    message(STATUS "[MATLAB] Determining mex files extensions from '${matlab_root}/bin' with program '${Matlab_MEXEXTENSIONS_PROG}'")
661  endif()
662
663  # the program has been found?
664  if((NOT Matlab_MEXEXTENSIONS_PROG) OR (NOT EXISTS ${Matlab_MEXEXTENSIONS_PROG}))
665    if(MATLAB_FIND_DEBUG)
666      message(WARNING "[MATLAB] Cannot found mexext program. Matlab root is ${matlab_root}")
667    endif()
668    unset(Matlab_MEXEXTENSIONS_PROG CACHE)
669    return()
670  endif()
671
672  set(_matlab_mex_extension)
673
674  set(devnull)
675  if(UNIX)
676    set(devnull INPUT_FILE /dev/null)
677  elseif(WIN32)
678    set(devnull INPUT_FILE NUL)
679  endif()
680
681  if(WIN32)
682    # this environment variable is used to determine the arch on Windows
683    if(CMAKE_SIZEOF_VOID_P EQUAL 8)
684      set(ENV{MATLAB_ARCH} "win64")
685    else()
686      set(ENV{MATLAB_ARCH} "win32")
687    endif()
688  endif()
689
690  # this is the preferred way. If this does not work properly (eg. MCR on Windows), then we use our own knowledge
691  execute_process(
692    COMMAND ${Matlab_MEXEXTENSIONS_PROG}
693    OUTPUT_VARIABLE _matlab_mex_extension
694    ERROR_VARIABLE _matlab_mex_extension_error
695    OUTPUT_STRIP_TRAILING_WHITESPACE
696    ${devnull})
697  unset(ENV{MATLAB_ARCH})
698
699  if(_matlab_mex_extension_error)
700    if(WIN32)
701      # this is only for intel architecture
702      if(CMAKE_SIZEOF_VOID_P EQUAL 8)
703        set(_matlab_mex_extension "mexw64")
704      else()
705        set(_matlab_mex_extension "mexw32")
706      endif()
707    endif()
708  endif()
709
710  string(STRIP "${_matlab_mex_extension}"  _matlab_mex_extension)
711  if(MATLAB_FIND_DEBUG)
712    message(STATUS "[MATLAB] '${Matlab_MEXEXTENSIONS_PROG}' : determined extension '${_matlab_mex_extension}' and error string is '${_matlab_mex_extension_error}'")
713  endif()
714
715  unset(Matlab_MEXEXTENSIONS_PROG CACHE)
716  set(${mex_suffix} ${_matlab_mex_extension} PARENT_SCOPE)
717endfunction()
718
719
720
721
722#[=======================================================================[.rst:
723.. command:: matlab_get_version_from_matlab_run
724
725  This function runs Matlab program specified on arguments and extracts its
726  version. If the path provided for the Matlab installation points to an MCR
727  installation, the version is extracted from the installed files.
728
729  ::
730
731    matlab_get_version_from_matlab_run(
732        matlab_binary_path
733        matlab_list_versions)
734
735  ``matlab_binary_path``
736    the location of the `matlab` binary executable
737  ``matlab_list_versions``
738    the version extracted from Matlab
739#]=======================================================================]
740function(matlab_get_version_from_matlab_run matlab_binary_program matlab_list_versions)
741
742  set(${matlab_list_versions} "" PARENT_SCOPE)
743
744  if(MATLAB_FIND_DEBUG)
745    message(STATUS "[MATLAB] Determining the version of Matlab from ${matlab_binary_program}")
746  endif()
747
748  if(EXISTS "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp")
749    if(MATLAB_FIND_DEBUG)
750      message(STATUS "[MATLAB] Removing previous ${_matlab_temporary_folder}/matlabVersionLog.cmaketmp file")
751    endif()
752    file(REMOVE "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp")
753  endif()
754
755
756  # the log file is needed since on windows the command executes in a new
757  # window and it is not possible to get back the answer of Matlab
758  # the -wait command is needed on windows, otherwise the call returns
759  # immediately after the program launches itself.
760  if(WIN32)
761    set(_matlab_additional_commands "-wait")
762  endif()
763
764  set(devnull)
765  if(UNIX)
766    set(devnull INPUT_FILE /dev/null)
767  elseif(WIN32)
768    set(devnull INPUT_FILE NUL)
769  endif()
770
771  # timeout set to 120 seconds, in case it does not start
772  # note as said before OUTPUT_VARIABLE cannot be used in a platform
773  # independent manner however, not setting it would flush the output of Matlab
774  # in the current console (unix variant)
775  execute_process(
776    COMMAND "${matlab_binary_program}" -nosplash -nojvm ${_matlab_additional_commands} -logfile "matlabVersionLog.cmaketmp" -nodesktop -nodisplay -r "version, exit"
777    OUTPUT_VARIABLE _matlab_version_from_cmd_dummy
778    RESULT_VARIABLE _matlab_result_version_call
779    ERROR_VARIABLE _matlab_result_version_call_error
780    TIMEOUT 120
781    WORKING_DIRECTORY "${_matlab_temporary_folder}"
782    ${devnull}
783    )
784
785  if(_matlab_result_version_call MATCHES "timeout")
786    if(MATLAB_FIND_DEBUG)
787      message(WARNING "[MATLAB] Unable to determine the version of Matlab."
788        " Matlab call timed out after 120 seconds.")
789    endif()
790    return()
791  endif()
792
793  if(${_matlab_result_version_call})
794    if(MATLAB_FIND_DEBUG)
795      message(WARNING "[MATLAB] Unable to determine the version of Matlab. Matlab call returned with error ${_matlab_result_version_call}.")
796    endif()
797    return()
798  elseif(NOT EXISTS "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp")
799    if(MATLAB_FIND_DEBUG)
800      message(WARNING "[MATLAB] Unable to determine the version of Matlab. The log file does not exist.")
801    endif()
802    return()
803  endif()
804
805  # if successful, read back the log
806  file(READ "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp" _matlab_version_from_cmd)
807  file(REMOVE "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp")
808
809  set(index -1)
810  string(FIND "${_matlab_version_from_cmd}" "ans" index)
811  if(index EQUAL -1)
812
813    if(MATLAB_FIND_DEBUG)
814      message(WARNING "[MATLAB] Cannot find the version of Matlab returned by the run.")
815    endif()
816
817  else()
818    set(matlab_list_of_all_versions_tmp)
819
820    string(SUBSTRING "${_matlab_version_from_cmd}" ${index} -1 substring_ans)
821    string(
822      REGEX MATCHALL "ans[\r\n\t ]*=[\r\n\t ]*'?([0-9]+(\\.[0-9]+)?)"
823      matlab_versions_regex
824      ${substring_ans})
825    foreach(match IN LISTS matlab_versions_regex)
826      string(
827        REGEX MATCH "ans[\r\n\t ]*=[\r\n\t ]*'?(([0-9]+)(\\.([0-9]+))?)"
828        current_match ${match})
829
830      list(APPEND matlab_list_of_all_versions_tmp ${CMAKE_MATCH_1})
831    endforeach()
832    if(matlab_list_of_all_versions_tmp)
833      list(REMOVE_DUPLICATES matlab_list_of_all_versions_tmp)
834    endif()
835    set(${matlab_list_versions} ${matlab_list_of_all_versions_tmp} PARENT_SCOPE)
836
837  endif()
838
839endfunction()
840
841#[=======================================================================[.rst:
842.. command:: matlab_add_unit_test
843
844  Adds a Matlab unit test to the test set of cmake/ctest.
845  This command requires the component ``MAIN_PROGRAM`` and hence is not
846  available for an MCR installation.
847
848  The unit test uses the Matlab unittest framework (default, available
849  starting Matlab 2013b+) except if the option ``NO_UNITTEST_FRAMEWORK``
850  is given.
851
852  The function expects one Matlab test script file to be given.
853  In the case ``NO_UNITTEST_FRAMEWORK`` is given, the unittest script file
854  should contain the script to be run, plus an exit command with the exit
855  value. This exit value will be passed to the ctest framework (0 success,
856  non 0 failure). Additional arguments accepted by :command:`add_test` can be
857  passed through ``TEST_ARGS`` (eg. ``CONFIGURATION <config> ...``).
858
859  ::
860
861    matlab_add_unit_test(
862        NAME <name>
863        UNITTEST_FILE matlab_file_containing_unittest.m
864        [CUSTOM_TEST_COMMAND matlab_command_to_run_as_test]
865        [UNITTEST_PRECOMMAND matlab_command_to_run]
866        [TIMEOUT timeout]
867        [ADDITIONAL_PATH path1 [path2 ...]]
868        [MATLAB_ADDITIONAL_STARTUP_OPTIONS option1 [option2 ...]]
869        [TEST_ARGS arg1 [arg2 ...]]
870        [NO_UNITTEST_FRAMEWORK]
871        )
872
873  The function arguments are:
874
875  ``NAME``
876    name of the unittest in ctest.
877  ``UNITTEST_FILE``
878    the matlab unittest file. Its path will be automatically
879    added to the Matlab path.
880  ``CUSTOM_TEST_COMMAND``
881    Matlab script command to run as the test.
882    If this is not set, then the following is run:
883    ``runtests('matlab_file_name'), exit(max([ans(1,:).Failed]))``
884    where ``matlab_file_name`` is the ``UNITTEST_FILE`` without the extension.
885  ``UNITTEST_PRECOMMAND``
886    Matlab script command to be ran before the file
887    containing the test (eg. GPU device initialization based on CMake
888    variables).
889  ``TIMEOUT``
890    the test timeout in seconds. Defaults to 180 seconds as the
891    Matlab unit test may hang.
892  ``ADDITIONAL_PATH``
893    a list of paths to add to the Matlab path prior to
894    running the unit test.
895  ``MATLAB_ADDITIONAL_STARTUP_OPTIONS``
896    a list of additional option in order
897    to run Matlab from the command line.
898    ``-nosplash -nodesktop -nodisplay`` are always added.
899  ``TEST_ARGS``
900    Additional options provided to the add_test command. These
901    options are added to the default options (eg. "CONFIGURATIONS Release")
902  ``NO_UNITTEST_FRAMEWORK``
903    when set, indicates that the test should not
904    use the unittest framework of Matlab (available for versions >= R2013a).
905  ``WORKING_DIRECTORY``
906    This will be the working directory for the test. If specified it will
907    also be the output directory used for the log file of the test run.
908    If not specified the temporary directory ``${CMAKE_BINARY_DIR}/Matlab`` will
909    be used as the working directory and the log location.
910
911#]=======================================================================]
912function(matlab_add_unit_test)
913
914  if(NOT Matlab_MAIN_PROGRAM)
915    message(FATAL_ERROR "[MATLAB] This functionality needs the MAIN_PROGRAM component (not default)")
916  endif()
917
918  set(options NO_UNITTEST_FRAMEWORK)
919  set(oneValueArgs NAME UNITTEST_FILE TIMEOUT WORKING_DIRECTORY
920    UNITTEST_PRECOMMAND CUSTOM_TEST_COMMAND)
921  set(multiValueArgs ADDITIONAL_PATH MATLAB_ADDITIONAL_STARTUP_OPTIONS TEST_ARGS)
922
923  set(prefix _matlab_unittest_prefix)
924  cmake_parse_arguments(PARSE_ARGV 0 ${prefix} "${options}" "${oneValueArgs}" "${multiValueArgs}" )
925
926  if(NOT ${prefix}_NAME)
927    message(FATAL_ERROR "[MATLAB] The Matlab test name cannot be empty")
928  endif()
929
930  add_test(NAME ${${prefix}_NAME}
931           COMMAND ${CMAKE_COMMAND}
932            "-Dtest_name=${${prefix}_NAME}"
933            "-Dadditional_paths=${${prefix}_ADDITIONAL_PATH}"
934            "-Dtest_timeout=${${prefix}_TIMEOUT}"
935            "-Doutput_directory=${_matlab_temporary_folder}"
936            "-Dworking_directory=${${prefix}_WORKING_DIRECTORY}"
937            "-DMatlab_PROGRAM=${Matlab_MAIN_PROGRAM}"
938            "-Dno_unittest_framework=${${prefix}_NO_UNITTEST_FRAMEWORK}"
939            "-DMatlab_ADDITIONAL_STARTUP_OPTIONS=${${prefix}_MATLAB_ADDITIONAL_STARTUP_OPTIONS}"
940            "-Dunittest_file_to_run=${${prefix}_UNITTEST_FILE}"
941            "-Dcustom_Matlab_test_command=${${prefix}_CUSTOM_TEST_COMMAND}"
942            "-Dcmd_to_run_before_test=${${prefix}_UNITTEST_PRECOMMAND}"
943            -P ${_FindMatlab_SELF_DIR}/MatlabTestsRedirect.cmake
944           ${${prefix}_TEST_ARGS}
945           ${${prefix}_UNPARSED_ARGUMENTS}
946           )
947endfunction()
948
949
950#[=======================================================================[.rst:
951.. command:: matlab_add_mex
952
953  Adds a Matlab MEX target.
954  This commands compiles the given sources with the current tool-chain in
955  order to produce a MEX file. The final name of the produced output may be
956  specified, as well as additional link libraries, and a documentation entry
957  for the MEX file. Remaining arguments of the call are passed to the
958  :command:`add_library` or :command:`add_executable` command.
959
960  ::
961
962     matlab_add_mex(
963         NAME <name>
964         [EXECUTABLE | MODULE | SHARED]
965         SRC src1 [src2 ...]
966         [OUTPUT_NAME output_name]
967         [DOCUMENTATION file.txt]
968         [LINK_TO target1 target2 ...]
969         [R2017b | R2018a]
970         [EXCLUDE_FROM_ALL]
971         [...]
972     )
973
974  ``NAME``
975    name of the target.
976  ``SRC``
977    list of source files.
978  ``LINK_TO``
979    a list of additional link dependencies.  The target links to ``libmex``
980    and ``libmx`` by default.
981  ``OUTPUT_NAME``
982    if given, overrides the default name. The default name is
983    the name of the target without any prefix and
984    with ``Matlab_MEX_EXTENSION`` suffix.
985  ``DOCUMENTATION``
986    if given, the file ``file.txt`` will be considered as
987    being the documentation file for the MEX file. This file is copied into
988    the same folder without any processing, with the same name as the final
989    mex file, and with extension `.m`. In that case, typing ``help <name>``
990    in Matlab prints the documentation contained in this file.
991  ``R2017b`` or ``R2018a``
992    .. versionadded:: 3.14
993
994    May be given to specify the version of the C API
995    to use: ``R2017b`` specifies the traditional (separate complex) C API,
996    and corresponds to the ``-R2017b`` flag for the `mex` command. ``R2018a``
997    specifies the new interleaved complex C API, and corresponds to the
998    ``-R2018a`` flag for the `mex` command. Ignored if MATLAB version prior
999    to R2018a. Defaults to ``R2017b``.
1000
1001  ``MODULE`` or ``SHARED``
1002    .. versionadded:: 3.7
1003
1004    May be given to specify the type of library to be
1005    created.
1006
1007  ``EXECUTABLE``
1008    .. versionadded:: 3.7
1009
1010    May be given to create an executable instead of
1011    a library. If no type is given explicitly, the type is ``SHARED``.
1012  ``EXCLUDE_FROM_ALL``
1013    This option has the same meaning as for :prop_tgt:`EXCLUDE_FROM_ALL` and
1014    is forwarded to :command:`add_library` or :command:`add_executable`
1015    commands.
1016
1017  The documentation file is not processed and should be in the following
1018  format:
1019
1020  ::
1021
1022    % This is the documentation
1023    function ret = mex_target_output_name(input1)
1024
1025#]=======================================================================]
1026function(matlab_add_mex)
1027
1028  if(NOT WIN32)
1029    # we do not need all this on Windows
1030    # pthread options
1031    if(CMAKE_CXX_COMPILER_LOADED)
1032      check_cxx_compiler_flag(-pthread HAS_MINUS_PTHREAD)
1033    elseif(CMAKE_C_COMPILER_LOADED)
1034      check_c_compiler_flag(-pthread HAS_MINUS_PTHREAD)
1035    endif()
1036    # we should use try_compile instead, the link flags are discarded from
1037    # this compiler_flag function.
1038    #check_cxx_compiler_flag(-Wl,--exclude-libs,ALL HAS_SYMBOL_HIDING_CAPABILITY)
1039
1040  endif()
1041
1042  set(options EXECUTABLE MODULE SHARED R2017b R2018a EXCLUDE_FROM_ALL)
1043  set(oneValueArgs NAME DOCUMENTATION OUTPUT_NAME)
1044  set(multiValueArgs LINK_TO SRC)
1045
1046  set(prefix _matlab_addmex_prefix)
1047  cmake_parse_arguments(${prefix} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
1048
1049  if(NOT ${prefix}_NAME)
1050    message(FATAL_ERROR "[MATLAB] The MEX target name cannot be empty")
1051  endif()
1052
1053  if(NOT ${prefix}_OUTPUT_NAME)
1054    set(${prefix}_OUTPUT_NAME ${${prefix}_NAME})
1055  endif()
1056
1057  if(NOT Matlab_VERSION_STRING VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, add version source file
1058    # Add the correct version file depending on which languages are enabled in the project
1059    if(CMAKE_C_COMPILER_LOADED)
1060      # If C is enabled, use the .c file as it will work fine also with C++
1061      set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/c_mexapi_version.c")
1062    elseif(CMAKE_CXX_COMPILER_LOADED)
1063      # If C is not enabled, check if CXX is enabled and use the .cpp file
1064      # to avoid that the .c file is silently ignored
1065      set(MEX_VERSION_FILE "${Matlab_ROOT_DIR}/extern/version/cpp_mexapi_version.cpp")
1066    else()
1067      # If neither C or CXX is enabled, warn because we cannot add the source.
1068      # TODO: add support for fortran mex files
1069      message(WARNING "[MATLAB] matlab_add_mex requires that at least C or CXX are enabled languages")
1070    endif()
1071  endif()
1072
1073  # For 9.4 (R2018a) and newer, add API macro.
1074  # Add it for unknown versions too, just in case.
1075  if(NOT Matlab_VERSION_STRING VERSION_LESS "9.4"
1076      OR Matlab_VERSION_STRING STREQUAL "unknown")
1077    if(${${prefix}_R2018a})
1078      set(MEX_API_MACRO "MATLAB_DEFAULT_RELEASE=R2018a")
1079    else()
1080      set(MEX_API_MACRO "MATLAB_DEFAULT_RELEASE=R2017b")
1081    endif()
1082  endif()
1083
1084  set(_option_EXCLUDE_FROM_ALL)
1085  if(${prefix}_EXCLUDE_FROM_ALL)
1086    set(_option_EXCLUDE_FROM_ALL "EXCLUDE_FROM_ALL")
1087  endif()
1088
1089  if(${prefix}_EXECUTABLE)
1090    add_executable(${${prefix}_NAME}
1091      ${_option_EXCLUDE_FROM_ALL}
1092      ${${prefix}_SRC}
1093      ${MEX_VERSION_FILE}
1094      ${${prefix}_DOCUMENTATION}
1095      ${${prefix}_UNPARSED_ARGUMENTS})
1096  else()
1097    if(${prefix}_MODULE)
1098      set(type MODULE)
1099    else()
1100      set(type SHARED)
1101    endif()
1102
1103    add_library(${${prefix}_NAME}
1104      ${type}
1105      ${_option_EXCLUDE_FROM_ALL}
1106      ${${prefix}_SRC}
1107      ${MEX_VERSION_FILE}
1108      ${${prefix}_DOCUMENTATION}
1109      ${${prefix}_UNPARSED_ARGUMENTS})
1110  endif()
1111
1112  target_include_directories(${${prefix}_NAME} PRIVATE ${Matlab_INCLUDE_DIRS})
1113
1114  if(Matlab_HAS_CPP_API)
1115    if(Matlab_ENGINE_LIBRARY)
1116      target_link_libraries(${${prefix}_NAME} ${Matlab_ENGINE_LIBRARY})
1117    endif()
1118    if(Matlab_DATAARRAY_LIBRARY)
1119      target_link_libraries(${${prefix}_NAME} ${Matlab_DATAARRAY_LIBRARY})
1120    endif()
1121  endif()
1122
1123  target_link_libraries(${${prefix}_NAME} ${Matlab_MEX_LIBRARY} ${Matlab_MX_LIBRARY} ${${prefix}_LINK_TO})
1124  set_target_properties(${${prefix}_NAME}
1125      PROPERTIES
1126        PREFIX ""
1127        OUTPUT_NAME ${${prefix}_OUTPUT_NAME}
1128        SUFFIX ".${Matlab_MEX_EXTENSION}")
1129
1130  target_compile_definitions(${${prefix}_NAME} PRIVATE ${MEX_API_MACRO} MATLAB_MEX_FILE)
1131
1132  # documentation
1133  if(NOT ${${prefix}_DOCUMENTATION} STREQUAL "")
1134    get_target_property(output_name ${${prefix}_NAME} OUTPUT_NAME)
1135    add_custom_command(
1136      TARGET ${${prefix}_NAME}
1137      PRE_BUILD
1138      COMMAND ${CMAKE_COMMAND} -E copy_if_different ${${prefix}_DOCUMENTATION} $<TARGET_FILE_DIR:${${prefix}_NAME}>/${output_name}.m
1139      COMMENT "[MATLAB] Copy ${${prefix}_NAME} documentation file into the output folder"
1140    )
1141  endif() # documentation
1142
1143  # entry point in the mex file + taking care of visibility and symbol clashes.
1144  if(WIN32)
1145
1146    if (MSVC)
1147
1148      set(_link_flags "${_link_flags} /EXPORT:mexFunction")
1149      if(NOT Matlab_VERSION_STRING VERSION_LESS "9.1") # For 9.1 (R2016b) and newer, export version
1150        set(_link_flags "${_link_flags} /EXPORT:mexfilerequiredapiversion")
1151      endif()
1152
1153      set_property(TARGET ${${prefix}_NAME} APPEND PROPERTY LINK_FLAGS ${_link_flags})
1154
1155    endif() # No other compiler currently supported on Windows.
1156
1157    set_target_properties(${${prefix}_NAME}
1158      PROPERTIES
1159        DEFINE_SYMBOL "DLL_EXPORT_SYM=__declspec(dllexport)")
1160
1161  else()
1162
1163    if(Matlab_VERSION_STRING VERSION_LESS "9.1") # For versions prior to 9.1 (R2016b)
1164      set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/mexFunction.map)
1165    else()                                          # For 9.1 (R2016b) and newer
1166      set(_ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/c_exportsmexfileversion.map)
1167    endif()
1168
1169    if(NOT Matlab_VERSION_STRING VERSION_LESS "9.5") # For 9.5 (R2018b) (and newer?)
1170      target_compile_options(${${prefix}_NAME} PRIVATE "-fvisibility=default")
1171      # This one is weird, it might be a bug in <mex.h> for R2018b. When compiling with
1172      # -fvisibility=hidden, the symbol `mexFunction` cannot be exported. Reading the
1173      # source code for <mex.h>, it seems that the preprocessor macro `MW_NEEDS_VERSION_H`
1174      # needs to be defined for `__attribute__((visibility("default")))` to be added
1175      # in front of the declaration of `mexFunction`. In previous versions of MATLAB this
1176      # was not the case, there `DLL_EXPORT_SYM` needed to be defined.
1177      # Adding `-fvisibility=hidden` to the `mex` command causes the build to fail.
1178      # TODO: Check that this is still necessary in R2019a when it comes out.
1179    endif()
1180
1181    if(APPLE)
1182
1183      if(Matlab_HAS_CPP_API)
1184        list(APPEND _ver_map_files ${Matlab_EXTERN_LIBRARY_DIR}/cppMexFunction.map) # This one doesn't exist on Linux
1185        set(_link_flags "${_link_flags} -Wl,-U,_mexCreateMexFunction -Wl,-U,_mexDestroyMexFunction -Wl,-U,_mexFunctionAdapter")
1186        # On MacOS, the MEX command adds the above, without it the link breaks
1187        # because we indiscriminately use "cppMexFunction.map" even for C API MEX-files.
1188      endif()
1189
1190      set(_export_flag_name -exported_symbols_list)
1191
1192    else() # Linux
1193
1194      if(HAS_MINUS_PTHREAD)
1195        # Apparently, compiling with -pthread generated the proper link flags
1196        # and some defines at compilation
1197        target_compile_options(${${prefix}_NAME} PRIVATE "-pthread")
1198      endif()
1199
1200      set(_link_flags "${_link_flags} -Wl,--as-needed")
1201
1202      set(_export_flag_name --version-script)
1203
1204    endif()
1205
1206    foreach(_file ${_ver_map_files})
1207      set(_link_flags "${_link_flags} -Wl,${_export_flag_name},${_file}")
1208    endforeach()
1209
1210    # The `mex` command doesn't add this define. It is specified here in order
1211    # to export the symbol in case the client code decides to hide its symbols
1212    set_target_properties(${${prefix}_NAME}
1213      PROPERTIES
1214        DEFINE_SYMBOL "DLL_EXPORT_SYM=__attribute__((visibility(\"default\")))"
1215        LINK_FLAGS "${_link_flags}"
1216    )
1217
1218  endif()
1219
1220endfunction()
1221
1222
1223# (internal)
1224# Used to get the version of matlab, using caching. This basically transforms the
1225# output of the root list, with possible unknown version, to a version
1226# This can possibly run Matlab for extracting the version.
1227function(_Matlab_get_version_from_root matlab_root matlab_or_mcr matlab_known_version matlab_final_version)
1228
1229  # if the version is not trivial, we query matlab (if not MCR) for that
1230  # we keep track of the location of matlab that induced this version
1231  #if(NOT DEFINED Matlab_PROG_VERSION_STRING_AUTO_DETECT)
1232  #  set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version")
1233  #endif()
1234
1235  if(NOT matlab_known_version STREQUAL "NOTFOUND")
1236    # the version is known, we just return it
1237    set(${matlab_final_version} ${matlab_known_version} PARENT_SCOPE)
1238    set(Matlab_VERSION_STRING_INTERNAL ${matlab_known_version} CACHE INTERNAL "Matlab version (automatically determined)" FORCE)
1239    return()
1240  endif()
1241
1242  if(matlab_or_mcr STREQUAL "UNKNOWN")
1243    if(MATLAB_FIND_DEBUG)
1244      message(WARNING "[MATLAB] Determining Matlab or MCR")
1245    endif()
1246
1247    if(EXISTS "${matlab_root}/appdata/version.xml")
1248      # we inspect the application version.xml file that contains the product information
1249      file(STRINGS "${matlab_root}/appdata/version.xml" productinfo_string NEWLINE_CONSUME)
1250      string(REGEX MATCH "<installedProductData.*displayedString=\"([a-zA-Z ]+)\".*/>"
1251             product_reg_match
1252             ${productinfo_string}
1253            )
1254
1255      # default fallback to Matlab
1256      set(matlab_or_mcr "MATLAB")
1257      if(NOT CMAKE_MATCH_1 STREQUAL "")
1258        string(TOLOWER "${CMAKE_MATCH_1}" product_reg_match)
1259
1260        if(product_reg_match STREQUAL "matlab runtime")
1261          set(matlab_or_mcr "MCR")
1262        endif()
1263      endif()
1264    endif()
1265
1266    if(MATLAB_FIND_DEBUG)
1267      message(WARNING "[MATLAB] '${matlab_root}' contains the '${matlab_or_mcr}'")
1268    endif()
1269  endif()
1270
1271  # UNKNOWN is the default behavior in case we
1272  # - have an erroneous matlab_root
1273  # - have an initial 'UNKNOWN'
1274  if(matlab_or_mcr STREQUAL "MATLAB" OR matlab_or_mcr STREQUAL "UNKNOWN")
1275    # MATLAB versions
1276    set(_matlab_current_program ${Matlab_MAIN_PROGRAM})
1277
1278    # do we already have a matlab program?
1279    if(NOT _matlab_current_program)
1280
1281      set(_find_matlab_options)
1282      if(matlab_root AND EXISTS ${matlab_root})
1283        set(_find_matlab_options PATHS ${matlab_root} ${matlab_root}/bin NO_DEFAULT_PATH)
1284      endif()
1285
1286      find_program(
1287          _matlab_current_program
1288          matlab
1289          ${_find_matlab_options}
1290          DOC "Matlab main program"
1291        )
1292    endif()
1293
1294    if(NOT _matlab_current_program OR NOT EXISTS ${_matlab_current_program})
1295      # if not found, clear the dependent variables
1296      if(MATLAB_FIND_DEBUG)
1297        message(WARNING "[MATLAB] Cannot find the main matlab program under ${matlab_root}")
1298      endif()
1299      set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version" FORCE)
1300      set(Matlab_VERSION_STRING_INTERNAL "" CACHE INTERNAL "internal matlab location for the discovered version" FORCE)
1301      unset(_matlab_current_program)
1302      unset(_matlab_current_program CACHE)
1303      return()
1304    endif()
1305
1306    # full real path for path comparison
1307    get_filename_component(_matlab_main_real_path_tmp "${_matlab_current_program}" REALPATH)
1308    unset(_matlab_current_program)
1309    unset(_matlab_current_program CACHE)
1310
1311    # is it the same as the previous one?
1312    if(_matlab_main_real_path_tmp STREQUAL Matlab_PROG_VERSION_STRING_AUTO_DETECT)
1313      set(${matlab_final_version} ${Matlab_VERSION_STRING_INTERNAL} PARENT_SCOPE)
1314      return()
1315    endif()
1316
1317    # update the location of the program
1318    set(Matlab_PROG_VERSION_STRING_AUTO_DETECT
1319        ${_matlab_main_real_path_tmp}
1320        CACHE INTERNAL "internal matlab location for the discovered version" FORCE)
1321
1322    set(matlab_list_of_all_versions)
1323    matlab_get_version_from_matlab_run("${Matlab_PROG_VERSION_STRING_AUTO_DETECT}" matlab_list_of_all_versions)
1324
1325    list(LENGTH matlab_list_of_all_versions list_of_all_versions_length)
1326    if(list_of_all_versions_length GREATER 0)
1327      list(GET matlab_list_of_all_versions 0 _matlab_version_tmp)
1328    else()
1329      set(_matlab_version_tmp "unknown")
1330    endif()
1331
1332    # set the version into the cache
1333    set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)" FORCE)
1334
1335    # warning, just in case several versions found (should not happen)
1336    if((list_of_all_versions_length GREATER 1) AND MATLAB_FIND_DEBUG)
1337      message(WARNING "[MATLAB] Found several versions, taking the first one (versions found ${matlab_list_of_all_versions})")
1338    endif()
1339
1340    # return the updated value
1341    set(${matlab_final_version} ${Matlab_VERSION_STRING_INTERNAL} PARENT_SCOPE)
1342  elseif(EXISTS "${matlab_root}/VersionInfo.xml")
1343    # MCR
1344    # we cannot run anything in order to extract the version. We assume that the file
1345    # VersionInfo.xml exists under the MatlabRoot, we look for it and extract the version from there
1346    set(_matlab_version_tmp "unknown")
1347    file(STRINGS "${matlab_root}/VersionInfo.xml" versioninfo_string NEWLINE_CONSUME)
1348
1349    if(versioninfo_string)
1350      # parses "<version>9.2.0.538062</version>"
1351      string(REGEX MATCH "<version>(.*)</version>"
1352             version_reg_match
1353             ${versioninfo_string}
1354            )
1355
1356      if(CMAKE_MATCH_1 MATCHES "(([0-9])\\.([0-9]))[\\.0-9]*")
1357        set(_matlab_version_tmp "${CMAKE_MATCH_1}")
1358      endif()
1359    endif()
1360    set(${matlab_final_version} "${_matlab_version_tmp}" PARENT_SCOPE)
1361    set(Matlab_VERSION_STRING_INTERNAL
1362        "${_matlab_version_tmp}"
1363        CACHE INTERNAL "Matlab (MCR) version (automatically determined)"
1364        FORCE)
1365  endif() # Matlab or MCR
1366
1367endfunction()
1368
1369
1370# Utility function for finding Matlab or MCR on Win32
1371function(_Matlab_find_instances_win32 matlab_roots)
1372  # On WIN32, we look for Matlab installation in the registry
1373  # if unsuccessful, we look for all known revision and filter the existing
1374  # ones.
1375
1376  # testing if we are able to extract the needed information from the registry
1377  set(_matlab_versions_from_registry)
1378
1379  if(CMAKE_SIZEOF_VOID_P EQUAL 8)
1380    set(_matlab_win64 ON)
1381  else()
1382    set(_matlab_win64 OFF)
1383  endif()
1384
1385  matlab_extract_all_installed_versions_from_registry(_matlab_win64 _matlab_versions_from_registry)
1386
1387  # the returned list is empty, doing the search on all known versions
1388  if(NOT _matlab_versions_from_registry)
1389    if(MATLAB_FIND_DEBUG)
1390      message(STATUS "[MATLAB] Search for Matlab from the registry unsuccessful, testing all supported versions")
1391    endif()
1392    extract_matlab_versions_from_registry_brute_force(_matlab_versions_from_registry)
1393  endif()
1394
1395  # filtering the results with the registry keys
1396  matlab_get_all_valid_matlab_roots_from_registry("${_matlab_versions_from_registry}" _matlab_possible_roots)
1397  set(${matlab_roots} ${_matlab_possible_roots} PARENT_SCOPE)
1398
1399endfunction()
1400
1401# Utility function for finding Matlab or MCR on OSX
1402function(_Matlab_find_instances_osx matlab_roots)
1403
1404  set(_matlab_possible_roots)
1405  # on mac, we look for the /Application paths
1406  # this corresponds to the behavior on Windows. On Linux, we do not have
1407  # any other guess.
1408  matlab_get_supported_releases(_matlab_releases)
1409  if(MATLAB_FIND_DEBUG)
1410    message(STATUS "[MATLAB] Matlab supported versions ${_matlab_releases}. If more version should be supported "
1411                 "the variable MATLAB_ADDITIONAL_VERSIONS can be set according to the documentation")
1412  endif()
1413
1414  foreach(_matlab_current_release IN LISTS _matlab_releases)
1415    matlab_get_version_from_release_name("${_matlab_current_release}" _matlab_current_version)
1416    string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")
1417    set(_matlab_base_path "/Applications/MATLAB_${_matlab_current_release}.app")
1418
1419    # Check Matlab, has precedence over MCR
1420    if(EXISTS ${_matlab_base_path})
1421      if(MATLAB_FIND_DEBUG)
1422        message(STATUS "[MATLAB] Found version ${_matlab_current_release} (${_matlab_current_version}) in ${_matlab_base_path}")
1423      endif()
1424      list(APPEND _matlab_possible_roots "MATLAB" ${_matlab_current_version} ${_matlab_base_path})
1425    endif()
1426
1427    # Checks MCR
1428    set(_mcr_path "/Applications/MATLAB/MATLAB_Runtime/v${_matlab_current_version_without_dot}")
1429    if(EXISTS "${_mcr_path}")
1430      if(MATLAB_FIND_DEBUG)
1431        message(STATUS "[MATLAB] Found MCR version ${_matlab_current_release} (${_matlab_current_version}) in ${_mcr_path}")
1432      endif()
1433      list(APPEND _matlab_possible_roots "MCR" ${_matlab_current_version} ${_mcr_path})
1434    endif()
1435
1436  endforeach()
1437  set(${matlab_roots} ${_matlab_possible_roots} PARENT_SCOPE)
1438
1439endfunction()
1440
1441# Utility function for finding Matlab or MCR from the PATH
1442function(_Matlab_find_instances_from_path matlab_roots)
1443
1444  set(_matlab_possible_roots)
1445
1446  # At this point, we have no other choice than trying to find it from PATH.
1447  # If set by the user, this won't change.
1448  find_program(
1449    _matlab_main_tmp
1450    NAMES matlab)
1451
1452  if(_matlab_main_tmp)
1453    # we then populate the list of roots, with empty version
1454    if(MATLAB_FIND_DEBUG)
1455      message(STATUS "[MATLAB] matlab found from PATH: ${_matlab_main_tmp}")
1456    endif()
1457
1458    # resolve symlinks
1459    get_filename_component(_matlab_current_location "${_matlab_main_tmp}" REALPATH)
1460
1461    # get the directory (the command below has to be run twice)
1462    # this will be the matlab root
1463    get_filename_component(_matlab_current_location "${_matlab_current_location}" DIRECTORY)
1464    get_filename_component(_matlab_current_location "${_matlab_current_location}" DIRECTORY) # Matlab should be in bin
1465
1466    # We found the Matlab program
1467    list(APPEND _matlab_possible_roots "MATLAB" "NOTFOUND" ${_matlab_current_location})
1468
1469    # we remove this from the CACHE
1470    unset(_matlab_main_tmp CACHE)
1471  else()
1472    find_program(
1473      _matlab_mex_tmp
1474      NAMES mex)
1475    if(_matlab_mex_tmp)
1476      # we then populate the list of roots, with empty version
1477      if(MATLAB_FIND_DEBUG)
1478        message(STATUS "[MATLAB] mex compiler found from PATH: ${_matlab_mex_tmp}")
1479      endif()
1480
1481      # resolve symlinks
1482      get_filename_component(_mex_current_location "${_matlab_mex_tmp}" REALPATH)
1483
1484      # get the directory (the command below has to be run twice)
1485      # this will be the matlab root
1486      get_filename_component(_mex_current_location "${_mex_current_location}" DIRECTORY)
1487      get_filename_component(_mex_current_location "${_mex_current_location}" DIRECTORY) # Matlab Runtime mex compiler should be in bin
1488
1489      # We found the Matlab program
1490      list(APPEND _matlab_possible_roots "MCR" "NOTFOUND" ${_mex_current_location})
1491
1492      unset(_matlab_mex_tmp CACHE)
1493    else()
1494      if(MATLAB_FIND_DEBUG)
1495        message(STATUS "[MATLAB] mex compiler not found")
1496      endif()
1497    endif()
1498
1499
1500  endif()
1501
1502  set(${matlab_roots} ${_matlab_possible_roots} PARENT_SCOPE)
1503endfunction()
1504
1505
1506# ###################################
1507# Exploring the possible Matlab_ROOTS
1508
1509# this variable will get all Matlab installations found in the current system.
1510set(_matlab_possible_roots)
1511
1512if(Matlab_ROOT_DIR)
1513  # if the user specifies a possible root, we keep this one
1514
1515  if(NOT EXISTS "${Matlab_ROOT_DIR}")
1516    # if Matlab_ROOT_DIR specified but erroneous
1517    if(MATLAB_FIND_DEBUG)
1518      message(WARNING "[MATLAB] the specified path for Matlab_ROOT_DIR does not exist (${Matlab_ROOT_DIR})")
1519    endif()
1520  else()
1521    # NOTFOUND indicates the code below to search for the version automatically
1522    if("${Matlab_VERSION_STRING_INTERNAL}" STREQUAL "")
1523      list(APPEND _matlab_possible_roots "UNKNOWN" "NOTFOUND" ${Matlab_ROOT_DIR}) # empty version, empty MCR/Matlab indication
1524    else()
1525      list(APPEND _matlab_possible_roots "UNKNOWN" ${Matlab_VERSION_STRING_INTERNAL} ${Matlab_ROOT_DIR}) # cached version
1526    endif()
1527  endif()
1528else()
1529
1530  # if the user does not specify the possible installation root, we look for
1531  # one installation using the appropriate heuristics.
1532  # There is apparently no standard way on Linux.
1533  if(CMAKE_HOST_WIN32)
1534    _Matlab_find_instances_win32(_matlab_possible_roots_win32)
1535    list(APPEND _matlab_possible_roots ${_matlab_possible_roots_win32})
1536  elseif(APPLE)
1537    _Matlab_find_instances_osx(_matlab_possible_roots_osx)
1538    list(APPEND _matlab_possible_roots ${_matlab_possible_roots_osx})
1539  endif()
1540endif()
1541
1542
1543list(LENGTH _matlab_possible_roots _numbers_of_matlab_roots)
1544if(_numbers_of_matlab_roots EQUAL 0)
1545  # if we have not found anything, we fall back on the PATH
1546  _Matlab_find_instances_from_path(_matlab_possible_roots)
1547endif()
1548
1549
1550if(MATLAB_FIND_DEBUG)
1551  message(STATUS "[MATLAB] Matlab root folders are ${_matlab_possible_roots}")
1552endif()
1553
1554
1555
1556
1557
1558# take the first possible Matlab root
1559list(LENGTH _matlab_possible_roots _numbers_of_matlab_roots)
1560set(Matlab_VERSION_STRING "NOTFOUND")
1561set(Matlab_Or_MCR "UNKNOWN")
1562if(_numbers_of_matlab_roots GREATER 0)
1563  if(Matlab_FIND_VERSION_EXACT)
1564    list(FIND _matlab_possible_roots ${Matlab_FIND_VERSION} _list_index)
1565    if(_list_index LESS 0)
1566      set(_list_index 1)
1567    endif()
1568
1569    math(EXPR _matlab_or_mcr_index "${_list_index} - 1")
1570    math(EXPR _matlab_root_dir_index "${_list_index} + 1")
1571
1572    list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR)
1573    list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING)
1574    list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR)
1575  elseif(DEFINED Matlab_FIND_VERSION)
1576    foreach(_matlab_root_index RANGE 1 ${_numbers_of_matlab_roots} 3)
1577      list(GET _matlab_possible_roots ${_matlab_root_index} _matlab_root_version)
1578      if(_matlab_root_version VERSION_GREATER_EQUAL Matlab_FIND_VERSION)
1579        set(_list_index ${_matlab_root_index})
1580        break()
1581      endif()
1582    endforeach()
1583
1584    if(_list_index LESS 0)
1585      set(_list_index 1)
1586    endif()
1587
1588    math(EXPR _matlab_or_mcr_index "${_list_index} - 1")
1589    math(EXPR _matlab_root_dir_index "${_list_index} + 1")
1590    list(GET _matlab_possible_roots ${_matlab_or_mcr_index} Matlab_Or_MCR)
1591    list(GET _matlab_possible_roots ${_list_index} Matlab_VERSION_STRING)
1592    list(GET _matlab_possible_roots ${_matlab_root_dir_index} Matlab_ROOT_DIR)
1593    # adding a warning in case of ambiguity
1594    if(_numbers_of_matlab_roots GREATER 3 AND MATLAB_FIND_DEBUG)
1595      message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})."
1596                      " If this is not the desired behavior, use the EXACT keyword or provide the -DMatlab_ROOT_DIR=... on the command line")
1597    endif()
1598  else()
1599    list(GET _matlab_possible_roots 0 Matlab_Or_MCR)
1600    list(GET _matlab_possible_roots 1 Matlab_VERSION_STRING)
1601    list(GET _matlab_possible_roots 2 Matlab_ROOT_DIR)
1602
1603    # adding a warning in case of ambiguity
1604    if(_numbers_of_matlab_roots GREATER 3 AND MATLAB_FIND_DEBUG)
1605      message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})."
1606                      " If this is not the desired behavior, use the EXACT keyword or provide the -DMatlab_ROOT_DIR=... on the command line")
1607    endif()
1608  endif()
1609endif()
1610
1611
1612# check if the root changed wrt. the previous defined one, if so
1613# clear all the cached variables for being able to reconfigure properly
1614if(DEFINED Matlab_ROOT_DIR_LAST_CACHED)
1615
1616  if(NOT Matlab_ROOT_DIR_LAST_CACHED STREQUAL Matlab_ROOT_DIR)
1617    set(_Matlab_cached_vars
1618        Matlab_VERSION_STRING
1619        Matlab_INCLUDE_DIRS
1620        Matlab_MEX_LIBRARY
1621        Matlab_MEX_COMPILER
1622        Matlab_MCC_COMPILER
1623        Matlab_MAIN_PROGRAM
1624        Matlab_MX_LIBRARY
1625        Matlab_ENG_LIBRARY
1626        Matlab_MAT_LIBRARY
1627        Matlab_ENGINE_LIBRARY
1628        Matlab_DATAARRAY_LIBRARY
1629        Matlab_MEX_EXTENSION
1630        Matlab_SIMULINK_INCLUDE_DIR
1631
1632        # internal
1633        Matlab_MEXEXTENSIONS_PROG
1634        Matlab_ROOT_DIR_LAST_CACHED
1635        #Matlab_PROG_VERSION_STRING_AUTO_DETECT
1636        #Matlab_VERSION_STRING_INTERNAL
1637        )
1638    foreach(_var IN LISTS _Matlab_cached_vars)
1639      if(DEFINED ${_var})
1640        unset(${_var} CACHE)
1641      endif()
1642    endforeach()
1643  endif()
1644endif()
1645
1646set(Matlab_ROOT_DIR_LAST_CACHED ${Matlab_ROOT_DIR} CACHE INTERNAL "last Matlab root dir location")
1647set(Matlab_ROOT_DIR ${Matlab_ROOT_DIR} CACHE PATH "Matlab installation root path" FORCE)
1648
1649# Fix the version, in case this one is NOTFOUND
1650_Matlab_get_version_from_root(
1651  "${Matlab_ROOT_DIR}"
1652  "${Matlab_Or_MCR}"
1653  ${Matlab_VERSION_STRING}
1654  Matlab_VERSION_STRING
1655)
1656
1657if(MATLAB_FIND_DEBUG)
1658  message(STATUS "[MATLAB] Current version is ${Matlab_VERSION_STRING} located ${Matlab_ROOT_DIR}")
1659endif()
1660
1661# MATLAB 9.4 (R2018a) and newer have a new C++ API
1662# This API pulls additional required libraries.
1663if(NOT ${Matlab_VERSION_STRING} VERSION_LESS "9.4")
1664  set(Matlab_HAS_CPP_API 1)
1665endif()
1666
1667if(Matlab_ROOT_DIR)
1668  file(TO_CMAKE_PATH ${Matlab_ROOT_DIR} Matlab_ROOT_DIR)
1669endif()
1670
1671if(CMAKE_SIZEOF_VOID_P EQUAL 4)
1672  set(_matlab_64Build FALSE)
1673else()
1674  set(_matlab_64Build TRUE)
1675endif()
1676
1677if(APPLE)
1678  set(_matlab_bin_prefix "mac") # i should be for intel
1679  set(_matlab_bin_suffix_32bits "i")
1680  set(_matlab_bin_suffix_64bits "i64")
1681elseif(UNIX)
1682  set(_matlab_bin_prefix "gln")
1683  set(_matlab_bin_suffix_32bits "x86")
1684  set(_matlab_bin_suffix_64bits "xa64")
1685else()
1686  set(_matlab_bin_prefix "win")
1687  set(_matlab_bin_suffix_32bits "32")
1688  set(_matlab_bin_suffix_64bits "64")
1689endif()
1690
1691
1692
1693set(MATLAB_INCLUDE_DIR_TO_LOOK ${Matlab_ROOT_DIR}/extern/include)
1694if(_matlab_64Build)
1695  set(_matlab_current_suffix ${_matlab_bin_suffix_64bits})
1696else()
1697  set(_matlab_current_suffix ${_matlab_bin_suffix_32bits})
1698endif()
1699
1700set(Matlab_BINARIES_DIR
1701    ${Matlab_ROOT_DIR}/bin/${_matlab_bin_prefix}${_matlab_current_suffix})
1702set(Matlab_EXTERN_LIBRARY_DIR
1703    ${Matlab_ROOT_DIR}/extern/lib/${_matlab_bin_prefix}${_matlab_current_suffix})
1704set(Matlab_EXTERN_BINARIES_DIR
1705    ${Matlab_ROOT_DIR}/extern/bin/${_matlab_bin_prefix}${_matlab_current_suffix})
1706
1707if(WIN32)
1708  if(MINGW)
1709    set(_matlab_lib_dir_for_search ${Matlab_EXTERN_LIBRARY_DIR}/mingw64)
1710  else()
1711    set(_matlab_lib_dir_for_search ${Matlab_EXTERN_LIBRARY_DIR}/microsoft)
1712  endif()
1713  set(_matlab_lib_prefix_for_search "lib")
1714else()
1715  set(_matlab_lib_dir_for_search ${Matlab_BINARIES_DIR} ${Matlab_EXTERN_BINARIES_DIR})
1716  set(_matlab_lib_prefix_for_search "lib")
1717endif()
1718
1719unset(_matlab_64Build)
1720
1721
1722if(NOT DEFINED Matlab_MEX_EXTENSION)
1723  set(_matlab_mex_extension "")
1724  matlab_get_mex_suffix("${Matlab_ROOT_DIR}" _matlab_mex_extension)
1725
1726  # This variable goes to the cache.
1727  set(Matlab_MEX_EXTENSION ${_matlab_mex_extension} CACHE STRING "Extensions for the mex targets (automatically given by Matlab)")
1728  unset(_matlab_mex_extension)
1729endif()
1730
1731
1732if(MATLAB_FIND_DEBUG)
1733  message(STATUS "[MATLAB] [DEBUG]_matlab_lib_prefix_for_search = ${_matlab_lib_prefix_for_search} | _matlab_lib_dir_for_search = ${_matlab_lib_dir_for_search}")
1734endif()
1735
1736
1737
1738# internal
1739# This small stub around find_library is to prevent any pollution of CMAKE_FIND_LIBRARY_PREFIXES in the global scope.
1740# This is the function to be used below instead of the find_library directives.
1741function(_Matlab_find_library _matlab_library_prefix)
1742  set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} ${_matlab_library_prefix})
1743  find_library(${ARGN})
1744endfunction()
1745
1746
1747set(_matlab_required_variables)
1748
1749# Order is as follow:
1750# - unconditionally required libraries/headers first
1751# - then library components
1752# - then program components
1753
1754# the MEX library/header are required
1755find_path(
1756  Matlab_INCLUDE_DIRS
1757  mex.h
1758  PATHS ${MATLAB_INCLUDE_DIR_TO_LOOK}
1759  NO_DEFAULT_PATH
1760  )
1761list(APPEND _matlab_required_variables Matlab_INCLUDE_DIRS)
1762
1763if(Matlab_Or_MCR STREQUAL "MATLAB" OR Matlab_Or_MCR STREQUAL "UNKNOWN")
1764  _Matlab_find_library(
1765    ${_matlab_lib_prefix_for_search}
1766    Matlab_MEX_LIBRARY
1767    mex
1768    PATHS ${_matlab_lib_dir_for_search}
1769    NO_DEFAULT_PATH
1770  )
1771  list(APPEND _matlab_required_variables Matlab_MEX_LIBRARY)
1772
1773  # the MEX extension is required
1774  list(APPEND _matlab_required_variables Matlab_MEX_EXTENSION)
1775
1776  # the matlab root is required
1777  list(APPEND _matlab_required_variables Matlab_ROOT_DIR)
1778
1779  # The MX library is required
1780  _Matlab_find_library(
1781    ${_matlab_lib_prefix_for_search}
1782    Matlab_MX_LIBRARY
1783    mx
1784    PATHS ${_matlab_lib_dir_for_search}
1785    NO_DEFAULT_PATH
1786  )
1787  list(APPEND _matlab_required_variables Matlab_MX_LIBRARY)
1788  if(Matlab_MX_LIBRARY)
1789    set(Matlab_MX_LIBRARY_FOUND TRUE)
1790  endif()
1791endif()
1792
1793if(Matlab_HAS_CPP_API)
1794
1795  # The MatlabEngine library is required for R2018a+
1796  _Matlab_find_library(
1797    ${_matlab_lib_prefix_for_search}
1798    Matlab_ENGINE_LIBRARY
1799    MatlabEngine
1800    PATHS ${_matlab_lib_dir_for_search}
1801    DOC "MatlabEngine Library"
1802    NO_DEFAULT_PATH
1803  )
1804  if(Matlab_ENGINE_LIBRARY)
1805    set(Matlab_ENGINE_LIBRARY_FOUND TRUE)
1806  endif()
1807
1808  # The MatlabDataArray library is required for R2018a+
1809  _Matlab_find_library(
1810    ${_matlab_lib_prefix_for_search}
1811    Matlab_DATAARRAY_LIBRARY
1812    MatlabDataArray
1813    PATHS ${_matlab_lib_dir_for_search}
1814    DOC "MatlabDataArray Library"
1815    NO_DEFAULT_PATH
1816  )
1817  if(Matlab_DATAARRAY_LIBRARY)
1818    set(Matlab_DATAARRAY_LIBRARY_FOUND TRUE)
1819  endif()
1820
1821endif()
1822
1823# Component ENG library
1824if("ENG_LIBRARY" IN_LIST Matlab_FIND_COMPONENTS)
1825  _Matlab_find_library(
1826    ${_matlab_lib_prefix_for_search}
1827    Matlab_ENG_LIBRARY
1828    eng
1829    PATHS ${_matlab_lib_dir_for_search}
1830    NO_DEFAULT_PATH
1831  )
1832  if(Matlab_ENG_LIBRARY)
1833    set(Matlab_ENG_LIBRARY_FOUND TRUE)
1834  endif()
1835endif()
1836
1837# Component MAT library
1838if("MAT_LIBRARY" IN_LIST Matlab_FIND_COMPONENTS)
1839  _Matlab_find_library(
1840    ${_matlab_lib_prefix_for_search}
1841    Matlab_MAT_LIBRARY
1842    mat
1843    PATHS ${_matlab_lib_dir_for_search}
1844    NO_DEFAULT_PATH
1845  )
1846  if(Matlab_MAT_LIBRARY)
1847    set(Matlab_MAT_LIBRARY_FOUND TRUE)
1848  endif()
1849endif()
1850
1851# Component Simulink
1852if("SIMULINK" IN_LIST Matlab_FIND_COMPONENTS)
1853  find_path(
1854    Matlab_SIMULINK_INCLUDE_DIR
1855    simstruc.h
1856    PATHS "${Matlab_ROOT_DIR}/simulink/include"
1857    NO_DEFAULT_PATH
1858    )
1859  if(Matlab_SIMULINK_INCLUDE_DIR)
1860    set(Matlab_SIMULINK_FOUND TRUE)
1861    list(APPEND Matlab_INCLUDE_DIRS "${Matlab_SIMULINK_INCLUDE_DIR}")
1862  endif()
1863endif()
1864
1865# component Matlab program
1866if("MAIN_PROGRAM" IN_LIST Matlab_FIND_COMPONENTS)
1867  find_program(
1868    Matlab_MAIN_PROGRAM
1869    matlab
1870    PATHS ${Matlab_ROOT_DIR} ${Matlab_ROOT_DIR}/bin
1871    DOC "Matlab main program"
1872    NO_DEFAULT_PATH
1873  )
1874  if(Matlab_MAIN_PROGRAM)
1875    set(Matlab_MAIN_PROGRAM_FOUND TRUE)
1876  endif()
1877endif()
1878
1879# component Mex Compiler
1880if("MEX_COMPILER" IN_LIST Matlab_FIND_COMPONENTS)
1881  find_program(
1882    Matlab_MEX_COMPILER
1883    "mex"
1884    PATHS ${Matlab_BINARIES_DIR}
1885    DOC "Matlab MEX compiler"
1886    NO_DEFAULT_PATH
1887  )
1888  if(Matlab_MEX_COMPILER)
1889    set(Matlab_MEX_COMPILER_FOUND TRUE)
1890  endif()
1891endif()
1892
1893# component MCC Compiler
1894if("MCC_COMPILER" IN_LIST Matlab_FIND_COMPONENTS)
1895  find_program(
1896    Matlab_MCC_COMPILER
1897    "mcc"
1898    PATHS ${Matlab_BINARIES_DIR}
1899    DOC "Matlab MCC compiler"
1900    NO_DEFAULT_PATH
1901  )
1902  if(Matlab_MCC_COMPILER)
1903    set(Matlab_MCC_COMPILER_FOUND TRUE)
1904  endif()
1905endif()
1906
1907set(Matlab_LIBRARIES
1908  ${Matlab_MEX_LIBRARY} ${Matlab_MX_LIBRARY}
1909  ${Matlab_ENG_LIBRARY} ${Matlab_MAT_LIBRARY})
1910
1911if(Matlab_ENGINE_LIBRARY)
1912  list(APPEND Matlab_LIBRARIES ${Matlab_ENGINE_LIBRARY})
1913endif()
1914
1915if(Matlab_DATAARRAY_LIBRARY)
1916  list(APPEND Matlab_LIBRARIES ${Matlab_DATAARRAY_LIBRARY})
1917endif()
1918
1919# internal
1920# This small stub permits to add imported targets for the found MATLAB libraries
1921function(_Matlab_add_imported_target _matlab_library_variable_name _matlab_library_target_name)
1922  if(Matlab_${_matlab_library_variable_name}_LIBRARY)
1923    if(NOT TARGET Matlab::${_matlab_library_target_name})
1924      add_library(Matlab::${_matlab_library_target_name} UNKNOWN IMPORTED)
1925      set_target_properties(Matlab::${_matlab_library_target_name} PROPERTIES
1926        INTERFACE_INCLUDE_DIRECTORIES "${Matlab_INCLUDE_DIRS}"
1927        IMPORTED_LOCATION "${Matlab_${_matlab_library_variable_name}_LIBRARY}")
1928      if(_matlab_library_target_name STREQUAL "mex" OR
1929         _matlab_library_target_name STREQUAL "eng" OR
1930         _matlab_library_target_name STREQUAL "mat")
1931        set_target_properties(Matlab::${_matlab_library_target_name} PROPERTIES
1932          INTERFACE_LINK_LIBRARIES Matlab::mx)
1933      endif()
1934    endif()
1935  endif()
1936endfunction()
1937
1938_Matlab_add_imported_target(MX mx)
1939_Matlab_add_imported_target(MEX mex)
1940_Matlab_add_imported_target(ENG eng)
1941_Matlab_add_imported_target(MAT mat)
1942_Matlab_add_imported_target(ENGINE MatlabEngine)
1943_Matlab_add_imported_target(DATAARRAY MatlabDataArray)
1944
1945find_package_handle_standard_args(
1946  Matlab
1947  FOUND_VAR Matlab_FOUND
1948  REQUIRED_VARS ${_matlab_required_variables}
1949  VERSION_VAR Matlab_VERSION_STRING
1950  HANDLE_COMPONENTS)
1951
1952unset(_matlab_required_variables)
1953unset(_matlab_bin_prefix)
1954unset(_matlab_bin_suffix_32bits)
1955unset(_matlab_bin_suffix_64bits)
1956unset(_matlab_current_suffix)
1957unset(_matlab_lib_dir_for_search)
1958unset(_matlab_lib_prefix_for_search)
1959
1960if(Matlab_INCLUDE_DIRS AND Matlab_LIBRARIES)
1961  mark_as_advanced(
1962    Matlab_MEX_LIBRARY
1963    Matlab_MX_LIBRARY
1964    Matlab_ENG_LIBRARY
1965    Matlab_ENGINE_LIBRARY
1966    Matlab_DATAARRAY_LIBRARY
1967    Matlab_MAT_LIBRARY
1968    Matlab_INCLUDE_DIRS
1969    Matlab_FOUND
1970    Matlab_MAIN_PROGRAM
1971    Matlab_MEXEXTENSIONS_PROG
1972    Matlab_MEX_EXTENSION
1973  )
1974endif()
1975
1976cmake_policy(POP)
1977