I am building a C++ library called alpha in Ubuntu with cmake, which contains one source file:
cmake_minimum_required(VERSION 2.8)
project(Alpha)
add_library (alpha alpha.cpp)
This creates a file called libalpha.a, which I now want to link to. So, I copy it into the source directory of another C++ projected called beta, which also contains one source file:
cmake_minimum_required(VERSION 2.8)
project(Beta)
add_executable(beta beta.cpp)
target_link_libraries(beta alpha)
However, I get the following error:
/usr/bin/ld: cannot find -lalpha
The same thing happens if I use the line:
target_link_libraries(beta libalpha.a)
Why can beta not find the alpha library?
If you wish to build the library and the program completely separately, you have to use imported targets. When you try to link your executable against a "completely unknown" library, CMake build system automatically passes the task of locating the library to the linker, simply adding -lalpha option. When the linker encounters the option it attempts to locate libalpha.so in one of the standard library locations (i.e. /usr/lib/, /usr/local/lib etc) and expectedly fails. You can use an absolute path to the libalpha.a: target_link_libraries(beta /path/to/libalpha.a).
However if you can build things together, this greatly simplifies the task. Consider
<project>/CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
Project(Test)
add_subdirectory(alpha)
add_subdirectory(beta)
<project>/alpha/CMakeLists.txt
cmake_minimum_required(VERSION 2.8.11)
project(alpha)
set(SOURCES alpha.c)
add_library(alpha ${SOURCES})
target_include_directories(
alpha INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>"
)
target_include_directories() with the complex expression within is required to automatically add libalpha include directories to all components which later are linked against libalpha.
<project>/beta/CMakeLists.txt
project(beta)
set(SOURCES beta.c)
add_executable(beta ${SOURCES})
target_link_libraries(beta alpha)
Add this line with the path to alpha-library.
link_directories( <library path> )
Related
I am currently using CMake to create a static library which utilizes a few of the static libraries from OpenCV 4 ( core imgcodecs video highgui imgproc ). My intention is to be able to bundle all of the required OpenCV static libraries into my own library so that I can distribute it as one library. Additionally, I want for the user of my library to not have to install OpenCV 4 on their system (but do not mind if the user has to do simple installs using apt-get install). I know there are tools for bundling static libraries (such as using ar for linux).
However, where I really am having the issue is with all the dependencies of OpenCV (such as libjpeg, libpng, etc). I don't necessarily mind if these libraries are bundled with mine or linked dynamically as they are relatively easy to install (can be installed with sudo apt-get install, whereas opencv4 needs to be built from source).
What is the best way to go about doing this?
This is my current CMakeLists.txt
It is currently working, but that is because I am using find_package(OpenCV REQUIRED) (which defeats the purpose of what I am trying to do). When I remove that line, the linker complains about not being able to find the OpenCV dependencies.
cmake_minimum_required(VERSION 2.8)
project(myproject)
set(CMAKE_CXX_STANDARD 14)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)
find_package(OpenMP REQUIRED)
find_package(OpenCV REQUIRED)
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)
list(APPEND LINKER_LIBS opencv_core opencv_highgui opencv_video opencv_imgcodecs libmxnet.so libncnn.a nlohmann_json::nlohmann_json)
file(GLOB SRC${CMAKE_CURRENT_LIST_DIR}/src/*.cpp${CMAKE_CURRENT_LIST_DIR}/main.cpp)
add_library(myproject ${SRC})
target_link_libraries(myproject ${LINKER_LIBS} ${OpenMP_CXX_FLAGS})
To elaborate on my question. I build my project which generates libmyproject.a. I then take this library and will eventually extract the symbols from the OpenCV libs (libopencv_core.a libopencv_highgui.a libopencv_imgcodecs.a libopencv_video.a) and add them to my lib (for the time being, I have not yet done this step, which is why in the below example I am linking libopencv_*). I then use my library in a new project, for which the CMakeLists.txt is shown below:
cmake_minimum_required(VERSION 2.8)
project(myproject-driver)
set(CMAKE_CXX_STANDARD 14)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)
find_package(OpenMP REQUIRED)
add_executable(myproject-driver main.cpp)
target_link_libraries(myproject-driver myproject libncnn.a ${OpenMP_CXX_FLAGS} libmxnet.so libopencv_core.a libopencv_highgui.a libopencv_imgcodecs.a libopencv_video.a)
Building this generates the following errors:
Linking CXX executable myproject-driver
/usr/bin/ld: /home/nchafni/Cyrus/myproject/lib/libopencv_imgcodecs.a(grfmt_jpeg.cpp.o): undefined reference to symbol 'jpeg_default_qtables##LIBJPEG_8.0'
//usr/lib/x86_64-linux-gnu/libjpeg.so.8: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
How can I fix this. Is there some CMake command which will link all these dependencies for me? Do I need to manually track down each dependency of those libopencv_* libs and link those manually? Once again, this is assuming that the person using libmyproject.a can't use find_package(OpenCV REQUIRED) as it won't be defined as they have not installed OpenCV on their machine.
First of all, don't use the super old and outdated version 2.8 of CMake. CMake 3.x is so much more powerful and pretty straightforward to use.
Some tips for modern CMake.
Don't use file(GLOB), see here why that is.
Don't use directory wide instructions, rather use target instructions, e.g. target_include_directories vs. include_directories.
Don't use string variables like ${<PACKAGE_NAME>_LIBRARIES}, rather use targets, e.g. <Package_NAME>::lib
When using targets instead of string variables, all the properties (including LINK_INTERFACE) of that target will be populated to the library/executable when calling target_link_libraries, so no more include_directories,link_directories, etc.
myproject
cmake_minimum_required(VERSION 3.14)
project(myproject)
set(CMAKE_CXX_STANDARD 14)
find_package(OpenMP REQUIRED)
find_package(OpenCV REQUIRED)
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)
set(SOURCES ...) # list all the source files here
add_library(myproject ${SOURCES})
target_include_directories(myproject PUBLIC # give it a scope
${CMAKE_CURRENT_LIST_DIR}/include
)
target_link_libraries(myproject PUBLIC # give it a scope
opencv_core # using the target, you will get all LINK_LIBRARIES
opencv_highgui
opencv_video
opencv_imgcodecs
libmxnet.so # where is this coming from?
libncnn.a # where is this coming from?
nlohmann_json::nlohmann_json
OpenMP::OpenMP_CXX ## linking against a target, CXX_FLAGS will be populated automatically
)
myprojec-driver
cmake_minimum_required(VERSION 3.14)
project(myproject-driver)
set(CMAKE_CXX_STANDARD 14)
add_executable(myproject-driver main.cpp)
target_link_libraries(myproject-driver PUBLIC # give it a scope
myproject # gets all dependencies through the LINK_INTERFACE
)
Question is: How do I get CMAKE to set/make the CURL_INCLUDE_DIR be "/usr/local/Cellar/curl/7.75.0/include/"?
No matter what I do, CMAKE only finds the Xcode lib version/location.
My CMAKE file contains:
set(CURL_ROOT_DIR /usr/local/Cellar/curl/7.75.0/include/)
find_package(curl REQUIRED)
message(STATUS ">>> CURL Dir Found: " ${CURL_INCLUDE_DIR})
No matter what I've tried, the above CMAKE code will result in a "CURL_INCLUDE_DIR" of
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include
Note, utilizing "include(findcurl)" instead of "find_package(curl REQUIRED)" gives same result.
I've resorted to adding
set(CMAKE_CXX_FLAGS "-I/usr/local/Cellar/curl/7.57.0/include
-L/usr/local/Cellar/curl/7.57.0/lib")
to get CMAKE to actually utilize the lib version/location that I want. Is there a better way that I'm failing to figure out?
The cmake FindCURL module (at least in the version I have, 3.5.1) does not make reference to CURL_ROOT_DIR anywhere, so I don't believe setting that variable will have any effect.
In fact the FindCURL module is really quite basic and doesn't offer any means for customising where to look, which means it will be looking only in "standard" paths (eg /usr/lib, /usr/local/lib etc)
You can, however, use find_library which takes a PATHS argument.
set(CURL_PATH "/usr/local/Cellar/curl/7.75.0")
find_library(
LIB_CURL
NAMES
curl
PATHS
${CURL_PATH}/lib)
At this point you will have a variable ${LIB_CURL} which points to the location of libcurl
Link against the library and set the include path:
You can then just link against ${LIB_CURL} directly, and manually specify the include path for your target too, such as:
add_executable(
foo
main.cpp)
target_link_libraries(
foo
${LIB_CURL})
target_include_directories(
foo
SYSTEM
PRIVATE "${CURL_PATH}/include")
IMPORTED library target:
Another option, if you want to be fancy, is to create an IMPORTED library target using ${LIB_CURL}, and set the INTERFACE_INCLUDE_DIRECTORIES on that target, so consumers get the include path set automatically.
add_library(
libcurl
IMPORTED)
set_target_properties(
libcurl
PROPERTIES
IMPORTED_LOCATION "${LIB_CURL}")
set_target_properties(
libcurl
PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${CURL_PATH}/include")
Now you will just be able to link against target libcurl directly, and your app will automatically have its include paths updated
add_executable(
foo
main.cpp)
target_link_libraries(
foo
libcurl)
I am trying to import OpenCV into my CMakeLists.txt file. Here's the code I'm using:
find_package(OpenCV 3 REQUIRED)
add_library(opencv SHARED IMPORTED)
set_target_properties(opencv PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OpenCV_INCLUDE_DIRS}"
IMPORTED_LOCATION "${OpenCV_LIBS}")
Later in the cmake file, I create a target that links to opencv, like so:
add_executable(sample "src/sample.cpp")
target_link_libraries(sample opencv)
However, this fails to build. I happen to be using Ninja, but it fails with Make too.
Here is the error I am getting when I try to build with Ninja:
ninja: error: stat(opencv_viz;opencv_videostab;opencv_videoio;opencv_video;opencv_superres;opencv_stitching;opencv_shape;opencv_photo;opencv_objdetect;opencv_ml;opencv_imgproc;opencv_imgcodecs;opencv_highgui;opencv_flann;opencv_features2d;opencv_cudev;opencv_cudawarping;opencv_cudastereo;opencv_cudaoptflow;opencv_cudaobjdetect;opencv_cudalegacy;opencv_cudaimgproc;opencv_cudafilters;opencv_cudafeatures2d;opencv_cudacodec;opencv_cudabgsegm;opencv_cudaarithm;opencv_core;opencv_calib3d): File name too long
Clearly the value of ${OpenCV_LIBS} is a list of values, and that list is getting passed as one long string instead of being split up and linked as individual args to the compiler.
I have verified that other libraries fail too if the IMPORTED_LOCATION is a list of values (separated by semi-colon) instead of just being a single path to a library.
What am I doing wrong?
The problem is that a shared imported library will be looking for one file to link against.
To solve your problem, I would use an interface library instead and set its dependencies:
find_package(OpenCV 3 REQUIRED)
add_library(opencv INTERFACE )
target_include_directories(opencv INTERFACE "${OpenCV_INCLUDE_DIRS}")
target_link_libraries(opencv INTERFACE "${OpenCV_LIBS}")
The target_include_directories may even not be necessary depending on your CMake version.
oLen's answer will work, but then you lose the ability to mark the library as IMPORTED.
A better way to accomplish what I was trying to do it to set the INTERFACE_LINK_LIBRARIES property. The library should also be an INTERFACE library instead of SHARED.
So the cmake config to import the target becomes:
find_package(OpenCV 3 REQUIRED)
add_library(opencv INTERFACE IMPORTED)
set_target_properties(opencv PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${OpenCV_INCLUDE_DIRS}"
INTERFACE_LINK_LIBRARIES "${OpenCV_LIBS}")
I would like to create two executables: one executable for the application, and one for the testing of the application. To that end, I have the following in my CMakeLists.txt file:
include_directories(include)
file(GLOB SOURCE "src/*.cc")
file(GLOB TEST "test/*.cc")
add_executable(interest_calc ${SOURCE})
add_executable(interest_calc_test "src/interest_calc.cc" ${TEST})
Since both src and test directories contain main functions, I have to manually add source files to the "test" executable. Is there another, non-manual, way to add required source files to the "test" executable?
Further, is there a better way to test functionality than creating a separate test executable? If so, what/how?
One way to improve your process would be to pull the guts of your executable into a library, then have a nominal "main" executable which just calls into your library and a "test" executable which exercises the library however you want to test it.
This way, any changes you need to make go into the library and the executable build process is untouched.
Edit to show CMake with your example:
include_directories(include)
file(GLOB SOURCE "src/*.cc")
# Remove main from library, only needed for exec.
list(REMOVE_ITEM SOURCE "main.cc")
file(GLOB TEST "test/*.cc")
add_library(interest_calc_lib STATIC ${SOURCE})
add_executable(interest_calc "main.cc")
target_link_libraries(interest_calc interest_calc_lib)
add_executable(interest_calc_test ${TEST})
target_link_libraries(interest_calc_test interest_calc_lib)
There are already some good answers from Soeren and mascoj but I would like to give a more concrete recommendation.
When you already have a CMakeLists.txt for your executable and you like to add testing, I recommend adding a static dummy library. This library can have all the sources of the executable except the main method (it may be easiest to single out the main method in a separate file if you do not have that already). Using a static library will give you two benefits:
The final executable will behave exactly as your current one, so there is no need to deal with distribution of a new, shared library
You do not need to deal with exporting symbols or throwing exceptions across shared object boundaries
The changes to your CMakeLists.txt can be quite small. I will give an example here, assuming you use cmake 3.0 or newer. First, an example of CMakeLists.txt before adding the dummy library:
project(MyProject)
set(SOURCES src/First.cc src/Second.cc src/Third.cc)
add_executable(${PROJECT_NAME} ${SOURCES} src/Main.cc)
target_include_directories(${PROJECT_NAME}
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR})
target_compile_options(${PROJECT_NAME}
$<$<CXX_COMPILER_ID:GNU>:-Wall;-pedantic)
target_compile_definitions(${PROJECT_NAME}
$<$<CONFIG:Debug>:DEBUG;_DEBUG>)
set_target_properties(${PROJECT_NAME}
PROPERTIES CXX_STANDARD 14)
target_link_libraries(${PROJECT_NAME}
Threads::Threads)
To add the dummy library and testing, you need to introduce a new target with a different name. I choose here to use ${PROJECT_NAME}_lib because this will be very non-intrusive on the CMakeLists.txt. Here is the updated version. Notice the use of ${PROJECT_NAME}_lib in place of ${PROJECT_NAME} in almost all places. Most properties are now passed down to the executable by making them PUBLIC. Only calls to set_target_properties() are not transitive and must be duplicated for library and executable.
project(MyProject)
set(SOURCES src/First.cc src/Second.cc src/Third.cc)
add_library(${PROJECT_NAME}_lib STATIC ${SOURCES})
add_executable(${PROJECT_NAME} src/Main.cc)
target_include_directories(${PROJECT_NAME}_lib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_BINARY_DIR})
target_compile_options(${PROJECT_NAME}_lib PUBLIC
$<$<CXX_COMPILER_ID:GNU>:-Wall;-pedantic)
target_compile_definitions(${PROJECT_NAME}_lib PUBLIC
$<$<CONFIG:Debug>:DEBUG;_DEBUG>)
set_target_properties(${PROJECT_NAME}_lib
PROPERTIES CXX_STANDARD 14)
set_target_properties(${PROJECT_NAME}
PROPERTIES CXX_STANDARD 14)
target_link_libraries(${PROJECT_NAME}
${PROJECT_NAME}_lib
Threads::Threads)
Now you can link your tests against ${PROJECT_NAME}_lib with a different main method.
You could do like this :
In the current CMakeLists.txt, put these lines :
add_subdirectory(src)
add_subdirectory(test)
and then, in each directories add a CMakeLists.txt that link correctly sources to each files.
About test, I've heard that CMake can do test automation, but I don't really know how it works.
In my opinion the best solution is to create a library (shared or static) and two executables (one for the main program and one for the test main). After that you should link the library against the two applications.
In this answer I write down a explanation with a little example how you could managed the project with cmake.
I would like to use CMake to link my project to my shared library. The library is only shared between a handful of projects and is rather small, so I would really like to build it before it is linked. Building it every time seems a better idea than having to maintain an up-to-date precompiled version, because I ten to change it together with the project. It is separate, because it contains stuff I will almost certainly need in the next project.
How can I configure CMake to do it?
My current CMakeLists.txt for the relevant project looks like this:
find_package( Boost REQUIRED COMPONENTS unit_test_framework)
include_directories(${BaumWelch_SOURCE_DIR}/../../grzesLib/src
${BaumWelch_SOURCE_DIR}/src
${Boost_INCLUDE_DIRS})
if(CMAKE_COMPILER_IS_GNUCXX)
add_definitions(-g -std=c++11 -Wall -Werror -Wextra -pedantic -Wuninitialized)
endif()
# Create the unit tests executable
add_executable(
baumwelchtests stateindextest.cpp baumiterationtest.cpp baumwelchtest.cpp sampleparameters.cpp sdetest.cpp
# Key includes for setting up Boost.Test
testrunner.cpp
# Just for handy reference
exampletests.cpp
)
# Link the libraries
target_link_libraries( baumwelchtests ${Boost_LIBRARIES} baumwelchlib grzeslib)
but obviously the compilation fails with:
/usr/bin/ld: cannot find -lgrzeslib
You mentioned you'd like to build the library rather than use a precompiled version. If the library has a CMakeList, you should add it using add_subdirectory(path/to/the/library/source/directory). It will then become a subproject of your project and you can use names of its targets normally in your CMakeList.
Note that while the command is called add_subdirectory, it can be an arbitrary directory on disk; it doesn't have to be a subdirectory of the master project's source dir. In case it's not a subdirectory, you have to explicitly specify a binary directory for it as well. Example:
add_subdirectory(/path/to/the/library/source/directory subproject/grzeslib)
The second argument, if given as a relative path, is interpreted relative to CMAKE_CURRENT_BINARY_DIR.