FindMatlab.cmake 63.5 KB
Newer Older
1
2
3
# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.

4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#[=======================================================================[.rst:
FindMatlab
----------

Finds Matlab or Matlab Compiler Runtime (MCR) and provides Matlab tools,
libraries and compilers to CMake.

This package primary purpose is to find the libraries associated with Matlab
or the MCR in order to be able to build Matlab extensions (mex files). It
can also be used:

* to run specific commands in Matlab in case Matlab is available
* for declaring Matlab unit test
* to retrieve various information from Matlab (mex extensions, versions and
  release queries, ...)

20
21
22
.. versionadded:: 3.12
  Added Matlab Compiler Runtime (MCR) support.

23
24
The module supports the following components:

25
26
* ``ENG_LIBRARY`` and ``MAT_LIBRARY``: respectively the ``ENG`` and ``MAT``
  libraries of Matlab
27
28
29
30
31
32
33
* ``MAIN_PROGRAM`` the Matlab binary program. Note that this component is not
  available on the MCR version, and will yield an error if the MCR is found
  instead of the regular Matlab installation.
* ``MEX_COMPILER`` the MEX compiler.
* ``MCC_COMPILER`` the MCC compiler, included with the Matlab Compiler add-on.
* ``SIMULINK`` the Simulink environment.

34
35
36
37
38
39
40
41
42
43
44
.. versionadded:: 3.7
  Added the ``MAT_LIBRARY`` component.

.. versionadded:: 3.13
  Added the ``ENGINE_LIBRARY``, ``DATAARRAY_LIBRARY`` and ``MCC_COMPILER``
  components.

.. versionchanged:: 3.14
  Removed the ``MX_LIBRARY``, ``ENGINE_LIBRARY`` and ``DATAARRAY_LIBRARY``
  components.  These libraries are found unconditionally.

45
46
47
48
49
50
51
52
53
54
.. note::

  The version given to the :command:`find_package` directive is the Matlab
  **version**, which should not be confused with the Matlab *release* name
  (eg. `R2014`).
  The :command:`matlab_get_version_from_release_name` and
  :command:`matlab_get_release_name_from_version` provide a mapping
  between the release name and the version.

The variable :variable:`Matlab_ROOT_DIR` may be specified in order to give
55
the path of the desired Matlab version. Otherwise, the behavior is platform
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
specific:

* Windows: The installed versions of Matlab/MCR are retrieved from the
  Windows registry
* OS X: The installed versions of Matlab/MCR are given by the MATLAB
  default installation paths in ``/Application``. If no such application is
  found, it falls back to the one that might be accessible from the ``PATH``.
* Unix: The desired Matlab should be accessible from the ``PATH``. This does
  not work for MCR installation and :variable:`Matlab_ROOT_DIR` should be
  specified on this platform.

Additional information is provided when :variable:`MATLAB_FIND_DEBUG` is set.
When a Matlab/MCR installation is found automatically and the ``MATLAB_VERSION``
is not given, the version is queried from Matlab directly (on Windows this
may pop up a Matlab window) or from the MCR installation.

The mapping of the release names and the version of Matlab is performed by
defining pairs (name, version).  The variable
:variable:`MATLAB_ADDITIONAL_VERSIONS` may be provided before the call to
the :command:`find_package` in order to handle additional versions.

A Matlab scripts can be added to the set of tests using the
:command:`matlab_add_unit_test`. By default, the Matlab unit test framework
will be used (>= 2013a) to run this script, but regular ``.m`` files
returning an exit code can be used as well (0 indicating a success).

Module Input Variables
^^^^^^^^^^^^^^^^^^^^^^

Users or projects may set the following variables to configure the module
86
behavior:
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

:variable:`Matlab_ROOT_DIR`
  the root of the Matlab installation.
:variable:`MATLAB_FIND_DEBUG`
  outputs debug information
:variable:`MATLAB_ADDITIONAL_VERSIONS`
  additional versions of Matlab for the automatic retrieval of the installed
  versions.

