CMakeLists.txt 14.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
# ----------------------------------------------------------------------------
#  CMake file for Matlab/Octave support
#
#  Matlab code generation and compilation is broken down into two distinct
#  stages: configure time and build time. The idea is that we want to give
#  the user reasonable guarantees that once they type 'make', wrapper
#  generation is unlikely to fail. Therefore we run a series of tests at
#  configure time to check the working status of the core components.
#
#  Configure Time
#  During configure time, the script attempts to ascertain whether the
#  generator and mex compiler are working for a given architecture.
#  Currently this involves:
#   1) Generating a simple CV_EXPORTS_W symbol and checking whether a file
#      of the symbol name is generated
#   2) Compiling a simple mex gateway to check that Bridge.hpp and mex.h
#      can be found, and that a file with the mexext is produced
#
#  Build Time
#  If the configure time tests pass, then we assume Matlab wrapper generation
#  will not fail during build time. We simply glob all of the symbols in
#  the OpenCV module headers, generate intermediate .cpp files, then compile
#  them with mex.
# ----------------------------------------------------------------------------

26 27 28
# ----------------------------------------------------------------------------
#  Architecture checks
# ----------------------------------------------------------------------------
29
# make sure we're on a supported architecture with Matlab and Python (with jinja2) installed
30 31
if(APPLE_FRAMEWORK OR ANDROID OR NOT MATLAB_FOUND)
    ocv_module_disable(matlab)
32
elseif(NOT PYTHON_DEFAULT_AVAILABLE)
33 34
    message(WARNING "A required dependency of the matlab module (Python) was not found. Disabling Matlab bindings...")
    ocv_module_disable(matlab)
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
endif()

if(NOT DEFINED HAVE_PYTHON_JINJA2)
  # Bindings generator requires Jinja2 python package
  execute_process(COMMAND "${PYTHON_DEFAULT_EXECUTABLE}" -c "import jinja2; print(jinja2.__version__)"
                  RESULT_VARIABLE _result
                  OUTPUT_VARIABLE _jinja2_version
                  OUTPUT_STRIP_TRAILING_WHITESPACE)

  if(NOT _result EQUAL 0)
    set(HAVE_PYTHON_JINJA2 0 CACHE INTERNAL "")
  else()
    message(STATUS "Python Jinja version: ${_jinja2_version}")
    set(HAVE_PYTHON_JINJA2 1 CACHE INTERNAL "")
  endif()
endif()
if(NOT HAVE_PYTHON_JINJA2)
  message(WARNING "A required dependency of the matlab module (Python Jinja2 package) was not found (installation command: \"pip install jinja2\"). Disabling Matlab bindings...")
  ocv_module_disable(matlab)
54 55 56
endif()


57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
# PREPEND
# Given a list of strings IN and a TOKEN, prepend the token to each string
# and append to OUT. This is used for passing command line "-I", "-L" and "-l"
# arguments to mex. e.g.
# prepend("-I" OUT /path/to/include/dir) --> -I/path/to/include/dir
macro(PREPEND TOKEN OUT IN)
    foreach(VAR ${IN} ${ARGN})
        list(APPEND ${OUT} "${TOKEN}${VAR}")
    endforeach()
endmacro()


# WARN_MIXED_PRECISION
# Formats a warning message if the compiler and Matlab bitness is different
macro(WARN_MIXED_PRECISION COMPILER_BITNESS MATLAB_BITNESS)
    set(MSG "Your compiler is ${COMPILER_BITNESS}-bit")
    set(MSG "${MSG} but your version of Matlab is ${MATLAB_BITNESS}-bit.")
    set(MSG "${MSG} To build Matlab bindings, please switch to a ${MATLAB_BITNESS}-bit compiler.")
    message(WARNING ${MSG})
endmacro()

78 79 80
ocv_assert(DEFINED MATLAB_ARCH)
ocv_assert(DEFINED MATLAB_MEX_SCRIPT)
ocv_assert(DEFINED MATLAB_MEXEXT)
81 82 83 84 85 86 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

