cmake: build dependency downloaded with ExternalProject_Add before hand - c++

I'm trying to get my test program main.cpp to link against a an externally downloaded library (in this case fmt 8.1.1) via cmake's 'ExternalProject_Add' function as follows
CMakeLists.txt
cmake_minimum_required(VERSION 3.23)
project(test)
set(CMAKE_CXX_STANDARD 20)
include(ExternalProject)
ExternalProject_Add(fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 8.1.1
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 -DFMT_TEST=OFF -DFMT_DOC=OFF -DFMT_INSTALL=ON -DCMAKE_INSTALL_PREFIX=${CMAKE_BINARY_DIR}/fmt-build
STEP_TARGETS build
)
set(fmt_BINARY_DIR "${CMAKE_BINARY_DIR}/fmt-prefix/src/fmt-build")
set(fmt_SOURCE_DIR "${CMAKE_BINARY_DIR}/fmt-prefix/src/fmt/include")
add_executable(${PROJECT_NAME} main.cpp)
add_dependencies(${PROJECT_NAME} fmt-build) # does not have an effect
target_link_libraries(${PROJECT_NAME} PRIVATE ${fmt_BINARY_DIR}/libfmt.a)
target_include_directories(${PROJECT_NAME} PUBLIC ${fmt_SOURCE_DIR})
main.cpp
#include <chrono>
#include <fmt/core.h>
#include <fmt/chrono.h>
int main() {
const auto now = std::chrono::system_clock::now();
fmt::print("The time is {}", now);
return 0;
}
The test program main.cpp runs OK if I build the target fmt-build before hand, but complains that
ninja: error: 'fmt-prefix/src/fmt-build/libfmt.a', needed by 'test', missing and no known rule to make it
indicating that the dependency fmt-build must be built before hand. While that's doable, how do I get cmake to autmoatically build fmt-build as a dependency for test without haing to run it manually first? I thought the line add_dependencies(${PROJECT_NAME} fmt-build) should take care of that but evidently it does not.

Based on what #Tsyvarev commented, simply adding the line
BUILD_BYPRODUCTS ${CMAKE_BINARY_DIR}/fmt-prefix/src/fmt-build/libfmt.a
to the ExternalProject_Add call does the trick, and one does not have to manually build fmt-build before hand.

Related

Unabled to load lohmann/json.hpp using CMake - getting fatal error: 'nlohmann/json.hpp' file not found

I have the following main.cpp, very simple script, trying to re-produce the problem and isolate to it's most basic.
#include<iostream>
#include<fmt/core.h>
// #include "json/json.hpp"
// #include <json/json.hpp>
// #include <nlohmann/json.hpp>
// #include "json.hpp"
// #include "nlohmann/json.hpp"
int main(){
fmt::print("Hello, world!\n");
return 0;
}
The commented out include statements are all of the paths I have tried to get this into my program.
My CMakeLists.txt file looks like this
cmake_minimum_required(VERSION 3.24)
project(main)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-std=c++17")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build)
# include(FetchContent)
# FetchContent_Declare(
# json
# GIT_REPOSITORY https://github.com/nlohmann/json
# )
# FetchContent_MakeAvailable(json)
include(FetchContent)
FetchContent_Declare(fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG 9.0.0
)
FetchContent_MakeAvailable(fmt)
add_executable(main main.cpp)
# target_link_libraries(main json)
target_link_libraries(main fmt)
I've commented out the json part since it's not working obviously, I can also json-src and json-build in my _deps folder so I know for a fact that it is being downloaded into my local machine.
After running cmake CMakeLists.txt and make and then running the executable I get the error in the title 'nlohmann/json.hpp' file not found
Any help would be appreciated.
Tried to reproduce the example with another popular C++ package which worked fine. Tried changing the include statements to see if maybe I had something wrong since I don't fully understand all the nuance with CMake and am quite new to it.
You can either specifically use the interface target already included in the nlohmann library, which will automatically populate the correct include path for you, with:
target_link_libraries(main nlohmann_json::nlohmann_json)
Or you would need to specifically include the include path yourself:
include_directories(${json_SOURCE_DIR}/include)
With either way, you will be able to use #include <nlohmann/json.hpp> in your source.