Variables defined by the module
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Result variables
""""""""""""""""

``Matlab_FOUND``
  ``TRUE`` if the Matlab installation is found, ``FALSE``
  otherwise. All variable below are defined if Matlab is found.
``Matlab_ROOT_DIR``
  the final root of the Matlab installation determined by the FindMatlab
  module.
``Matlab_MAIN_PROGRAM``
  the Matlab binary program. Available only if the component ``MAIN_PROGRAM``
  is given in the :command:`find_package` directive.
``Matlab_INCLUDE_DIRS``
 the path of the Matlab libraries headers
``Matlab_MEX_LIBRARY``
  library for mex, always available.
``Matlab_MX_LIBRARY``
116
  mx library of Matlab (arrays), always available.
117
118
119
120
121
122
123
``Matlab_ENG_LIBRARY``
  Matlab engine library. Available only if the component ``ENG_LIBRARY``
  is requested.
``Matlab_MAT_LIBRARY``
  Matlab matrix library. Available only if the component ``MAT_LIBRARY``
  is requested.
``Matlab_ENGINE_LIBRARY``
124
125
  .. versionadded:: 3.13

126
  Matlab C++ engine library, always available for R2018a and newer.
127
``Matlab_DATAARRAY_LIBRARY``
128
129
  .. versionadded:: 3.13

130
  Matlab C++ data array library, always available for R2018a and newer.
131
132
133
134
135
136
``Matlab_LIBRARIES``
  the whole set of libraries of Matlab
``Matlab_MEX_COMPILER``
  the mex compiler of Matlab. Currently not used.
  Available only if the component ``MEX_COMPILER`` is requested.
``Matlab_MCC_COMPILER``
137
138
  .. versionadded:: 3.13

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  the mcc compiler of Matlab. Included with the Matlab Compiler add-on.
  Available only if the component ``MCC_COMPILER`` is requested.

Cached variables
""""""""""""""""

``Matlab_MEX_EXTENSION``
  the extension of the mex files for the current platform (given by Matlab).
``Matlab_ROOT_DIR``
  the location of the root of the Matlab installation found. If this value
  is changed by the user, the result variables are recomputed.

Provided macros
^^^^^^^^^^^^^^^

:command:`matlab_get_version_from_release_name`
  returns the version from the release name
:command:`matlab_get_release_name_from_version`
  returns the release name from the Matlab version

Provided functions
^^^^^^^^^^^^^^^^^^

:command:`matlab_add_mex`
  adds a target compiling a MEX file.
:command:`matlab_add_unit_test`
  adds a Matlab unit test file as a test to the project.
:command:`matlab_extract_all_installed_versions_from_registry`
  parses the registry for all Matlab versions. Available on Windows only.
  The part of the registry parsed is dependent on the host processor
:command:`matlab_get_all_valid_matlab_roots_from_registry`
  returns all the possible Matlab or MCR paths, according to a previously
  given list. Only the existing/accessible paths are kept. This is mainly
  useful for the searching all possible Matlab installation.
:command:`matlab_get_mex_suffix`
  returns the suffix to be used for the mex files
  (platform/architecture dependent)
:command:`matlab_get_version_from_matlab_run`
  returns the version of Matlab/MCR, given the full directory of the Matlab/MCR
  installation path.


Known issues
^^^^^^^^^^^^

**Symbol clash in a MEX target**
  By default, every symbols inside a MEX
  file defined with the command :command:`matlab_add_mex` have hidden
187
  visibility, except for the entry point. This is the default behavior of
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
  the MEX compiler, which lowers the risk of symbol collision between the
  libraries shipped with Matlab, and the libraries to which the MEX file is
  linking to. This is also the default on Windows platforms.

  However, this is not sufficient in certain case, where for instance your
  MEX file is linking against libraries that are already loaded by Matlab,
  even if those libraries have different SONAMES.
  A possible solution is to hide the symbols of the libraries to which the
  MEX target is linking to. This can be achieved in GNU GCC compilers with
  the linker option ``-Wl,--exclude-libs,ALL``.

**Tests using GPU resources**
  in case your MEX file is using the GPU and
  in order to be able to run unit tests on this MEX file, the GPU resources
  should be properly released by Matlab. A possible solution is to make
  Matlab aware of the use of the GPU resources in the session, which can be
  performed by a command such as ``D = gpuDevice()`` at the beginning of
  the test script (or via a fixture).


Reference
^^^^^^^^^

.. variable:: Matlab_ROOT_DIR

   The root folder of the Matlab installation. If set before the call to
   :command:`find_package`, the module will look for the components in that
   path. If not set, then an automatic search of Matlab
   will be performed. If set, it should point to a valid version of Matlab.

.. variable:: MATLAB_FIND_DEBUG

   If set, the lookup of Matlab and the intermediate configuration steps are
   outputted to the console.

.. variable:: MATLAB_ADDITIONAL_VERSIONS

  If set, specifies additional versions of Matlab that may be looked for.
226
  The variable should be a list of strings, organized by pairs of release
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
  name and versions, such as follows::

    set(MATLAB_ADDITIONAL_VERSIONS
        "release_name1=corresponding_version1"
        "release_name2=corresponding_version2"
        ...
        )

  Example::

    set(MATLAB_ADDITIONAL_VERSIONS
        "R2013b=8.2"
        "R2013a=8.1"
        "R2012b=8.0")

  The order of entries in this list matters when several versions of
  Matlab are installed. The priority is set according to the ordering in
  this list.
#]=======================================================================]
Bill Hoffman's avatar
Bill Hoffman committed
246

247
248
249
cmake_policy(PUSH)
cmake_policy(SET CMP0057 NEW) # if IN_LIST

250
251
set(_FindMatlab_SELF_DIR "${CMAKE_CURRENT_LIST_DIR}")

