This post (heavily inspired by this and this) will show you how to use cmake (version 2.8) to build a MEX library for MATLAB. This is the first step toward interfacing MATLAB to ITK (the Insight ToolKit) and VTK (the Visualization ToolKit). For the sake of introduction, MEX stands for MATLAB Executable. MEX files are dynamically linked subroutines produced from C, C++ or Fortran source code that, when compiled, can be run from within MATLAB in the same way as MATLAB M-files or built-in functions. Here, we will see how to create a simple c/c++ MEX library for MATLAB using cmake on Mandriva Linux 2010 64 bit (using gcc 4.2) and Microsoft Windows XP 64.
We will need two files, CMakeLists.txt and cmex.c. CMakeLists.txt will be used by cmake to build platform-dependent files for compilation (e.g. a Makefile in Linux, a Visual Studio solution in Windows). cmex.c is the actual c code for the MEX library (which is going to be incredibly simple for the sake of the demonstration).
Here is the content of CMakeLists.txt, with in green the variables defined by the FindMatlab.cmake module (see below):
PROJECT(CMEX)
# Set the minimum CMAKE required to 2.8.
cmake_minimum_required( VERSION 2.8 )
# Find MATLAB (mind that Find_Matlab.cmake might need to be modified to
# find your MATLAB installation)
FIND_PACKAGE( Matlab )
IF( MATLAB_FOUND )
INCLUDE_DIRECTORIES( ${MATLAB_INCLUDE_DIR} )
ELSE( MATLAB_FOUND )
MESSAGE( FATAL_ERROR "MATLAB not found!" )
ENDIF( MATLAB_FOUND )
# Mex libs have specific extensions, which are platform dependent.
# The following will not work on a MAC, but the tests can be easily extended.
IF( WIN32 )
IF( CMAKE_SIZEOF_VOID_P EQUAL 4 )
# x86
SET(MATLAB_MEXFILE_EXT mexw32)
ELSE( CMAKE_SIZEOF_VOID_P EQUAL 4 )
# x64
SET(MATLAB_MEXFILE_EXT mexw64)
ENDIF( CMAKE_SIZEOF_VOID_P EQUAL 4 )
ELSE( WIN32 )
IF( CMAKE_SIZEOF_VOID_P EQUAL 4 )
# Linux x86
SET(MATLAB_MEXFILE_EXT mexglx)
ELSE( CMAKE_SIZEOF_VOID_P EQUAL 4 )
# Linux x64
SET(MATLAB_MEXFILE_EXT mexa64)
ENDIF( CMAKE_SIZEOF_VOID_P EQUAL 4 )
ENDIF( WIN32 )
# We create a library cmex
ADD_LIBRARY( cmex SHARED cmex.c )
# Define as MATLAB MEX file
ADD_DEFINITIONS( -DMATLAB_MEX_FILE )
# The file mexFunction.def exports the library access point (mexFunction)
IF( WIN32)
SET_TARGET_PROPERTIES( cmex PROPERTIES LINK_FLAGS "/export:mexFunction" )
ENDIF( WIN32 )
# Set the correct file extension
SET_TARGET_PROPERTIES( cmex PROPERTIES
PREFIX ""
SUFFIX ".${MATLAB_MEXFILE_EXT}" )
# We link against the MATLAB MEX libraries
TARGET_LINK_LIBRARIES( cmex ${MATLAB_LIBRARIES} )
The call to Find_Package(Matlab) might fail for your specific MATLAB installation (it for sure failed for mine), since the FindMatlab.cmake module that comes with cmake contains some hard-coded, default paths that won’t fit most installations. If this is the case, you will have to change it slightly (on Mandriva Linux 2010 under /usr/share/cmake/Modules/FindMatlab.cmake, on Windows XP under C:\Program Files (x86)\CMake 2.8\share\cmake-2.8\Modules) to look for MATLAB in the right place. For my installation, the relevant changes are in red below.
Here is a tripped-down version of FindMatlab.cmake as distributed with cmake 2.8. The green variables are used by CMakeLists.txt:
# - this module looks for Matlab
# Defines:
# MATLAB_INCLUDE_DIR: include path for mex.h, engine.h
# MATLAB_LIBRARIES: required libraries: libmex, etc
# MATLAB_MEX_LIBRARY: path to libmex.lib
# MATLAB_MX_LIBRARY: path to libmx.lib
# MATLAB_ENG_LIBRARY: path to libeng.lib
#=============================================================================
# Copyright 2005-2009 Kitware, Inc.
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distributed this file outside of CMake, substitute the full
# License text for the above reference.)
SET(MATLAB_FOUND 0)
IF(WIN32)
IF(${CMAKE_GENERATOR} MATCHES "Visual Studio 9")
SET(MATLAB_ROOT "C:/Program Files/MATLAB/R2009b/extern/lib/win64/microsoft")
ELSE(${CMAKE_GENERATOR} MATCHES "Visual Studio 9")
IF(MATLAB_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Generator not compatible: ${CMAKE_GENERATOR}")
ENDIF(MATLAB_FIND_REQUIRED)
ENDIF(${CMAKE_GENERATOR} MATCHES "Visual Studio 9")
FIND_LIBRARY(MATLAB_MEX_LIBRARY
libmex
${MATLAB_ROOT}
)
FIND_LIBRARY(MATLAB_MX_LIBRARY
libmx
${MATLAB_ROOT}
)
FIND_LIBRARY(MATLAB_ENG_LIBRARY
libeng
${MATLAB_ROOT}
)
FIND_PATH(MATLAB_INCLUDE_DIR
"mex.h"
"C:/Program Files/MATLAB/R2009b/extern/include"
)
ELSE( WIN32 )
IF(CMAKE_SIZEOF_VOID_P EQUAL 4)
# Regular x86
SET( MATLAB_ROOT /usr/local/r2009b/bin/glnx86/ )
ELSE(CMAKE_SIZEOF_VOID_P EQUAL 4)
# AMD64:
SET(MATLAB_ROOT /usr/local/r2009b/bin/glnxa64/ )
ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 4)
FIND_LIBRARY(MATLAB_MEX_LIBRARY
mex
${MATLAB_ROOT}
)
FIND_LIBRARY(MATLAB_MX_LIBRARY
mx
${MATLAB_ROOT}
)
FIND_LIBRARY(MATLAB_ENG_LIBRARY
eng
${MATLAB_ROOT}
)
FIND_PATH(MATLAB_INCLUDE_DIR
"mex.h"
"/usr/local/r2009b/extern/include/"
)
ENDIF(WIN32)
# This is common to UNIX and Win32:
SET(MATLAB_LIBRARIES
${MATLAB_MEX_LIBRARY}
${MATLAB_MX_LIBRARY}
${MATLAB_ENG_LIBRARY}
)
IF(MATLAB_INCLUDE_DIR AND MATLAB_LIBRARIES)
SET(MATLAB_FOUND 1)
ENDIF(MATLAB_INCLUDE_DIR AND MATLAB_LIBRARIES)
MARK_AS_ADVANCED(
MATLAB_LIBRARIES
MATLAB_MEX_LIBRARY
MATLAB_MX_LIBRARY
MATLAB_ENG_LIBRARY
MATLAB_INCLUDE_DIR
MATLAB_FOUND
MATLAB_ROOT
)
Finally, the cmex.c file contains a very simple MEX function that returns the number of elements in the passed input array:
/*
* cmex.c
*
*/
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[])
{
plhs[ 0 ] = mxCreateDoubleMatrix( 1, 1, mxREAL );
*mxGetPr( plhs[ 0 ] ) = ( double ) mxGetNumberOfElements( prhs[ 0 ] );
}
Copy or save the two files CMakeLists.txt and cmex.c to a directory of your choice, e.g. /home/{you}/Devel/cmex. Now you can cmake (or ccmake, or cmake-gui) the project. At the bash prompt ($), after having changed to the directory containing the three files, type:
$ cmake .
This should result in the following (or similar) output.
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to /home/{you}/Devel/cmex
Please mind that in Windows 64 bit using cmake as shown above does not work because the test for 64 bit fails. Using cmake-gui (and selecting Visual Studio 2008 64 bit as the compiler) in contrast works as expected.
And now make (or open the solution in Visual Studio and compile):
$ make
that should say…
Scanning dependencies of target cmex
[100%] Building C object CMakeFiles/cmex.dir/cmex.c.o
Linking C shared library cmex.mexa64
[100%] Built target cmex
You can now copy/add the generated cmex.mexa64 (or cmex.mexglx, cmex.mexw32, cmex.mexw64) file to your MATLAB path and use it as follows:
>> cmex( rand( 5, 5 ) )
ans =
25
where >> is the MATLAB prompt.