# If the user built OpenCV as X-bit, but they have a Y-bit version of Matlab,
# attempting to link to OpenCV during binding generation will fail, since
# mixed precision pointers are not allowed. Disable the bindings.
math(EXPR ARCH "${CMAKE_SIZEOF_VOID_P} * 8")
if (${ARCH} EQUAL 32 AND ${MATLAB_ARCH} MATCHES "64")
    warn_mixed_precision("32" "64")
    ocv_module_disable(matlab)
    return()
elseif (${ARCH} EQUAL 64 AND NOT ${MATLAB_ARCH} MATCHES "64")
    warn_mixed_precision("64" "32")
    ocv_module_disable(matlab)
    return()
endif()

# If it's MSVC, warn the user that bindings will only be built in Release mode.
# Debug mode seems to cause issues...
if (MSVC)
    message(STATUS "Warning: Matlab bindings will only be built in Release configurations")
endif()


# ----------------------------------------------------------------------------
#  Configure time components
# ----------------------------------------------------------------------------
set(the_description "The Matlab/Octave bindings")
ocv_add_module(matlab   BINDINGS
                        OPTIONAL opencv_core
                                 opencv_imgproc opencv_ml
                                 opencv_imgcodecs opencv_videoio opencv_highgui
                                 opencv_objdetect opencv_flann opencv_features2d
                                 opencv_photo opencv_video opencv_videostab
                                 opencv_calib opencv_calib3d
                                 opencv_stitching opencv_superres
115
                                 opencv_xfeatures2d
116 117 118 119 120 121 122 123 124 125
)

# get the commit information
execute_process(COMMAND git log -1 --pretty=%H OUTPUT_VARIABLE GIT_COMMIT ERROR_QUIET)
string(REGEX REPLACE "(\r?\n)+$" "" GIT_COMMIT "${GIT_COMMIT}")

# set the path to the C++ header and doc parser, and template engine
set(HDR_PARSER_PATH ${CMAKE_SOURCE_DIR}/modules/python/src2)

# set mex compiler options
126
prepend("-I" MEX_INCLUDE_DIRS ${CMAKE_BINARY_DIR})
127 128 129 130 131 132 133 134 135 136 137 138 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 187 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 226 227 228 229 230 231 232 233 234 235 236
prepend("-I" MEX_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include)
if (MSVC)
    prepend("-L" MEX_LIB_DIR  ${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR})
else()
    prepend("-L" MEX_LIB_DIR  ${LIBRARY_OUTPUT_PATH})
endif()
set(MEX_OPTS "-largeArrayDims")

if (BUILD_TESTS)
    add_subdirectory(test)
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)


# intersection of available modules and optional dependencies
# 1. populate the command-line include directories (-I/path/to/module/header, ...)
# 2. populate the command-line link libraries (-lopencv_core, ...) for Debug and Release
set(MATLAB_DEPS ${OPENCV_MODULE_${the_module}_REQ_DEPS} ${OPENCV_MODULE_${the_module}_OPT_DEPS})
foreach(opencv_module ${MATLAB_DEPS})
    if (HAVE_${opencv_module})
        string(REPLACE "opencv_" "" module ${opencv_module})
        list(APPEND opencv_modules ${module})
        list(APPEND ${the_module}_ACTUAL_DEPS ${opencv_module})
        prepend("-I" MEX_INCLUDE_DIRS "${OPENCV_MODULE_${opencv_module}_LOCATION}/include")
        prepend("-l" MEX_LIBS ${opencv_module}${OPENCV_DLLVERSION})
        prepend("-l" MEX_DEBUG_LIBS ${opencv_module}${OPENCV_DLLVERSION}${OPENCV_DEBUG_POSTFIX})
    endif()
endforeach()

# add extra headers by hand
list(APPEND opencv_extra_hdrs "core=${OPENCV_MODULE_opencv_core_LOCATION}/include/opencv2/core/base.hpp")
list(APPEND opencv_extra_hdrs "video=${OPENCV_MODULE_opencv_video_LOCATION}/include/opencv2/video/tracking.hpp")

# pass the OPENCV_CXX_EXTRA_FLAGS through to the mex compiler
# remove the visibility modifiers, so the mex gateway is visible
# TODO: get mex working without warnings
string(REGEX REPLACE "[^\ ]*visibility[^\ ]*" "" MEX_CXXFLAGS "${OPENCV_EXTRA_FLAGS} ${OPENCV_EXTRA_CXX_FLAGS}")