252
include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
253
include(CheckCXXCompilerFlag)
254
include(CheckCCompilerFlag)
255
256
257
258
259
260
261
262
263


# The currently supported versions. Other version can be added by the user by
# providing MATLAB_ADDITIONAL_VERSIONS
if(NOT MATLAB_ADDITIONAL_VERSIONS)
  set(MATLAB_ADDITIONAL_VERSIONS)
endif()

set(MATLAB_VERSIONS_MAPPING
264
  "R2021a=9.10"
Michael Hirsch's avatar
Michael Hirsch committed
265
  "R2020b=9.9"
266
  "R2020a=9.8"
267
268
  "R2019b=9.7"
  "R2019a=9.6"
269
270
  "R2018b=9.5"
  "R2018a=9.4"
271
  "R2017b=9.3"
272
  "R2017a=9.2"
273
  "R2016b=9.1"
274
  "R2016a=9.0"
275
  "R2015b=8.6"
276
277
  "R2015a=8.5"
  "R2014b=8.4"
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  "R2014a=8.3"
  "R2013b=8.2"
  "R2013a=8.1"
  "R2012b=8.0"
  "R2012a=7.14"
  "R2011b=7.13"
  "R2011a=7.12"
  "R2010b=7.11"

  ${MATLAB_ADDITIONAL_VERSIONS}
  )


# temporary folder for all Matlab runs
set(_matlab_temporary_folder ${CMAKE_BINARY_DIR}/Matlab)

if(NOT EXISTS "${_matlab_temporary_folder}")
  file(MAKE_DIRECTORY "${_matlab_temporary_folder}")
endif()

298
299
300
301
302
#[=======================================================================[.rst:
.. command:: matlab_get_version_from_release_name

  Returns the version of Matlab (17.58) from a release name (R2017k)
#]=======================================================================]
303
macro(matlab_get_version_from_release_name release_name version_name)
304
305
306
307
308
309

  string(REGEX MATCHALL "${release_name}=([0-9]+\\.?[0-9]*)" _matched ${MATLAB_VERSIONS_MAPPING})

  set(${version_name} "")
  if(NOT _matched STREQUAL "")
    set(${version_name} ${CMAKE_MATCH_1})
310
  else()
311
    message(WARNING "[MATLAB] The release name ${release_name} is not registered")
312
313
314
315
316
317
318
319
320
  endif()
  unset(_matched)

endmacro()





321
322
323
324
325
#[=======================================================================[.rst:
.. command:: matlab_get_release_name_from_version

  Returns the release name (R2017k) from the version of Matlab (17.58)
#]=======================================================================]
326
macro(matlab_get_release_name_from_version version release_name)
327
328
329
330
331
332
333
334
335
336
337
338
339

  set(${release_name} "")
  foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING)
    string(REGEX MATCHALL "(.+)=${version}" _matched ${_var})
    if(NOT _matched STREQUAL "")
      set(${release_name} ${CMAKE_MATCH_1})
      break()
    endif()
  endforeach(_var)

  unset(_var)
  unset(_matched)
  if(${release_name} STREQUAL "")
340
    message(WARNING "[MATLAB] The version ${version} is not registered")
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
  endif()

endmacro()





# extracts all the supported release names (R2017k...) of Matlab
# internal use
macro(matlab_get_supported_releases list_releases)
  set(${list_releases})
  foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING)
    string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var})
    if(NOT _matched STREQUAL "")
      list(APPEND ${list_releases} ${CMAKE_MATCH_1})
    endif()
    unset(_matched)
    unset(CMAKE_MATCH_1)
  endforeach(_var)
  unset(_var)
endmacro()



# extracts all the supported versions of Matlab
# internal use
macro(matlab_get_supported_versions list_versions)
  set(${list_versions})
  foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING)
    string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var})
    if(NOT _matched STREQUAL "")
      list(APPEND ${list_versions} ${CMAKE_MATCH_2})
    endif()
    unset(_matched)
    unset(CMAKE_MATCH_1)
  endforeach(_var)
  unset(_var)
endmacro()


382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#[=======================================================================[.rst:
.. command:: matlab_extract_all_installed_versions_from_registry

  This function parses the registry and founds the Matlab versions that are
  installed. The found versions are returned in `matlab_versions`.
  Set `win64` to `TRUE` if the 64 bit version of Matlab should be looked for
  The returned list contains all versions under
  ``HKLM\\SOFTWARE\\Mathworks\\MATLAB`` and
  ``HKLM\\SOFTWARE\\Mathworks\\MATLAB Runtime`` or an empty list in case an
  error occurred (or nothing found).

  .. note::

    Only the versions are provided. No check is made over the existence of the
    installation referenced in the registry,

#]=======================================================================]
399
400
401
function(matlab_extract_all_installed_versions_from_registry win64 matlab_versions)

  if(NOT CMAKE_HOST_WIN32)
