4

I want to build an application that depends on the OpenCV (version 3.4.6) viz module. This module has the VTK library (version 7.1.1) as dependency. I want to use ExternalProject to build both, the vtk library and the opencv viz module and subsequently want to build the main application, all in one cmake run.

.
├── CMakeLists.txt
├── deps
│   └── CMakeLists.txt
└── main.cpp

I am using the cmake ExternalProject module to build both opencv and vtk inside a subdirectory like this:

deps/CMakeLists.txt

cmake_minimum_required(VERSION 3.14)

project(dependencies)

include(ExternalProject)

ExternalProject_add(
  vtklib
  GIT_REPOSITORY https://github.com/Kitware/vtk
  GIT_TAG v7.1.1
  GIT_PROGRESS TRUE
  UPDATE_COMMAND ""
  CMAKE_ARGS
  -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
  -DBUILD_TESTING=OFF
  -DBUILD_EXAMPLES=OFF
  -DVTK_DATA_EXCLUDE_FROM_ALL=ON
  -DVTK_USE_CXX11_FEATURES=ON
  -Wno-dev
  )

add_library(vtk INTERFACE IMPORTED GLOBAL)
add_dependencies(vtk vtklib)

ExternalProject_add(
  ocv
  GIT_REPOSITORY https://github.com/opencv/opencv
  GIT_TAG 3.4.6
  GIT_PROGRESS TRUE
  UPDATE_COMMAND ""
  CMAKE_ARGS
  -DCMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}
  -DWITH_VTK=ON
  -Wno-dev
  )


# ExternalProject_Get_Property(ocv install_dir)
# include_directories(${install_dir}/src/ocv/include)
include_directories(${CMAKE_INSTALL_PREFIX}/include)

set(ocv_libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_VS_PLATFORM_NAME}/vc15)
set(OCV_VERSION 346)

add_dependencies(ocv vtklib)

add_library(opencv_core SHARED IMPORTED)
set_target_properties(opencv_core PROPERTIES
  IMPORTED_IMPLIB "${ocv_libdir}/lib/opencv_core${OCV_VERSION}.lib"
  IMPORTED_LOCATION "${ocv_libdir}/bin/opencv_core${OCV_VERSION}.dll"
  )

add_library(opencv_viz SHARED IMPORTED)
set_target_properties(opencv_viz PROPERTIES
  IMPORTED_IMPLIB "${ocv_libdir}/lib/opencv_viz${OCV_VERSION}.lib"
  IMPORTED_LOCATION "${ocv_libdir}/bin/opencv_viz${OCV_VERSION}.dll"
  )

the main CMakeLists.txt looks like this:

cmake_minimum_required(VERSION 3.14)

project(cmaketest VERSION 0.1 DESCRIPTION "" LANGUAGES CXX)


set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_Flags "${CMAKE_CXX_FLAGS} -std=c++17")

# include_directories(${CMAKE_INSTALL_PREFIX}/include)
add_subdirectory(deps)


add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} opencv_core opencv_viz)

install(
  TARGETS ${PROJECT_NAME}
  EXPORT "${PROJECT_NAME}-targets"
  LIBRARY DESTINATION lib/
  ARCHIVE DESTINATION lib/${CMAKE_PROJECT_NAME}
  RUNTIME DESTINATION bin
  PUBLIC_HEADER DESTINATION include/${CMAKE_PROJECT_NAME}/${PROJECT_NAME}
  )

the main.cpp for completeness:

#include <opencv2/viz.hpp>

int main(){}

but it seems that the include_directories and add_library calls inside deps/CMakeLists.txt do not work on the correct scope as i am getting the following error messages:

error C1083: File (Include) can not be opened: "opencv2/viz.hpp"

if i uncomment the include_directories inside the main CMakeLists.txt then i get a linker error (this is not what i want, included directories should be specified inside deps/CMakeLists.txt):

LNK1181: opencv_core.lib can not be opened

If i just copy the contents of deps/CMakeLists.txt in the main CMakeLists.txt in place of the add_subdirectory call everything works fine.

So, how do i get the include directories and the created targets from the subdirectory in my main CMakeLists?

edit:

call to cmake configure:

cmake.exe -B build -S . -G "Visual Studio 17 2022" -A x64 -T v141 -DCMAKE_INSTALL_PREFIX=D:/test

call to cmake build:

cmake.exe --build build --config Release --target install
IjonTichy
  • 309
  • 1
  • 11
  • You didn't describe (I do not see any `cmake ....` commands). How did you run `cmake`? How did you configured build? How did you build, did you performed any install step? – Marek R Jul 11 '22 at 09:07
  • i edited the cmake calls in @MarekR – IjonTichy Jul 11 '22 at 09:13
  • I have different error: `C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.29.30133\include\xtree(1576,1): error C3848: expression having type 'const vtkLabelHierarchy::Imple mentation::PriorityComparator' would lose some const-volatile qualifiers in order to call 'bool vtkLabelHierarchy::Implementation::PriorityComparator::operator ()(const vtkIdType &, const vtkIdType &)' [C:\Users\User\opencvSO_magic\build\deps\vtklib-prefix\src\vtklib-build\Rendering\Label\vtkRenderingLabel.vcxproj] [C:\Users\User\opencvSO_ma gic\build\deps\vtklib.vcxproj]`. :/ – Marek R Jul 11 '22 at 12:56
  • I got this error too when i did not set the CXX Standard. VTK needs CXX11 i think. But honestly i am not quite sure here. I think when i remove the `set(CMAKE_CXX_FLAGS ...)` i will get this error too. I am currently running a test build so i cannot check immediatly... As you might have found out VTKs build time is exhaustive :( – IjonTichy Jul 11 '22 at 13:08
  • No, this is not that, you have: `set(CMAKE_CXX_STANDARD 17)`. – Marek R Jul 11 '22 at 13:45

2 Answers2

7

Unlike to normal targets, which are global, an IMPORTED target by default is local to the directory where it is created.

For extend visibility of the IMPORTED target, use GLOBAL keyword:

add_library(opencv_core SHARED IMPORTED GLOBAL)

This is written in the documentation for add_library(IMPORTED):

The target name has scope in the directory in which it is created and below, but the GLOBAL option extends visibility.

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • Thanks that works. nvm the first comment. I was looking at the docs for add_subdirectory and did not find anything relevant there. – IjonTichy Jul 11 '22 at 12:54
1

cmake has couple steps:

  • configuration
  • generation (depends on configuration)
  • build (depends on generation)
  • test (depends on build)
  • install (depends on build)

Now problem is that your build step depends on result of install step. This happens here:

set(ocv_libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_VS_PLATFORM_NAME}/vc15)

and when this variable is used.

This is causing that to be able to complete build step you need success in install step. And cmake will do install step after successful build. So you have dependency cycle.

Instead importing opencv as shared library:

add_library(opencv_viz SHARED IMPORTED)

Link your target with targets created by imported project of opnecv.

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • ocv_libdir is available at configure time, i know where the install location is thats why this works. I plan to use ExternalProject_get_property for this but havent quite gotten it to work. Like i wrote in the post if i drop `add_subdirectory` call and copy the contents of `deps/CMakeLists` in the main `CMakeLists` then everything works. The problem is that ExternalProject does not populate the namespace with the appropriate targets. See also: https://stackoverflow.com/a/15176075/10825362. Also, i just found this https://crascit.com/2015/07/25/cmake-gtest/ which i am trying out now. – IjonTichy Jul 11 '22 at 09:59
  • `ocv_libdir is available at configure time` so what? How this relates to my answer? This variable is something which participate in dependency cycle, but its lifetime is irrelevant. Problem is target `opencv_viz` (which uses this variable) which depends on products of `installation`. Note `installation` step is not invoked since respective library can't be found (since it is copied in `install` step). – Marek R Jul 11 '22 at 10:23
  • In your post you said the problem was the variable ocv_libdir and when the variable is being used. Nethertheless ExternalProject install step is done during the main projects build step (afaik). So the problem you are describing is not the issue. the issue is that the `IMPORTED SHARED` library targets `opencv_core` and `opencv_viz` created in the `deps/CMakeLists.txt` are not available in the main `CMakeLists.txt`. Otherwise the LNK error message would be `opencv_viz346.lib not found` and not `opencv_viz.lib not found`. – IjonTichy Jul 11 '22 at 10:43