# Configure checks
# Check to see whether the generator and the mex compiler are working.
# The checks currently test:
#   - whether the python generator can be found
#   - whether the python generator correctly outputs a file for a definition
#   - whether the mex compiler can find the required headers
#   - whether the mex compiler can compile a trivial definition
if (NOT MEX_WORKS)
    # attempt to generate a gateway for a function
    message(STATUS "Trying to generate Matlab code")
    execute_process(
        COMMAND ${PYTHON_DEFAULT_EXECUTABLE}
                ${CMAKE_CURRENT_SOURCE_DIR}/generator/gen_matlab.py
                --hdrparser ${HDR_PARSER_PATH}
                --extra     "test=${CMAKE_CURRENT_SOURCE_DIR}/test/test_generator.hpp"
                --outdir    ${CMAKE_BINARY_DIR}/junk
        ERROR_VARIABLE GEN_ERROR
        OUTPUT_QUIET
    )

    if (GEN_ERROR)
        message(${GEN_ERROR})
        message(STATUS "Error generating Matlab code. Disabling Matlab bindings...")
        ocv_module_disable(matlab)
        return()
    else()
        message(STATUS "Trying to generate Matlab code - OK")
    endif()

    # attempt to compile a gateway using mex
    message(STATUS "Trying to compile mex file")
    execute_process(
        COMMAND ${MATLAB_MEX_SCRIPT} ${MEX_OPTS} "CXXFLAGS=\$CXXFLAGS ${MEX_CXX_FLAGS}"
                ${MEX_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_compiler.cpp
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/junk
        ERROR_VARIABLE MEX_ERROR
        OUTPUT_QUIET
    )

    if (MEX_ERROR)
        message(${MEX_ERROR})
        message(STATUS "Error compiling mex file. Disabling Matlab bindings...")
        ocv_module_disable(matlab)
        return()
    else()
        message(STATUS "Trying to compile mex file - OK")
    endif()
endif()

# if we make it here, mex works!
set(MEX_WORKS True CACHE BOOL ADVANCED)


# ----------------------------------------------------------------------------
#  Build time components
# ----------------------------------------------------------------------------

# proxies
# these proxies are used to trigger the add_custom_commands
# (which do the real work) only when they're outdated
set(GENERATE_PROXY ${CMAKE_CURRENT_BINARY_DIR}/generate.proxy)
set(COMPILE_PROXY ${CMAKE_CURRENT_BINARY_DIR}/compile.proxy)
# TODO: Remove following line before merging with master
file(REMOVE ${GENERATE_PROXY} ${COMPILE_PROXY})

# generate
# call the python executable to generate the Matlab gateways
add_custom_command(
    OUTPUT ${GENERATE_PROXY}
    COMMAND ${PYTHON_DEFAULT_EXECUTABLE}
            ${CMAKE_CURRENT_SOURCE_DIR}/generator/gen_matlab.py
            --hdrparser  ${HDR_PARSER_PATH}
237
            --moduleroot ${CMAKE_SOURCE_DIR}/modules ${OPENCV_EXTRA_MODULES_PATH}
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
            --modules    ${opencv_modules}
            --extra      ${opencv_extra_hdrs}
            --outdir     ${CMAKE_CURRENT_BINARY_DIR}
    COMMAND ${PYTHON_DEFAULT_EXECUTABLE}
            ${CMAKE_CURRENT_SOURCE_DIR}/generator/build_info.py
            --os             ${CMAKE_SYSTEM}
            --arch           ${ARCH} ${CMAKE_SYSTEM_PROCESSOR}
            --compiler       ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}
            --mex_arch       ${MATLAB_ARCH}
            --mex_script     ${MATLAB_MEX_SCRIPT}
            --cxx_flags      ${MEX_CXXFLAGS}
            --opencv_version ${OPENCV_VERSION}
            --commit         ${GIT_COMMIT}
            --modules        ${opencv_modules}
            --configuration  $<CONFIGURATION>
            --outdir         ${CMAKE_CURRENT_BINARY_DIR}
    COMMAND ${PYTHON_DEFAULT_EXECUTABLE}
            ${CMAKE_CURRENT_SOURCE_DIR}/generator/cvmex.py
            --opts="${MEX_OPTS}"
            --include_dirs="${MEX_INCLUDE_DIRS}"
            --lib_dir="${MEX_LIB_DIR}"
            --libs="${MEX_LIBS}"
            --flags  ${MEX_CXXFLAGS}
            --outdir ${CMAKE_CURRENT_BINARY_DIR}
    COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/test/help.m ${CMAKE_CURRENT_BINARY_DIR}/+cv
    COMMAND ${CMAKE_COMMAND} -E touch ${GENERATE_PROXY}
    COMMENT "Generating Matlab source files"
265 266 267 268 269 270 271
    DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/build_info.py"
    DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/cvmex.py"
    DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/filters.py"
    DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/gen_matlab.py"
    DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/parse_tree.py"
    DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/templates/functional.cpp"
    DEPENDS "${CMAKE_CURRENT_LIST_DIR}/generator/templates/template_function_base.cpp"
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
)