402
    message(FATAL_ERROR "[MATLAB] This macro can only be called by a windows host (call to reg.exe)")
403
404
  endif()

405
  if(${win64} AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "64")
406
407
408
409
410
    set(APPEND_REG "/reg:64")
  else()
    set(APPEND_REG "/reg:32")
  endif()

411
  set(matlabs_from_registry)
412

Nikolaj Fogh's avatar
Nikolaj Fogh committed
413
  foreach(_installation_type IN ITEMS "MATLAB" "MATLAB Runtime" "MATLAB Compiler Runtime")
414

415
416
417
    # /reg:64 should be added on 64 bits capable OSs in order to enable the
    # redirection of 64 bits applications
    execute_process(
Nikolaj Fogh's avatar
Nikolaj Fogh committed
418
      COMMAND reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Mathworks\\${_installation_type}" /f * /k ${APPEND_REG}
419
420
421
422
423
      RESULT_VARIABLE resultMatlab
      OUTPUT_VARIABLE varMatlab
      ERROR_VARIABLE errMatlab
      INPUT_FILE NUL
      )
424
425


426
    if(resultMatlab EQUAL 0)
427

428
      string(
Nikolaj Fogh's avatar
Nikolaj Fogh committed
429
        REGEX MATCHALL "${_installation_type}\\\\([0-9]+(\\.[0-9]+)?)"
430
431
432
433
        matlab_versions_regex ${varMatlab})

      foreach(match IN LISTS matlab_versions_regex)
        string(
Nikolaj Fogh's avatar
Nikolaj Fogh committed
434
          REGEX MATCH "${_installation_type}\\\\(([0-9]+)(\\.([0-9]+))?)"
435
436
437
438
439
440
441
442
          current_match ${match})

        set(_matlab_current_version ${CMAKE_MATCH_1})
        set(current_matlab_version_major ${CMAKE_MATCH_2})
        set(current_matlab_version_minor ${CMAKE_MATCH_4})
        if(NOT current_matlab_version_minor)
          set(current_matlab_version_minor "0")
        endif()
443

444
445
446
        list(APPEND matlabs_from_registry ${_matlab_current_version})
        unset(_matlab_current_version)
      endforeach()
447

448
449
    endif()
  endforeach()
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506

  if(matlabs_from_registry)
    list(REMOVE_DUPLICATES matlabs_from_registry)
    list(SORT matlabs_from_registry)
    list(REVERSE matlabs_from_registry)
  endif()

  set(${matlab_versions} ${matlabs_from_registry} PARENT_SCOPE)

endfunction()



# (internal)
macro(extract_matlab_versions_from_registry_brute_force matlab_versions)
  # get the supported versions
  set(matlab_supported_versions)
  matlab_get_supported_versions(matlab_supported_versions)


  # this is a manual population of the versions we want to look for
  # this can be done as is, but preferably with the call to
  # matlab_get_supported_versions and variable

  # populating the versions we want to look for
  # set(matlab_supported_versions)

  # # Matlab 7
  # set(matlab_major 7)
  # foreach(current_matlab_minor RANGE 4 20)
    # list(APPEND matlab_supported_versions "${matlab_major}.${current_matlab_minor}")
  # endforeach(current_matlab_minor)

  # # Matlab 8
  # set(matlab_major 8)
  # foreach(current_matlab_minor RANGE 0 5)
    # list(APPEND matlab_supported_versions "${matlab_major}.${current_matlab_minor}")
  # endforeach(current_matlab_minor)

  # # taking into account the possible additional versions provided by the user
  # if(DEFINED MATLAB_ADDITIONAL_VERSIONS)
    # list(APPEND matlab_supported_versions MATLAB_ADDITIONAL_VERSIONS)
  # endif()

  # we order from more recent to older
  if(matlab_supported_versions)
    list(REMOVE_DUPLICATES matlab_supported_versions)
    list(SORT matlab_supported_versions)
    list(REVERSE matlab_supported_versions)
  endif()

  set(${matlab_versions} ${matlab_supported_versions})
endmacro()




507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
#[=======================================================================[.rst:
.. command:: matlab_get_all_valid_matlab_roots_from_registry

  Populates the Matlab root with valid versions of Matlab or
  Matlab Runtime (MCR).
  The returned matlab_roots is organized in triplets
  ``(type,version_number,matlab_root_path)``, where ``type``
  indicates either ``MATLAB`` or ``MCR``.

  ::

    matlab_get_all_valid_matlab_roots_from_registry(
        matlab_versions
        matlab_roots)

  ``matlab_versions``
    the versions of each of the Matlab or MCR installations
  ``matlab_roots``
    the location of each of the Matlab or MCR installations
#]=======================================================================]
527
528
529
530
531
532
533
function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_roots)

  # The matlab_versions comes either from
  # extract_matlab_versions_from_registry_brute_force or
  # matlab_extract_all_installed_versions_from_registry.

  set(_matlab_roots_list )
