I'm currently writing an application which will make use of OpenCV and a few other dependencies using cmake. For the sake of the question only one dependency is relevant.
How can I construct a CmakeLists.txt file which downloads the OpenCV source from github and prepares it for static use within my application?
I have tried using ExternalProject_Add which looked promising until I realised the static libraries, etc are not available when they are needed (as far as i understand). I've had similar issues with FetchContent_Declare and related functions.
I understand I can build all my dependencies outside of the single CmakeLists.txt and use them with something like: find_package( OpenCV REQUIRED PATHS "${CMAKE_CURRENT_LIST_DIR}/../build/opencv" ). This works perfectly fine, however I'm hoping to have everything self contained within cmake so anyone that checks out my application wont have to do any special configuration or dependency fetching. Just cmake build and they would be ready to go.
Note:
I've tried many things and followed many blog posts over the past week to try and figure these things out. The post would be excruciatingly long if I were to document them all. At this point im very confused with how it should all work and im hoping someone can point out something obvious im missing.
I'm also aware i can use something like vcpkg instead of compiling these applications from source. Due to the dependencies not listed I am not going down this route.
Environment:
Windows 10
Visual Studio 2019
Cmake
CmakeLists.txt
cmake_minimum_required(VERSION 3.19)
# set the project name
project(test-depends)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set(CMAKE_DEBUG_POSTFIX d)
# Global settings
set(GLOBAL_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
# dependencies
include(ExternalProject)
## other dependencies are here
# ......
## OpenCV contrib
ExternalProject_Add(opencv_contrib
GIT_REPOSITORY https://github.com/opencv/opencv_contrib.git
GIT_TAG 4.5.0
BUILD_COMMAND ""
CONFIGURE_COMMAND ""
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(opencv_contrib SOURCE_DIR)
set(OPENCV_CONTRIB_SOURCE_DIR ${SOURCE_DIR})
# OpenCV
ExternalProject_Add(opencv
GIT_REPOSITORY https://github.com/opencv/opencv.git
GIT_TAG 4.5.0
DEPENDS opencv_contrib # other dependencies omitted
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX=${GLOBAL_OUTPUT_PATH}/opencv
-DBUILD_SHARED_LIBS=OFF
-DOPENCV_EXTRA_MODULES_PATH=${OPENCV_CONTRIB_SOURCE_DIR}/modules
-DINSTALL_CREATE_DISTRIB=ON
-DBUILD_EXAMPLES=OFF
-DWITH_CUDA=OFF
-DBUILD_DOCS=OFF
-DDENABLE_CXX11=ON
-DDBUILD_TESTS=OFF
-DBUILD_PERF_TESTS=OFF
)
ExternalProject_Get_Property(opencv SOURCE_DIR BINARY_DIR)
set(OPENCV_SOURCE_DIR ${SOURCE_DIR})
set(OPENCV_BINARY_DIR ${BINARY_DIR})
file(GLOB_RECURSE PROJECT_SOURCES "src/*.h" "src/*.cpp")
add_executable(${PROJECT_NAME} ${PROJECT_SOURCES})
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
add_dependencies(${PROJECT_NAME} opencv)
include_directories(${GLOBAL_OUTPUT_PATH}/opencv/include)
target_link_directories( ${PROJECT_NAME}
PRIVATE ${OPENCV_BINARY_DIR}/lib
)
set(OpenCV_LIBS "ade;opencv_aruco450d;opencv_bgsegm450d;opencv_bioinspired450d;opencv_calib3d450d;opencv_ccalib450d;opencv_core450d;opencv_datasets450d;opencv_dnn450d;opencv_dnn_objdetect450d;opencv_dnn_superres450d;opencv_dpm450d;opencv_face450d;opencv_features2d450d;opencv_flann450d;opencv_fuzzy450d;opencv_gapi450d;opencv_hfs450d;opencv_highgui450d;opencv_imgcodecs450d;opencv_imgproc450d;opencv_img_hash450d;opencv_intensity_transform450d;opencv_line_descriptor450d;opencv_mcc450d;opencv_ml450d;opencv_objdetect450d;opencv_optflow450d;opencv_phase_unwrapping450d;opencv_photo450d;opencv_plot450d;opencv_quality450d;opencv_rapid450d;opencv_reg450d;opencv_rgbd450d;opencv_saliency450d;opencv_shape450d;opencv_stereo450d;opencv_stitching450d;opencv_structured_light450d;opencv_superres450d;opencv_surface_matching450d;opencv_text450d;opencv_tracking450d;opencv_video450d;opencv_videoio450d;opencv_videostab450d;opencv_xfeatures2d450d;opencv_ximgproc450d;opencv_xobjdetect450d;opencv_xphoto450d;")
target_link_libraries(
${PROJECT_NAME}
PRIVATE "${OpenCV_LIBS}"
)
src/main.cpp
#include <iostream>
#include "opencv2/opencv.hpp"
void main() {
cv::Mat testing;
std::cout << "hello" << std::endl;
}
Related
I'm trying to include opencv in my c++ project. I want CMake to handle this for me.
Currently I'm at the point where I need to include opencv with the tag: #include <opencv2/opencv.hpp>
The files in the _deps/opencv-src directory throw the following error though:
Scanning dependencies of target VisionC
Building CXX object CMakeFiles/VisionC.dir/main.cpp.o
In file included from /Users/koen/Vakken/MotionVision/VisionC/main.cpp:2:
/Users/koen/Vakken/MotionVision/VisionC/cmake-build-debug/_deps/opencv-src/include/opencv2/opencv.hpp:48:10: fatal error: 'opencv2/opencv_modules.hpp' file not found
#include "opencv2/opencv_modules.hpp"
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
It seems Like the files can't include their own headers
My CMakeLists file is still pretty simple:
cmake_minimum_required(VERSION 3.17)
project(VisionC)
set(CMAKE_CXX_STANDARD 20)
include(FetchContent)
FetchContent_Declare(
opencv
GIT_REPOSITORY https://github.com/opencv/opencv.git
GIT_TAG 4.4.0
)
FetchContent_GetProperties(opencv)
if (NOT opencv_POPULATED)
FetchContent_Populate(opencv)
add_subdirectory(${opencv_SOURCE_DIR} ${opencv_BINARY_DIR})
include_directories(${opencv_SOURCE_DIR}/include) # "/include" should be deleted somehow...
endif ()
FetchContent_MakeAvailable(opencv)
add_executable(VisionC main.cpp)
target_link_libraries(VisionC opencv_lib)
I think the "/include" in the include_directories line hints that the library is included in a directory to "high" or so... I'm not sure how I should change this. If I delete this line I have to include opencv like #include <include/opencv2/opencv.hpp>
I found the solution, this is my cmakelists now:
cmake_minimum_required(VERSION 3.17)
project(VisionC)
set(CMAKE_CXX_STANDARD 20)
# Fetch from git
include(FetchContent)
FetchContent_Declare(
opencv
GIT_REPOSITORY https://github.com/opencv/opencv.git
GIT_TAG 4.4.0
)
FetchContent_GetProperties(opencv)
if (NOT opencv_POPULATED)
FetchContent_Populate(opencv)
endif ()
FetchContent_MakeAvailable(opencv)
# Find on pc
set(OpenCV_DIR ${CMAKE_CURRENT_BINARY_DIR})
include_directories(${OpenCV_INCLUDE_DIRS})
find_package(OpenCV REQUIRED)
# Link
add_executable(VisionC main.cpp)
target_link_libraries(VisionC ${OpenCV_LIBS})
The solution provided by #Typhaon is not a solution at all because it uses find_package alongside FetchContent and only uses the result of the find_package ignoring the fetched content. In, other words if you remove FetchContent usages it will work. If anyone is wondering what is the proper integration of OpenCV with find_package, take a look at the example provided by OpenCV https://github.com/opencv/opencv/blob/4.x/samples/cpp/example_cmake/CMakeLists.txt.
So, what about fetch_content or even using add_subdriectory?
That kind of usage is currently unsupported
https://github.com/opencv/opencv/issues/20548
But, I currently found a workaround that works for me with the current release. So the problem is that the targets of the OpenCV modules do not include in them the header include dir so when you are trying to link with them you can but you also need to specify the include directory manually. Currently, there are OPENCV_MODULE_opencv_{module_name}_LOCATION variables that point to the source directory of the module and they can be used for obtaining the include dir. For example
target_include_directories(my_target PRIVATE
${OPENCV_MODULE_opencv_core_LOCATION}/include
${OPENCV_MODULE_opencv_highgui_LOCATION}/include
)
target_link_libraries(my_target opencv_core opencv_highgui)
After this, there is an error like this
52 | #include "opencv2/opencv_modules.hpp"
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
And this is because for some reason the opencv_modules.hpp is being generated in the root of the build directory. But fortunately, there is a variable that is pointing to that directory OPENCV_CONFIG_FILE_INCLUDE_DIR. So the final CMake list file looks like the following
cmake_minimum_required(VERSION 3.23)
project(my_project)
set(CMAKE_CXX_STANDARD 20)
include(FetchContent)
FetchContent_Declare(
opencv
GIT_REPOSITORY https://github.com/opencv/opencv.git
GIT_TAG 4.6.0
GIT_SHALLOW TRUE
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(opencv)
add_executable(my_target main.cpp)
target_include_directories(my_target PRIVATE
${OPENCV_CONFIG_FILE_INCLUDE_DIR}
${OPENCV_MODULE_opencv_core_LOCATION}/include
${OPENCV_MODULE_opencv_highgui_LOCATION}/include
)
target_link_libraries(my_target opencv_core opencv_highgui)
The downside of this workaround is that the mentioned variables can change over the releases, and this currently works for 4.6.0.
I have a little Hello, World style program which I'd like to work on more in end-to-end fashion using CMake. In this case it means I could download Boost libraries, compile them and finally compile my little Hello, World and link the Boost libraries to it.
The Directory layout looks like this currently
cmaketest
|- build //I would like to have here the CMake generated IDE files.
|- src
|- main.cpp
|- hello.cpp
|- hello.hpp
|- depends //I would like to have here the built Boost libraries and headers.
|- downloads //This contains the downloaded zip file.
|- temp //Temporary files.
I have the following CMakeLists.txt, and currently I generate the Visual Studio 2015 project files like so: cmake build -G "Visual Studio 14 2015 Win64". All good and well, I'll start the solution, CMake complains it can't find Boost, goes to download it starts to build (in the following code I've set to fetch a file previously acquired). But then after building for a while, the problems emerge...
The Boost files seem to appear in \cmaketest\CMakeFiles\build\Boost-prefix\src\Boost as the root. How could I these so they'd be built to \depends\boost (or to that effect) and how to include the libraries to be linked to the solution? This linking is a separate problem I have, I know how to include the headers from this strange directory, but it's not really what I want.
It looks like the VS IDE gives first warning the Boost headers aren't to be found (see the main program shortly) first and then starts the Boost build. Can this be avoided?
How to make the Boost library disappear from the VS solution? I.e. not to make a project of it, but just a dependency on headers and libraries?
Is there a built-in way to avoid downloading the Boost package if it is already on the disk? It looks like it will be retrieved in any event, and I've thought about checking the existence of the file.
The main.cpp (I suppose this is the relevant file in this case)
//The Boost headers are included just to check the linker.
#include "hello.hpp"
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <iostream>
using std::cout;
using std::endl;
int main()
{
hello helloer;
cout << helloer.greet("World here") << endl;
return EXIT_SUCCESS;
}
And then the CMakeLists.txt file.
cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
set(CMAKE_VERBOSE_MAKEFILE ON)
project(Cmaketest)
if(POLICY CMP0042)
cmake_policy(SET CMP0042 NEW)
endif()
if(POLICY CMP0066)
cmake_policy(SET CMP0066 NEW)
endif()
# Default build type if none was specified.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
message(STATUS "Setting build type to '${CMAKE_BUILD_TYPE} as none was specified.")
# Possible values of build type for CMake GUI.
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
# The path to extra modules.
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
# Setup build locations.
if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif()
set(CMAKE_CURRENT_BINARY_DIR
${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/build)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
include(ExternalProject)
include(ProcessorCount)
ProcessorCount(N)
if(NOT N EQUAL 0)
set(CMAKE_BUILD_FLAGS -j${N})
endif()
# set(BOOST_VERSION 1.63.0)
# Boost related settings:
# https://cmake.org/cmake/help/v3.6/module/FindBoost.html.
# Should one check the environment variables and flags here explicitly
# to remove the complaint about missing Boost library? Or should perhaps
# BOOST_ROOT be set? Point to what?
find_package(Boost)
if(NOT Boost_FOUND)
# It shouldn't hurt to enable extensive diagnostics, just in case.
# Also, a different set of files is downloaded for UNIX and WIN32
# due to file permissions and line-feeds (Windows should handle
# also Unix style line-feeds).
set(Boost_DETAILED_FAILURE_MSG on)
add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS})
set(Boost_Bootstrap_Command)
if(UNIX)
set(Boost_Url "http://sourceforge.net/projects/boost/files/boost/1.63.0/boost_1_63_0.tar.gz")
set(Boost_Sha1 "2cecf1848a813de55e5770f324f084c568abca0a")
set(Boost_Bootstrap_Command ./bootstrap.sh)
set(Boost_b2_Command ./b2)
elseif(WIN32)
set(Boost_Url "http://sourceforge.net/projects/boost/files/boost/1.63.0/boost_1_63_0.zip")
set(Boost_Sha1 "4364989afbe6b11f2d5e59df902c3ca4d7851824")
set(Boost_Bootstrap_Command bootstrap.bat)
set(Boost_b2_Command b2.exe)
endif()
set(Config_Libraries "chrono,filesystem,program_options,system,thread,test")
ExternalProject_Add(Boost
TMP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/temp"
DOWNLOAD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/downloads"
URL "${CMAKE_CURRENT_SOURCE_DIR}/downloads/boost_1_63_0.zip"
URL_HASH "SHA1=${Boost_Sha1}"
BUILD_IN_SOURCE 1
UPDATE_COMMAND ""
PATCH_COMMAND ""
CONFIGURE_COMMAND ${Boost_Bootstrap_Command} --without-icu --with_libraries=${Config_Libraries}
BUILD_COMMAND ${Boost_b2_Command}
# --prefix="${CMAKE_CURRENT_SOURCE_DIR}/depends"
--without-python
--address-model=64
--architecture=x64
--threading=multi
--link=static
--variant=release
--layout=tagged
--build-type=complete
-j${N}
INSTALL_COMMAND ""
# INSTALL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/depends"
#As it happens, these directories are currently empty...
# set(BOOST_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/depends/include/boost-1_63)
# set(Boost_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/depends/boost_1_63/include)
# set(Boost_LIBRARY ${CMAKE_CURRENT_SOURCE_DIR}/depends/boost_1_63/lib)
)
endif()
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
include_directories(${CMAKE_SOURCE_DIR}/src)
# Grabbing just all the test project files.
file(GLOB SOURCES "src/*.*")
add_executable(cmaketest ${SOURCES})
# TARGET_LINK_LIBRARIES(cmaketest ${Boost_LIBRARY})
I will attempt to give you some hints, to try to answer your questions.
1) With this snippet you should be able to have sources and built files in the right directories. I kept TMP_DIR "${Cmaketest_SOURCE_DIR}/temp" but in my project I don't use that line.
ExternalProject_Add(Boost
TMP_DIR "${Cmaketest_SOURCE_DIR}/temp"
DOWNLOAD_DIR "${Cmaketest_SOURCE_DIR}/downloads"
URL "${Cmaketest_SOURCE_DIR}/downloads/boost_1_63_0.zip"
URL_HASH "SHA1=${Boost_Sha1}"
SOURCE_DIR ${Cmaketest_SOURCE_DIR}/downloads/boost_1_63_0
BUILD_IN_SOURCE 1
UPDATE_COMMAND ""
PATCH_COMMAND ""
CONFIGURE_COMMAND ${Boost_Bootstrap_Command} --without-icu --with_libraries=${Config_Libraries}
BUILD_COMMAND ${Boost_b2_Command}
--prefix="${Cmaketest_SOURCE_DIR}/depends/boost_1_63_0"
--without-python
--address-model=64
--architecture=x64
--threading=multi
--link=static
--variant=release
--layout=tagged
--build-type=complete
-j${N}
INSTALL_COMMAND ${Boost_b2_Command} --prefix="${Cmaketest_SOURCE_DIR}/depends/boost_1_63_0" --without-python
--address-model=64
--architecture=x64
--threading=multi
--link=static
--variant=release
--layout=tagged
--build-type=complete
-j${N} install
INSTALL_DIR "${Cmaketest_SOURCE_DIR}/depends/boost_1_63_0"
)
set(BOOST_ROOT ${Cmaketest_SOURCE_DIR}/depends/boost_1_63_0)
set(Boost_LIBRARY ${Cmaketest_SOURCE_DIR}/depends/boost_1_63_0/lib)
set(Boost_INCLUDE_DIR ${Cmaketest_SOURCE_DIR}/depends/boost_1_63_0/include)
include_directories(${Boost_INCLUDE_DIR})
To notice that you were not calling "install" so once built in that "strange" directory, the files were not moved to the specified prefix. Notice also the use of ${Cmaketest_SOURCE_DIR} instead of the previous.
Try to print both and see if there's a difference.
2) To address the warning you get in case Boost is not found, you should write:
find_package(Boost QUIET)
3) To address this I guess you could not generate project files, i.e. instead of having
cmake build -G "Visual Studio 14 2015 Win64"
just have cmake . or cmake .. depending whether you will do an in-source or an out-of-source build. I suggest doing out-of-source builds, but that's your choice.
4) FindBoost.cmake (FindBoost.cmake) gives you the possibility to pass an arbitrary path to a Boost directory. You could use that at configuration to hint CMake to look for Boost in the location you give, e.g. you would call cmake in the following way:
cmake -DBOOST_ROOT=/path_to_cmaketest_dir/depends/boost_1_63_0 ..
and if your system might be picky you could specify all things:
cmake -DBOOST_ROOT=/path_to_cmaketest_dir/depends/boost_1_63_0 -DBOOST_LIBRARYDIR=/path_to_cmaketest_dir/depends/boost_1_63_0/lib DBOOST_INCLUDEDIR=/path_to_cmaketest_dir/depends/boost_1_63_0/include ..
Notice that in the last two snippets I assumed configuration of an out-of-source build.
Also notice that if you already installed or have the Boost package downloaded, using ${Cmaketest_SOURCE_DIR} in the snipped of code in 1) should solve also 4) so when it will not find Boost it will try to download but it will not, since now cmake should be able to see the file (.zip).
Please see CMaker_Boost, build the Boost with the CMake at a configure time. Now it is tested on the Linux and Android, gcc and clang. Other systems are not tested. I hope this helps.
I faced with a problem when I was trying to use Google Test.
There are lot of manuals on how to use ExternalProject_Add for the adding gtest into the project, however most of these describe a method based on downloading zip archive with gtest and build it.
As we know gtest is github-hosted and cmake-based project. So I'd like to find native cmake way.
If this would be a header-only project, I'd write something like:
cmake_minimum_required(VERSION 2.8.8)
include(ExternalProject)
find_package(Git REQUIRED)
ExternalProject_Add(
gtest
PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/ext
GIT_REPOSITORY https://github.com/google/googletest.git
TIMEOUT 10
UPDATE_COMMAND ${GIT_EXECUTABLE} pull
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD ON
)
ExternalProject_Get_Property(gtest source_dir)
set(GTEST_INCLUDE_DIR ${source_dir}/googletest/include CACHE INTERNAL "Path to include folder for GTest")
set(GTEST_ROOT_DIR ${source_dir}/googletest CACHE INTERNAL "Path to source folder for GTest")
include_directories(${INCLUDE_DIRECTORIES} ${GTEST_INCLUDE_DIR} ${GTEST_ROOT_DIR})
message(${GTEST_INCLUDE_DIR})
and add this script from my cmake project like:
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake.modules/")
include(AddGTest)
....
add_dependencies(${PROJECT_NAME} gtest)
However this requires a build step.
How should this be implemented?
By adding BUILD_COMMAND into ExternaProject_Add and linking with produced libs?
Or by including gtest's cmakelists into my project, something like this:
add_subdirectory (${CMAKE_SOURCE_DIR}\ext\src\gtest\googletest\CMakeLists.txt)
this is not correct way because on the moment of the project load the folder does not exist.
Any other ways?
What is a correct/prefer way?
I would go with the first approach. You don't need to specify a build command because cmake is used by default. This could look like:
cmake_minimum_required(VERSION 3.0)
project(GTestProject)
include(ExternalProject)
set(EXTERNAL_INSTALL_LOCATION ${CMAKE_BINARY_DIR}/external)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${EXTERNAL_INSTALL_LOCATION}
)
include_directories(${EXTERNAL_INSTALL_LOCATION}/include)
link_directories(${EXTERNAL_INSTALL_LOCATION}/lib)
add_executable(FirstTest main.cpp)
add_dependencies(FirstTest googletest)
target_link_libraries(FirstTest gtest gtest_main pthread)
I don't know if this is the correct/preferred way if there even is one. If you wanted to implement your second approach you would have to download the code with execute_process first.
With the following CMakeLists.txt build script:
include( ExternalProject )
ExternalProject_Add( framework SOURCE_DIR ${framework_SOURCE}
PREFIX framework_build
INSTALL_DIR ${framework_DISTRIBUTION} )
...
add_library( ${PROJECT_NAME} SHARED ${BUILD_MANIFEST} )
add_dependencies( ${PROJECT_NAME} framework )
When I attempt to perform a parallel build (make -j5) it will occasionally fail due to a build artefact from the framework not being present. The order of the build, fixed by add_dependencies, is not being adhered to.
Have I misunderstood the documentation around add_dependencies?
Output from cmake --graphviz=graph.dot
Ok, so an updated version of CMake has warned me that the framework dependency is not present. ExternalProject_Add and add_dependencies can not be used with each other, as ExternalProject_Add has not actually built and therefore registered the framework as a high-level target.
Note:
Anyone encountering this problem in future. I've found another SO post by #matiu that resolved my issue.
Maybe ExternalProject_Add_StepDependencies could solve that and create a dependency between the externalproject_add and the imported target?
This is a minimal working example adding Google test as a dependency.
cmake_minimum_required(VERSION 2.8)
project(ExampleProject)
# Set the build type if it isn't already
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif()
include(ExternalProject)
set(GOOGLE_TEST GoogleTest)
set(GTEST_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/${GOOGLE_TEST}")
ExternalProject_Add(${GOOGLE_TEST}
GIT_REPOSITORY https://chromium.googlesource.com/external/googletest
PREFIX ${GTEST_PREFIX}
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ""
)
# Specify include directory
ExternalProject_Get_Property(${GOOGLE_TEST} source_dir)
include_directories(${source_dir}/include)
set(LIBPREFIX "${CMAKE_STATIC_LIBRARY_PREFIX}")
set(LIBSUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}")
set(GTEST_LOCATION "${GTEST_PREFIX}/src/${GOOGLE_TEST}-build")
set(GTEST_LIBRARY "${GTEST_LOCATION}/${LIBPREFIX}gtest${LIBSUFFIX}")
set(EXECUTABLE_NAME ${CMAKE_PROJECT_NAME})
add_executable(${EXECUTABLE_NAME} main.cpp)
add_dependencies(${EXECUTABLE_NAME} ${GOOGLE_TEST})
target_link_libraries(
${EXECUTABLE_NAME}
${GTEST_LIBRARY}
-lpthread
)
enable_testing()
set(TEST_NAME ${EXECUTABLE_NAME})
add_test(${EXECUTABLE_NAME} ${TEST_NAME})
And this is the dependency graph:
In this case without add_dependencies a parallel build will always fail, because of missing the dependency.
I have the following situation: a Project A depends on a Project B, but both are built at the same time. Project A has to include the includes of project B and it needs also to link its libraries. Up to now I've tried this way:
ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/other_project other_project)
and then:
INCLUDE_DIRECTORIES(includ ${CMAKE_SOURCE_DIR}/other_project/include})
LIST(APPEND LINK_LIBS other_project)
in the CMakeLists.txt of Project A
but it doesn't seem to work, the compiler also gives me error when including the headers of Project B saying that they do not exist.
What is the right way to add dependencies in A? How should the CMakeLists.txt look like?
EDIT:
as suggested in the comments, this question was addressed in this, however I'd like to see an example of how to use it in a CMakeList.txt file.
The following is a simple example which builds zlib and then builds libxml2 which depends on the zlib we build. One thing to note, I quickly pulled this example from stuff I've done before. The libxml2 example uses make, thus will only actually build on a system which has it, e.g. Linux, Mac ...
Here is the proposed directory structure for this example ...
src/
-- CMakeLists.txt
CMake/
---- External_ZLib.cmake
---- External_libxml2.cmake
Dowloads/ ( no need to create this directory, CMake will do it for you )
build/ ( Out of source build to prevent littering source tree )
Files:
CMakeLists.txt
# Any version that supports ExternalProject should do
cmake_minimum_required( VERSION 3.1)
project(test_ext_proj)
set(test_ext_proj_CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
set(CMAKE_MODULE_PATH ${test_ext_proj_CMAKE_DIR} ${CMAKE_MODULE_PATH})
set(test_ext_proj_BUILD_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install)
set(test_ext_proj_DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Downloads CACHE PATH "Directory to store downloaded tarballs.")
include(ExternalProject)
include(External_ZLib)
include(External_libxml2)
External_ZLib.cmake:
set(ZLib_version 1.2.8)
set(ZLib_url "http://zlib.net/zlib-${ZLib_version}.tar.gz")
set(ZLib_md5 "44d667c142d7cda120332623eab69f40")
ExternalProject_Add(ZLib
URL ${ZLib_url}
URL_MD5 ${ZLib_md5}
PREFIX ${vision-tpl_BUILD_PREFIX}
DOWNLOAD_DIR ${test_ext_proj_DOWNLOAD_DIR}
INSTALL_DIR ${test_ext_proj_BUILD_INSTALL_PREFIX}
CMAKE_GENERATOR ${gen}
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=${test_ext_proj_BUILD_INSTALL_PREFIX}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
)
#This variable is required so other packages can find it.
set(ZLIB_ROOT ${test_ext_proj_BUILD_INSTALL_PREFIX} CACHE PATH "" FORCE)
External_libxml2.cmake:
set(libxml2_release "2.9")
set(libxml2_patch_version 0)
set(libxml2_url "ftp://xmlsoft.org/libxml2/libxml2-sources-${libxml2_release}.${libxml2_patch_version}.tar.gz")
set(libxml2_md5 "7da7af8f62e111497d5a2b61d01bd811")
#We need to state that we're dependent on ZLib so build order is correct
set(_XML2_DEPENDS ZLib)
#This build requires make, ensure we have it, or error out.
if(CMAKE_GENERATOR MATCHES ".*Makefiles")
set(MAKE_EXECUTABLE "$(MAKE)")
else()
find_program(MAKE_EXECUTABLE make)
if(NOT MAKE_EXECUTABLE)
message(FATAL_ERROR "Could not find 'make', required to build libxml2.")
endif()
endif()
ExternalProject_Add(libxml2
DEPENDS ${_XML2_DEPENDS}
URL ${libxml2_url}
URL_MD5 ${libxml2_md5}
PREFIX ${test_ext_proj_BUILD_PREFIX}
DOWNLOAD_DIR ${test_ext_proj_DOWNLOAD_DIR}
INSTALL_DIR ${test_ext_proj_BUILD_INSTALL_PREFIX}
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ./configure
--prefix=${test_ext_proj_BUILD_INSTALL_PREFIX}
--with-zlib=${ZLIB_ROOT}
BUILD_COMMAND ${MAKE_EXECUTABLE}
INSTALL_COMMAND ${MAKE_EXECUTABLE} install
)