Search code examples
opencv3.0zlibintel-ipp

Linking OpenCV with static zlib using IPP, using CMake on Linux


I'm building OpenCV from source, using the standalone Intel version of the IPP libraries. I'm also using the Intel-optimised version of zlib, which (unlike the standard version of zlib) carries a dependency on three further IPP libraries. Also, I'm linking zlib in statically, so I need to make any target that uses it aware of these dependencies.

I've already got everything working on Windows, and now I'm trying to do the same in Linux. I've added the following text to the master CMakeLists.txt file in Open CV, just after the place where all the other dependencies are searched for and initialised:

include(cmake/OpenCVFindLibsGrfmt.cmake)
include(cmake/OpenCVFindLibsGUI.cmake)
include(cmake/OpenCVFindLibsVideo.cmake)
include(cmake/OpenCVFindLibsPerf.cmake)
include(cmake/OpenCVFindLAPACK.cmake)
include(cmake/OpenCVFindProtobuf.cmake)

#---------------------------My changes start here-------------------------
# We insert this code here because this is the point where ZLIB_LIBRARIES 
# has just become defined, and by putting it here we ensure that our 
# changes are propagated throughout the rest of the project.
if (MSVC)

    add_library(ippdcmt STATIC IMPORTED)
    add_library(ippsmt STATIC IMPORTED)
    add_library(ippcoremt STATIC IMPORTED)
    if(CMAKE_SIZEOF_VOID_P EQUAL 8)
        # We are compiling 64-bit code
        set_target_properties(ippdcmt PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/intel64_win/ippdcmt.lib")
        set_target_properties(ippsmt PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/intel64_win/ippsmt.lib")
        set_target_properties(ippcoremt PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/intel64_win/ippcoremt.lib")
    else()
        # We are compiling 32-bit code
        set_target_properties(ippdcmt PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/ia32_win/ippdcmt.lib")
        set_target_properties(ippsmt PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/ia32_win/ippsmt.lib")
        set_target_properties(ippcoremt PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/ia32_win/ipcoremt.lib")
    endif()
    list(APPEND ZLIB_LIBRARIES ippdcmt ippsmt ippcoremt)

elseif(CMAKE_COMPILER_IS_GNUCC)

    add_library(libippdc STATIC IMPORTED)
    add_library(libipps STATIC IMPORTED)
    add_library(libippcore STATIC IMPORTED)
    if(CMAKE_SIZEOF_VOID_P EQUAL 8)
        # We are compiling 64-bit code
        set_target_properties(libippdc PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/intel64/libippdc.a")
        set_target_properties(libipps PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/intel64/libipps.a")
        set_target_properties(libippcore PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/intel64/libippcore.a")        
    else()
        # We are compiling 32-bit code
        set_target_properties(libippdc PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/ia32/libippdc.a")
        set_target_properties(libipps PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/ia32/libipps.a")
        set_target_properties(libippcore PROPERTIES IMPORTED_LOCATION "${IPP_ROOT_DIR}/lib/ia32/libippcore.a")  
    endif()
    list(APPEND ZLIB_LIBRARIES libippdc libipps libippcore)

endif()
#---------------------------My changes end here---------------------------

On running CMake and then make, the build gets to 99% after a couple of hours and then fails on one of the vary last modules to be compiled, complaining that it cannot find the IPP-related dependencies for zlib. On inspecting the compile log, it's easy to see why: for some reason, the makefile has put zlib as the very last library on the link line, after the relevant Intel IPP libraries, and since gcc reads the link line in strict order this means that the dependencies aren't handled correctly.

What I need therefore is a way to force CMake to construct the makefile such a way that the IPP libraries are always put after zlib on the link line.

I've read elsewhere on this site that I ought to be able to achieve this using the CMake property IMPORTED_LINK_INTERFACE_LIBRARIES. So I've tried adding a line akin to

set_target_properties([something to denote zlib] PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES libippdc libipps libippcore)

...immediately after list(APPEND ZLIB_LIBRARIES libippdc libipps libippcore). However, I can't figure out what to put in place of [something to denote zlib] so that CMake understands and acts accordingly. If I replace it with ZLIB::ZLIB (the usual output of CMake's native FindZLIB function), then I get no error messages but it doesn't make a blind bit of difference; a couple of hours later the build fails exactly as it did before. If I try replacing [something to denote zlib] with anything else I can think of or find elsewhere in OpenCV's CMake files - so far, 'zlib', 'ZLIB', 'ZLIB_LIBRARY', 'zlib.a', 'libz.a' - then CMake fails with an error message saying:

set_target_properties Can not find target to add properties to: libz.a (or whatever)

So I guess my question is, how does OpenCV's CMake system refer internally to zlib, so that I can add this property to it? Alternatively, am I approaching this in completely the wrong way, and is there a better way to achieve my objective (which is, in short, having an OpenCV build that statically links the Intel-enhanced version of zlib with all its dependencies)?


Solution

  • It turned out that there was nothing wrong with my block of code. I'm not sure if the set_target_properties line was even necessary, though to be on the safe side I left it in with the final syntax:

      set_property(TARGET   ZLIB::ZLIB 
                   PROPERTY INTERFACE_LINK_LIBRARIES libippdc libipps libippcore)
    

    The main problem was the location of my block of code. It turned out that, within the call to include(cmake/OpenCVFindLibsGrfmt.cmake), zlib itself was found and initialised along with libtiff, libpng and libIlmImf, all of which depended on it. Therefore, the properties of these targets contained a dependency on zlib alone, and my block of code which came afterwards did not change this. My block of code had to be moved between the initialisation of zlib and the initialisation of everything that depended upon it.

    This was a bit tricky, but in the end what I did was:

    1. Moved the line include(cmake/OpenCVFindLibsPerf.cmake) to first in the block, so that the IPP library was found and IPP_ROOT_DIR properly initialised.
    2. Extracted the first ten lines or so from cmake/OpenCVFindLibsGrfmt.cmake, where zlib is initialised, and placed them into the main CMakeLists.txt fine immediately after include(cmake/OpenCVFindLibsPerf.cmake).
    3. Then I re-inserted my block of custom code.
    4. Finally I put in the rest of the includes, to complete initialisation of all the other libraries.

    The result finally, was a clean build.