534
  # check for Matlab installations
535
536
537
538
539
540
  foreach(_matlab_current_version ${matlab_versions})
    get_filename_component(
      current_MATLAB_ROOT
      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${_matlab_current_version};MATLABROOT]"
      ABSOLUTE)

Nikolaj Fogh's avatar
Nikolaj Fogh committed
541
    if(EXISTS "${current_MATLAB_ROOT}")
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
      list(APPEND _matlab_roots_list "MATLAB" ${_matlab_current_version} ${current_MATLAB_ROOT})
    endif()

  endforeach()

  # Check for MCR installations
  foreach(_matlab_current_version ${matlab_versions})
    get_filename_component(
      current_MATLAB_ROOT
      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB Runtime\\${_matlab_current_version};MATLABROOT]"
      ABSOLUTE)

    # remove the dot
    string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")

Nikolaj Fogh's avatar
Nikolaj Fogh committed
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
    if(EXISTS "${current_MATLAB_ROOT}")
      list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
    endif()

  endforeach()

  # Check for old MCR installations
  foreach(_matlab_current_version ${matlab_versions})
    get_filename_component(
      current_MATLAB_ROOT
      "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB Compiler Runtime\\${_matlab_current_version};MATLABROOT]"
      ABSOLUTE)

    # remove the dot
    string(REPLACE "." "" _matlab_current_version_without_dot "${_matlab_current_version}")

    if(EXISTS "${current_MATLAB_ROOT}")
574
      list(APPEND _matlab_roots_list "MCR" ${_matlab_current_version} "${current_MATLAB_ROOT}/v${_matlab_current_version_without_dot}")
575
576
    endif()

577
  endforeach()
578
579
580
  set(${matlab_roots} ${_matlab_roots_list} PARENT_SCOPE)
endfunction()

581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
#[=======================================================================[.rst:
.. command:: matlab_get_mex_suffix

  Returns the extension of the mex files (the suffixes).
  This function should not be called before the appropriate Matlab root has
  been found.

  ::

    matlab_get_mex_suffix(
        matlab_root
        mex_suffix)

  ``matlab_root``
    the root of the Matlab/MCR installation
  ``mex_suffix``
    the variable name in which the suffix will be returned.
#]=======================================================================]
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
function(matlab_get_mex_suffix matlab_root mex_suffix)

  # todo setup the extension properly. Currently I do not know if this is
  # sufficient for all win32 distributions.
  # there is also CMAKE_EXECUTABLE_SUFFIX that could be tweaked
  set(mexext_suffix "")
  if(WIN32)
    list(APPEND mexext_suffix ".bat")
  endif()

  # we first try without suffix, since cmake does not understand a list with
  # one empty string element
  find_program(
    Matlab_MEXEXTENSIONS_PROG
    NAMES mexext
    PATHS ${matlab_root}/bin
    DOC "Matlab MEX extension provider"
    NO_DEFAULT_PATH
  )

  foreach(current_mexext_suffix IN LISTS mexext_suffix)
    if(NOT DEFINED Matlab_MEXEXTENSIONS_PROG OR NOT Matlab_MEXEXTENSIONS_PROG)
      # this call should populate the cache automatically
      find_program(
        Matlab_MEXEXTENSIONS_PROG
        "mexext${current_mexext_suffix}"
        PATHS ${matlab_root}/bin
        DOC "Matlab MEX extension provider"
        NO_DEFAULT_PATH
      )
    endif()
  endforeach(current_mexext_suffix)
631
632
633
  if(MATLAB_FIND_DEBUG)
    message(STATUS "[MATLAB] Determining mex files extensions from '${matlab_root}/bin' with program '${Matlab_MEXEXTENSIONS_PROG}'")
  endif()
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652

  # the program has been found?
  if((NOT Matlab_MEXEXTENSIONS_PROG) OR (NOT EXISTS ${Matlab_MEXEXTENSIONS_PROG}))
    if(MATLAB_FIND_DEBUG)
      message(WARNING "[MATLAB] Cannot found mexext program. Matlab root is ${matlab_root}")
    endif()
    unset(Matlab_MEXEXTENSIONS_PROG CACHE)
    return()
  endif()

  set(_matlab_mex_extension)

  set(devnull)
  if(UNIX)
    set(devnull INPUT_FILE /dev/null)
  elseif(WIN32)
    set(devnull INPUT_FILE NUL)
  endif()

653
654
655
656
657
658
659
660
661
  if(WIN32)
    # this environment variable is used to determine the arch on Windows
    if(CMAKE_SIZEOF_VOID_P EQUAL 8)
      set(ENV{MATLAB_ARCH} "win64")
    else()
      set(ENV{MATLAB_ARCH} "win32")
    endif()
  endif()