Why does "#include <gtest/gtest.h>" fail even after having used "FetchContent_MakeAvailable" in my CMake file?

This is my first time attempting the Google Test API in C++ and one of my first experiences with CMake. If it's useful, I'm using CLion. My CMake file, shown below, should ostensibly allow for files to #include <gtest/gtest.h>:
cmake_minimum_required(VERSION 3.19)
project(chord)
# GoogleTest requires at least C++11
set(CMAKE_CXX_STANDARD 11)
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -pthread")
add_executable(
chord
main.cpp
src/node.h
src/node.cpp
src/log.h
src/log.cpp
src/sha1.h
src/sha1.cpp
src/messageParser.h
src/messageParser.cpp
src/fingerTable.h
src/fingerTable.cpp
src/key.h
src/key.cpp)
include(GoogleTest)
enable_testing()
add_executable(
unitTests
test/fingerTableTests.cc
test/keyTests.cc
test/logTests.cc
test/messageParserTests.cc
test/nodeTests.cc
test/messageParserTests.cc
)
target_link_libraries(
unitTests
gtest_main
)
include(GoogleTest)
gtest_discover_tests(unitTests)
However, when I attempt #include <gtest/gtest.h> from main.cpp, I receive the error gtest/gtest.h: No such file or directory. Can someone pinpoint the error in my CMake file? Thanks and sorry for the trouble.
Try to also link gtest:
target_link_libraries(
unitTests
gtest_main
gtest
)

How to include cmake dependencies from source for static use

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;
}

Including opencv with fetchcontent does not work

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.

Linking freetype with cmake

I'm having troubles with linking freetype 2 under linux using cmake when building a C++11 project with an extern C library.
With cmake and freetype 2 I basically have 2 options :
use the utility freetype-config like freetype-config --libs
use the FindFreetype cmake module
Now I'm trying to implement the second option and I'm not very skilled with cmake nor I understand the logic of it.
My problem is the linking phase, I have no idea how to do that properly plus this module is not as complete as the result of freetype-config --libs which really includes all the libraries and flags that I need, and not just the path of a file; so I'm assuming that I have to do the same for zlib and libpng.
CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
project (FreetypeTutorials1)
include(FindFreetype)
include_directories(${FREETYPE_INCLUDE_DIRS})
SET(CMAKE_CXX_FLAGS "-O2 -std=c++11")
SET(CMAKE_EXE_LINKER_FLAGS "-v -lfreetype")
add_executable( demo "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp")
./src/main.cpp ( just some random code so I have something to feed to the compiler )
extern "C" {
#include <ft2build.h>
#include FT_FREETYPE_H
}
#include <iostream>
int main()
{
FT_Library library;
auto error = FT_Init_FreeType(&library);
if (error)
{
std::cout << "An error !\n";
}
}
To load a module like FindFreetype.cmake you need to use it in cmake with the find_package-command. The first argument is the package name. The "Find" of its corresponding filename is added automatically by cmake.
While include might work with find_package you can add some flags. For example, as shown below, REQUIRED, to make cmake fail when freetype wasn't found.
Additionally linking with cmake should be done with the command target_link_libraries.
This is how I would write you CMakeLists.txt:
cmake_minimum_required (VERSION 2.6)
project (FreetypeTutorials1)
find_package(Freetype REQUIRED)
SET(CMAKE_CXX_FLAGS "-O2 -std=c++11")
SET(CMAKE_EXE_LINKER_FLAGS "-v")
add_executable( demo src/main.cpp) # CMAKE_CURRENT_SOURCE_DIR is implicit here
target_link_libraries(demo ${FREETYPE_LIBRARIES})
target_include_directories(demo PRIVATE ${FREETYPE_INCLUDE_DIRS})
target_link_libraries is platform independent whereas '-lfreetype in CMAKE_EXE_LINKER_FLAGS is not.
The CMakeLists.txt will work on other platforms where freetype is available.
(Edit 2019: use target_include_directories() instead of include_directories()