# compile
# call the mex compiler to compile the gateways
# because we don't know the source files at configure-time, this
# has to be executed in a separate script in cmake's script processing mode
add_custom_command(
    OUTPUT ${COMPILE_PROXY}
    COMMAND ${CMAKE_COMMAND} -DMATLAB_MEX_SCRIPT=${MATLAB_MEX_SCRIPT}
                             -DMATLAB_MEXEXT=${MATLAB_MEXEXT}
                             -DMEX_OPTS=${MEX_OPTS}
                             -DMEX_CXXFLAGS=${MEX_CXX_FLAGS}
                             -DMEX_INCLUDE_DIRS="${MEX_INCLUDE_DIRS}"
                             -DMEX_LIB_DIR="${MEX_LIB_DIR}"
                             -DCONFIGURATION="$<CONFIGURATION>"
                             -DMEX_LIBS="${MEX_LIBS}"
                             -DMEX_DEBUG_LIBS="${MEX_DEBUG_LIBS}"
                             -P ${CMAKE_CURRENT_SOURCE_DIR}/compile.cmake
    COMMAND ${CMAKE_COMMAND} -E touch ${COMPILE_PROXY}
    COMMENT "Compiling Matlab source files. This could take a while..."
)

# targets
# opencv_matlab_sources --> opencv_matlab
add_custom_target(${the_module}_sources ALL DEPENDS ${GENERATE_PROXY})
add_custom_target(${the_module} ALL DEPENDS ${COMPILE_PROXY})
add_dependencies(${the_module} ${the_module}_sources ${${the_module}_ACTUAL_DEPS})

if (ENABLE_SOLUTION_FOLDERS)
    set_target_properties(${the_module} PROPERTIES FOLDER "modules")
endif()


# ----------------------------------------------------------------------------
#  Install time components
# ----------------------------------------------------------------------------
# NOTE: Trailing slashes on the DIRECTORY paths are important!
# TODO: What needs to be done with rpath????

# install the +cv directory verbatim
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION ${OPENCV_INCLUDE_INSTALL_PATH})
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/+cv/     DESTINATION matlab/+cv)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cv.m         DESTINATION matlab)

# update the custom mex compiler to point to the install locations
string(REPLACE ";" "\\ " MEX_OPTS "${MEX_OPTS}")
string(REPLACE ";" "\\ " MEX_LIBS "${MEX_LIBS}")
string(REPLACE " " "\\ " MEX_CXXFLAGS ${MEX_CXXFLAGS})
string(REPLACE ";" "\\ " MEX_INCLUDE_DIRS "${MEX_INCLUDE_DIRS}")
install(CODE
    "execute_process(
    COMMAND ${PYTHON_DEFAULT_EXECUTABLE}
            ${CMAKE_CURRENT_SOURCE_DIR}/generator/cvmex.py
            --opts=${MEX_OPTS}
            --include_dirs=-I${CMAKE_INSTALL_PREFIX}/${OPENCV_INCLUDE_INSTALL_PATH}
            --lib_dir=-L${CMAKE_INSTALL_PREFIX}/${OPENCV_LIB_INSTALL_PATH}
            --libs=${MEX_LIBS}
            --flags=${MEX_CXXFLAGS}
            --outdir ${CMAKE_INSTALL_PREFIX}/matlab
    )"
)