luz.paz's avatar
luz.paz committed
662
  # this is the preferred way. If this does not work properly (eg. MCR on Windows), then we use our own knowledge
663
664
665
666
  execute_process(
    COMMAND ${Matlab_MEXEXTENSIONS_PROG}
    OUTPUT_VARIABLE _matlab_mex_extension
    ERROR_VARIABLE _matlab_mex_extension_error
667
    OUTPUT_STRIP_TRAILING_WHITESPACE
668
    ${devnull})
669
  unset(ENV{MATLAB_ARCH})
670

671
  if(_matlab_mex_extension_error)
672
673
674
675
676
677
678
679
680
681
682
683
    if(WIN32)
      # this is only for intel architecture
      if(CMAKE_SIZEOF_VOID_P EQUAL 8)
        set(_matlab_mex_extension "mexw64")
      else()
        set(_matlab_mex_extension "mexw32")
      endif()
    endif()
  endif()

  string(STRIP "${_matlab_mex_extension}"  _matlab_mex_extension)
  if(MATLAB_FIND_DEBUG)
684
    message(STATUS "[MATLAB] '${Matlab_MEXEXTENSIONS_PROG}' : determined extension '${_matlab_mex_extension}' and error string is '${_matlab_mex_extension_error}'")
685
  endif()
686
687
688
689
690
691
692
693

  unset(Matlab_MEXEXTENSIONS_PROG CACHE)
  set(${mex_suffix} ${_matlab_mex_extension} PARENT_SCOPE)
endfunction()




694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
#[=======================================================================[.rst:
.. command:: matlab_get_version_from_matlab_run

  This function runs Matlab program specified on arguments and extracts its
  version. If the path provided for the Matlab installation points to an MCR
  installation, the version is extracted from the installed files.

  ::

    matlab_get_version_from_matlab_run(
        matlab_binary_path
        matlab_list_versions)

  ``matlab_binary_path``
    the location of the `matlab` binary executable
  ``matlab_list_versions``
    the version extracted from Matlab
#]=======================================================================]
712
713
714
715
716
717
718
719
720
721
722
function(matlab_get_version_from_matlab_run matlab_binary_program matlab_list_versions)

  set(${matlab_list_versions} "" PARENT_SCOPE)

  if(MATLAB_FIND_DEBUG)
    message(STATUS "[MATLAB] Determining the version of Matlab from ${matlab_binary_program}")
  endif()

  if(EXISTS "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp")
    if(MATLAB_FIND_DEBUG)
      message(STATUS "[MATLAB] Removing previous ${_matlab_temporary_folder}/matlabVersionLog.cmaketmp file")
723
    endif()
724
725
726
727
728
729
730
731
732
733
    file(REMOVE "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp")
  endif()


  # the log file is needed since on windows the command executes in a new
  # window and it is not possible to get back the answer of Matlab
  # the -wait command is needed on windows, otherwise the call returns
  # immediately after the program launches itself.
  if(WIN32)
    set(_matlab_additional_commands "-wait")
734
  endif()
735
736
737
738
739
740
741
742

  set(devnull)
  if(UNIX)
    set(devnull INPUT_FILE /dev/null)
  elseif(WIN32)
    set(devnull INPUT_FILE NUL)
  endif()

743
  # timeout set to 120 seconds, in case it does not start
744
745
746
747
748
749
750
751
  # note as said before OUTPUT_VARIABLE cannot be used in a platform
  # independent manner however, not setting it would flush the output of Matlab
  # in the current console (unix variant)
  execute_process(
    COMMAND "${matlab_binary_program}" -nosplash -nojvm ${_matlab_additional_commands} -logfile "matlabVersionLog.cmaketmp" -nodesktop -nodisplay -r "version, exit"
    OUTPUT_VARIABLE _matlab_version_from_cmd_dummy
    RESULT_VARIABLE _matlab_result_version_call
    ERROR_VARIABLE _matlab_result_version_call_error
752
    TIMEOUT 120
753
754
    WORKING_DIRECTORY "${_matlab_temporary_folder}"
    ${devnull}
Bill Hoffman's avatar
Bill Hoffman committed
755
    )
756

757
  if(_matlab_result_version_call MATCHES "timeout")
758
759
760
761
762
763
    if(MATLAB_FIND_DEBUG)
      message(WARNING "[MATLAB] Unable to determine the version of Matlab."
        " Matlab call timed out after 120 seconds.")
    endif()
    return()
  endif()
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781

  if(${_matlab_result_version_call})
    if(MATLAB_FIND_DEBUG)
      message(WARNING "[MATLAB] Unable to determine the version of Matlab. Matlab call returned with error ${_matlab_result_version_call}.")
    endif()
    return()
  elseif(NOT EXISTS "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp")
    if(MATLAB_FIND_DEBUG)
      message(WARNING "[MATLAB] Unable to determine the version of Matlab. The log file does not exist.")
    endif()
    return()
  endif()

  # if successful, read back the log
  file(READ "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp" _matlab_version_from_cmd)
  file(REMOVE "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp")

  set(index -1)
