Pages

Wednesday, June 11, 2014

A Tale of CMAKE + Matlab + OpenCV + OS X 10.9

I entered my blogging user interface, clicked drafts, and found an assortment of entires that roughly describe my usual development exploits and frustrations. One such draft detailed my first interaction with the Matlab mex script, it's listed as a compilation function but if you cat the file its a shell script that provides interoperability by checking system variables and the like, to compile and link C source files into a binary file that is callable in Matlab. Very cool!

Well, very cool if you're on Linux or Windows even. Apple's subtle and not-so-subtle changes per the developer ecosystem can be infuriating. It ain't BSD-like anymore folks. While I applaud some neat and not heavily known features of the latest OS X, such as:
  1. Attempting to use git
  2. git not found 
  3. Would you like to install developer tools (not all of XCode)
I still find my workflow affected by clang specifics (e.g., compiler permissiveness, flags), apple specifics (e.g., sandbox, default configurations), etc. One such problem surfaced when I was attempting to recompile some source that was graciously provided to aid my own research. The code had a dependency on an older version of OpenCV. My options, modify the source to accept the latest version of OpenCV, which had deprecated functions and refactored functionality into new headers, or attempt to compile the old version and fix whatever clang might spit out at me. I tried both options. I ended with option two.

I first address my issue with clang on OS X 10.9 and mex. On my installed Matlab version, R2014a, the default mexopts.sh, found in /Applications/MATLAB_R2014a.app/bin/, specified the development target as 10.6. This is problematic because, for example, the latest version of clang uses a different standard C++ library, libc++ vice libstdc++ (example problem), that is not supported in 10.6. I needed to change this.

I first ran /Applications/MATLAB_R2014a.app/bin/mex --setup. This created a local copy of mexopts to ~/.matlab/R2014a/ and will be interpreted on subsequent calls to mex unless -f is passed (pointing to a different mexopts script). This allowed me to work on a local copy of mexopts.sh without fear of mucking up the default file. I then modified the local copy to look as follows: 
        maci64)
#----------------------------------------------------------------------------
            # StorageVersion: 1.0
            # CkeyName: Clang
            # CkeyManufacturer: Apple
            # CkeyLanguage: C
            # CkeyVersion:
            # CkeyLinkerName:
            # CkeyLinkerVersion:
            CC='xcrun  -sdk macosx10.8  clang'
## workaround clang defect temporarily use line below           SDKROOT='/Developer/SDKs/MacOSX10.6.sdk'
# compute SDK root on the fly
# target 10.8 by Mike R. 
            MW_SDKROOT_TMP="find `xcode-select -print-path` -name MacOSX10.8.sdk"
			MW_SDKROOT=`$MW_SDKROOT_TMP`
            MACOSX_DEPLOYMENT_TARGET='10.8'
            ARCHS='x86_64'
            #ARCHS='x86_64 -stdlib=libstdc++'
            CFLAGS="-fno-common -arch $ARCHS -isysroot $MW_SDKROOT -mmacosx-version-min=$MACOSX_DEPLOYMENT_TARGET"
            CFLAGS="$CFLAGS  -fexceptions"
            CLIBS="$MLIBS"
            COPTIMFLAGS='-O2 -DNDEBUG'
            CDEBUGFLAGS='-g'
#
            CLIBS="$CLIBS -lstdc++"
            # C++keyName: Clang++
            # C++keyManufacturer: Apple
            # C++keyLanguage: C++
            # C++keyVersion:
            # C++keyLinkerName:
            # C++keyLinkerVersion:
            CXX='xcrun  -sdk macosx10.8  clang++'
            CXXFLAGS="-fno-common -fexceptions -arch $ARCHS -isysroot $MW_SDKROOT -mmacosx-version-min=$MACOSX_DEPLOYMENT_TARGET"
            CXXLIBS="$MLIBS -lstdc++"
            CXXOPTIMFLAGS='-O2 -DNDEBUG'
            CXXDEBUGFLAGS='-g'
#
            # FortrankeyName: GNU Fortran
            # FortrankeyManufacturer: GNU
            # FortrankeyLanguage: Fortran
            # FortrankeyVersion: 
            # FortrankeyLinkerName: 
            # FortrankeyLinkerVersion:
            FC='gfortran'
            FFLAGS='-fexceptions -m64 -fbackslash'
            FC_LIBDIR=`$FC -print-file-name=libgfortran.dylib 2>&1 | sed -n '1s/\/*libgfortran\.dylib//p'`
            FC_LIBDIR2=`$FC -print-file-name=libgfortranbegin.a 2>&1 | sed -n '1s/\/*libgfortranbegin\.a//p'`
            FLIBS="$MLIBS -L$FC_LIBDIR -lgfortran -L$FC_LIBDIR2 -lgfortranbegin"
            FOPTIMFLAGS='-O'
            FDEBUGFLAGS='-g'
#
            LD="$CC"
            LDEXTENSION='.mexmaci64'
            LDFLAGS="-arch $ARCHS -Wl,-syslibroot,$MW_SDKROOT -mmacosx-version-min=$MACOSX_DEPLOYMENT_TARGET"
            LDFLAGS="$LDFLAGS -bundle -Wl,-exported_symbols_list,$TMW_ROOT/extern/lib/$Arch/$MAPFILE"
            LDOPTIMFLAGS='-O'
            LDDEBUGFLAGS='-g'
#
            POSTLINK_CMDS=':'
#----------------------------------------------------------------------------
            ;;
The basis for this change are explained here, and officially here. Once this was done, I had to patch one or two errors in opencv2.4.2 that bubbled up because of clang (patches which I will share when I find them). I then used cmake to build opencv without error:
cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX:PATH=/opt/opencv -DCMAKE_OSX_ARCHITECTURES=x86_64 -DBUILD_opencv_matlab=NO -DCMAKE_CXX_FLAGS="-stdlib=libstdc++" ..
On a quick aside, I am still green with respect to cmake. I usually dig through the cmake configuration files to find build specific variables. I then stumbled on this command: cmake -i. If you do this in the source directory it will print all variables to stdout. I'm sure there might be a better way, but this made my day.

Finally, the following use of mex then went without a hitch:
/Applications/MATLAB_R2014a.app/bin/mex -v -O -largeArrayDims -I/opt/opencv/include -L/opt/opencv/lib -lopencv_core -lopencv_imgproc -lopencv_objdetect -lopencv_highgui mysource.cpp

Take a look at the following compilation command and screenshot for a laugh. It was a late night stumper for me. Why was I getting duplicate symbols? If I dropped one linked library I would get a linker error but the duplication went away. Was there some sort of circular linking going on? Could this post be the answer?
/Applications/MATLAB_R2014a.app/bin/mex -v -c -O -largeArrayDims -I/opt/opencv/include facedetection.cpp -L/opt/opencv/lib -lopencv_core -lopencv_imgproc -lopencv_objdetect -lopencv_highgui facedetection.cpp
Silly, silly programmer. Do you see the error? That's all for this post. Please come back later! I plan on writing up my experience with ArrayBuffers in different browsers and doing video encoding on a Raspberry Pi.

No comments:

Post a Comment