782
  string(FIND "${_matlab_version_from_cmd}" "ans" index)
783
784
785
786
787
788
789
790
791
  if(index EQUAL -1)

    if(MATLAB_FIND_DEBUG)
      message(WARNING "[MATLAB] Cannot find the version of Matlab returned by the run.")
    endif()

  else()
    set(matlab_list_of_all_versions_tmp)

792
    string(SUBSTRING "${_matlab_version_from_cmd}" ${index} -1 substring_ans)
793
    string(
794
      REGEX MATCHALL "ans[\r\n\t ]*=[\r\n\t ]*'?([0-9]+(\\.[0-9]+)?)"
795
796
797
798
      matlab_versions_regex
      ${substring_ans})
    foreach(match IN LISTS matlab_versions_regex)
      string(
799
        REGEX MATCH "ans[\r\n\t ]*=[\r\n\t ]*'?(([0-9]+)(\\.([0-9]+))?)"
800
801
802
803
804
805
806
807
808
809
810
811
812
        current_match ${match})

      list(APPEND matlab_list_of_all_versions_tmp ${CMAKE_MATCH_1})
    endforeach()
    if(matlab_list_of_all_versions_tmp)
      list(REMOVE_DUPLICATES matlab_list_of_all_versions_tmp)
    endif()
    set(${matlab_list_versions} ${matlab_list_of_all_versions_tmp} PARENT_SCOPE)

  endif()

endfunction()

813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
#[=======================================================================[.rst:
.. command:: matlab_add_unit_test

  Adds a Matlab unit test to the test set of cmake/ctest.
  This command requires the component ``MAIN_PROGRAM`` and hence is not
  available for an MCR installation.

  The unit test uses the Matlab unittest framework (default, available
  starting Matlab 2013b+) except if the option ``NO_UNITTEST_FRAMEWORK``
  is given.

  The function expects one Matlab test script file to be given.
  In the case ``NO_UNITTEST_FRAMEWORK`` is given, the unittest script file
  should contain the script to be run, plus an exit command with the exit
  value. This exit value will be passed to the ctest framework (0 success,
  non 0 failure). Additional arguments accepted by :command:`add_test` can be
  passed through ``TEST_ARGS`` (eg. ``CONFIGURATION <config> ...``).

  ::

    matlab_add_unit_test(
        NAME <name>
        UNITTEST_FILE matlab_file_containing_unittest.m
        [CUSTOM_TEST_COMMAND matlab_command_to_run_as_test]
        [UNITTEST_PRECOMMAND matlab_command_to_run]
        [TIMEOUT timeout]
        [ADDITIONAL_PATH path1 [path2 ...]]
        [MATLAB_ADDITIONAL_STARTUP_OPTIONS option1 [option2 ...]]
        [TEST_ARGS arg1 [arg2 ...]]
        [NO_UNITTEST_FRAMEWORK]
        )

  The function arguments are:

  ``NAME``
    name of the unittest in ctest.
  ``UNITTEST_FILE``
    the matlab unittest file. Its path will be automatically
    added to the Matlab path.
  ``CUSTOM_TEST_COMMAND``
    Matlab script command to run as the test.
    If this is not set, then the following is run:
    ``runtests('matlab_file_name'), exit(max([ans(1,:).Failed]))``
    where ``matlab_file_name`` is the ``UNITTEST_FILE`` without the extension.
  ``UNITTEST_PRECOMMAND``
    Matlab script command to be ran before the file
859
    containing the test (eg. GPU device initialization based on CMake
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
    variables).
  ``TIMEOUT``
    the test timeout in seconds. Defaults to 180 seconds as the
    Matlab unit test may hang.
  ``ADDITIONAL_PATH``
    a list of paths to add to the Matlab path prior to
    running the unit test.
  ``MATLAB_ADDITIONAL_STARTUP_OPTIONS``
    a list of additional option in order
    to run Matlab from the command line.
    ``-nosplash -nodesktop -nodisplay`` are always added.
  ``TEST_ARGS``
    Additional options provided to the add_test command. These
    options are added to the default options (eg. "CONFIGURATIONS Release")
  ``NO_UNITTEST_FRAMEWORK``
    when set, indicates that the test should not
    use the unittest framework of Matlab (available for versions >= R2013a).
  ``WORKING_DIRECTORY``
    This will be the working directory for the test. If specified it will
    also be the output directory used for the log file of the test run.
    If not specified the temporary directory ``${CMAKE_BINARY_DIR}/Matlab`` will
    be used as the working directory and the log location.

#]=======================================================================]
884
885
886
887
888
889
890
function(matlab_add_unit_test)

  if(NOT Matlab_MAIN_PROGRAM)
    message(FATAL_ERROR "[MATLAB] This functionality needs the MAIN_PROGRAM component (not default)")
  endif()

  set(options NO_UNITTEST_FRAMEWORK)
891
892
  set(oneValueArgs NAME UNITTEST_FILE TIMEOUT WORKING_DIRECTORY
    UNITTEST_PRECOMMAND CUSTOM_TEST_COMMAND)
893
894
895
  set(multiValueArgs ADDITIONAL_PATH MATLAB_ADDITIONAL_STARTUP_OPTIONS TEST_ARGS)

  set(prefix _matlab_unittest_prefix)
896
  cmake_parse_arguments(PARSE_ARGV 0 ${prefix} "${options}" "${oneValueArgs}" "${multiValueArgs}" )
897
898
899
900
901
902
903

  if(NOT ${prefix}_NAME)
    message(FATAL_ERROR "[MATLAB] The Matlab test name cannot be empty")
  endif()

  add_test(NAME ${${prefix}_NAME}
           COMMAND ${CMAKE_COMMAND}
904
905
906
907
908
909
910
            "-Dtest_name=${${prefix}_NAME}"
            "-Dadditional_paths=${${prefix}_ADDITIONAL_PATH}"
            "-Dtest_timeout=${${prefix}_TIMEOUT}"
            "-Doutput_directory=${_matlab_temporary_folder}"
            "-Dworking_directory=${${prefix}_WORKING_DIRECTORY}"
            "-DMatlab_PROGRAM=${Matlab_MAIN_PROGRAM}"
            "-Dno_unittest_framework=${${prefix}_NO_UNITTEST_FRAMEWORK}"
911
            "-DMatlab_ADDITIONAL_STARTUP_OPTIONS=${${prefix}_MATLAB_ADDITIONAL_STARTUP_OPTIONS}"
912
913
914
            "-Dunittest_file_to_run=${${prefix}_UNITTEST_FILE}"
            "-Dcustom_Matlab_test_command=${${prefix}_CUSTOM_TEST_COMMAND}"
            "-Dcmd_to_run_before_test=${${prefix}_UNITTEST_PRECOMMAND}"
915
916
917
918
919
920
921
            -P ${_FindMatlab_SELF_DIR}/MatlabTestsRedirect.cmake
           ${${prefix}_TEST_ARGS}
           ${${prefix}_UNPARSED_ARGUMENTS}
           )
endfunction()


922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
#[=======================================================================[.rst:
.. command:: matlab_add_mex

  Adds a Matlab MEX target.
  This commands compiles the given sources with the current tool-chain in
  order to produce a MEX file. The final name of the produced output may be
  specified, as well as additional link libraries, and a documentation entry
  for the MEX file. Remaining arguments of the call are passed to the
  :command:`add_library` or :command:`add_executable` command.

  ::

     matlab_add_mex(
         NAME <name>
         [EXECUTABLE | MODULE | SHARED]
         SRC src1 [src2 ...]
         [OUTPUT_NAME output_name]
         [DOCUMENTATION file.txt]
         [LINK_TO target1 target2 ...]
941
         [R2017b | R2018a]
942
         [EXCLUDE_FROM_ALL]
943
944
945
946
947
948
949
950
951
         [...]
     )

  ``NAME``
    name of the target.
  ``SRC``
    list of source files.
  ``LINK_TO``
    a list of additional link dependencies.  The target links to ``libmex``
952
    and ``libmx`` by default.
953
954
955
956
957
958
959
960
961
962
  ``OUTPUT_NAME``
    if given, overrides the default name. The default name is
    the name of the target without any prefix and
    with ``Matlab_MEX_EXTENSION`` suffix.
  ``DOCUMENTATION``
    if given, the file ``file.txt`` will be considered as
    being the documentation file for the MEX file. This file is copied into
    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.
963
964
965
966
  ``R2017b`` or ``R2018a``
    .. versionadded:: 3.14

    May be given to specify the version of the C API
967
968
969
970
971
    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``.
972
973
974
975
976
977
978
979
980
981
982

  ``MODULE`` or ``SHARED``
    .. versionadded:: 3.7

    May be given to specify the type of library to be
    created.

  ``EXECUTABLE``
    .. versionadded:: 3.7

    May be given to create an executable instead of
983
    a library. If no type is given explicitly, the type is ``SHARED``.
984
985
986
987
  ``EXCLUDE_FROM_ALL``
    This option has the same meaning as for :prop_tgt:`EXCLUDE_FROM_ALL` and
    is forwarded to :command:`add_library` or :command:`add_executable`
    commands.
988
989
990
991
992
993
994
995
996
997

  The documentation file is not processed and should be in the following
  format:

  ::

    % This is the documentation
    function ret = mex_target_output_name(input1)

#]=======================================================================]
998
function(matlab_add_mex)
999
1000

  if(NOT WIN32)
For faster browsing, not all history is shown. View entire blame