I'm trying to add a new library to a project built using CMake and am having trouble. I'm trying to follow this. I've made a test project that looks like this:
cmake_test/
test.cpp
CMakeLists.txt
liblsl/
include/
lsl_cpp.h
CMakeLists.txt
liblsl64.dll
liblsl64.so
build/
the CMakeLists.txt in cmake_test looks like this:
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_executable(Tutorial test.cpp)
add_subdirectory(liblsl)
target_link_libraries(Tutorial PUBLIC ${LSL_LIBRARY})
and the CMakeLists.txt in liblsl looks like this:
find_path(LSL_INCLUDE_DIR lsl_cpp.h)
find_library(LSL_LIBRARY liblsl64)
include_directories(${LSL_INCLUDE_DIR})
But I keep getting the error No rule to make target '.../liblsl64.lib', needed by 'Tutorial.exe'. Stop.
Any idea what I'm doing wrong?
I'm on Windows 10 using mingw-w64 v5.4.0 if that makes any difference.
CMakeLists.txt in cmake_test:
cmake_minimum_required(VERSION 3.10)
project(Tutorial VERSION 1.0)
add_subdirectory(liblsl)
add_executable(Tutorial test.cpp)
target_compile_features(Tutorial PUBLIC cxx_std_11)
target_link_libraries(Tutorial PUBLIC liblsl)
CMakeLists.txt in liblsl:
add_library(liblsl SHARED IMPORTED GLOBAL)
set_target_properties(liblsl PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/include")
set_target_properties(liblsl PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/liblsl64.so")
For Windows use:
set_target_properties(liblsl PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/liblsl64.dll")
set_target_properties(liblsl PROPERTIES IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/liblsl64.lib")
In add_library, you say SHARED because your library is a shared one (so/dll), you say IMPORTED because you don't want to build the library, and you say GLOBAL because you want it to be visible outside liblsl.
Related
This question already has answers here:
Undefined reference to function CMake
(2 answers)
Closed last month.
I've looked at many different threads, but nothing seemed to work.
I'm trying to include the NLohmann JSON library using CMake. IntelliSense does autocomplete the filename sometimes, but you see a red or purple error line underneath the include and during compilation it can't find it. GTest does work though. Does someone know what's going on?
root: CMakeLists.txt
cmake_minimum_required (VERSION 3.10)
project(my_project LANGUAGES CXX VERSION 1.0.0)
enable_testing()
add_subdirectory("src")
add_subdirectory("test")
set_property(TARGET my_project PROPERTY CXX_STANDARD 20)
src: CMakeLists.txt
cmake_minimum_required (VERSION 3.10)
add_executable(my_project
...
"main.cpp"
)
set_property(TARGET my_project PROPERTY CXX_STANDARD 20)
add_subdirectory("C:/dev/libs/nlohmann_json/3.11.2" "build/nlohmann_json")
target_link_libraries(my_project PRIVATE nlohmann_json::nlohmann_json)
test: CMakeLists.txt
cmake_minimum_required (VERSION 3.10)
add_executable(my_project_test
"properties.cpp"
"main.cpp"
)
set(CMAKE_CXX_FLAGS_DEBUG "/MTd")
set(CMAKE_CXX_FLAGS_RELEASE "/MT")
set_property(TARGET my_project_test PROPERTY CXX_STANDARD 20)
add_subdirectory("C:/dev/libs/googletest/1.12.1" "build/google_test")
target_link_libraries(my_project_test PRIVATE gtest)
EDIT: Based on the discussion in chat the issue was related to a different project (the test project) which needed to be linked against nlohmann_json as well.
The bellow code was not needed, as the targets were already imported.
First of all your usage of add_subdirectory is not recommended, there is a reason why it's called add_subdirectory. That being said this is most likely due to the fact that the targets which you use are not exported and thus not visible to your CMakeLists.txt. REPLACE the build/nlohmann_json with a relative path using ${CMAKE_BINARY_DIR}.
The reason why Frodynes solution will work is because FetchContent_MakeAvailable(json) makes sure that the targets are exported.
What you need to do is use add_library with the IMPORTED tag. So to fix your solution you should most likely do the following to your CMakeLists.txt i.e. import the targets. Also note that I'm using the actual library name and not the alias. It might work with the alias but I'm not 100% sure about it:
set_property(TARGET my_project PROPERTY CXX_STANDARD 20)
add_subdirectory("C:/dev/libs/nlohmann_json/3.11.2" ${CMAKE_BINARY_DIR}/nlohmann_json) #fixed
#This doesn't generate a build step (which is already done by the add_subdirectory)
enter code here
add_library(nlohmann_json STATIC IMPORTED)
#OR add_library(nlohmann_json SHARED IMPORTED)
target_link_libraries(my_project PRIVATE nlohmann_json)
########
# OR with the alias:
# add_library(nlohmann_json::nlohmann_json STATIC IMPORTED)
# OR add_library(nlohmann_json::nlohmann_json SHARED IMPORTED)
# target_link_libraries(my_project PRIVATE nlohmann_json::nlohmann_json)
I don't know how to make your setup work, but here is how I use the nlohmann json library in my project:
include(FetchContent)
FetchContent_Declare(
json
URL https://github.com/nlohmann/json/releases/download/v3.11.2/json.tar.xz
DOWNLOAD_EXTRACT_TIMESTAMP YES # <- May be needed depending on CMake version
)
FetchContent_MakeAvailable(json)
set(JSON_ImplicitConversions OFF) # <- You can skip this if you want
target_link_libraries(<your target> PRIVATE nlohmann_json::nlohmann_json)
FetchContent downloads, extracts, and compiles the library automatically, and then I just have to target_link_libraries to it.
I have the following folder structure in my c++ project
*--build
|---(building cmake here)
|
*--main.cpp
|
*--CMakeLists.txt (root)
|
*--modules
|---application
|------app.h
|------app.cpp
|------CMakeLists.txt
And the code below for both CMakeLists.txt files:
CMakeLists.txt (module)
cmake_minimum_required(VERSION 3.15.2)
file(GLOB APPLICATION_HEADERS *.h *.hpp)
file(GLOB APPLICATION_SRC *.c *.cpp)
add_library(app_lib STATIC
${APPLICATION_HEADERS}
${APPLICATION_SRC})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR})
CMakeLists.txt (root)
cmake_minimum_required(VERSION 3.15.2)
project(main)
enable_language(C CXX)
#set directories
set(CMAKE_BINARY_DIR build)
set(CMAKE_CONFIGURATION_TYPES UNIX)
set(CMAKE_MODULES_DIR ${SOURCE_DIR}/cmake)
add_executable(${PROJECT_NAME} main.cpp)
# Build sub-modules
include_directories(modules/application)
add_subdirectory(modules/application)
find_library(MY_APP_LIB app_lib REQUIRED)
target_link_libraries(${PROJECT_NAME} PUBLIC ${MY_APP_LIB})
However, when I do cmake .. in my build directory, it seems like my app library just doesn't build and it doesn't link to it. I end up with the following error:
CMake Error at CMakeLists.txt:80 (find_library):
Could not find MY_APP_LIB using the following names: app_lib
I tried looking at other stackoverflow questions but it seems like I'm missing something. Any help is appreciated!
Thanks!
You don't need to use find_* to locate the library. In fact you cannot locate the library this way, since find_library searches the file system for the library during configuration, i.e. before anything gets compiled.
There's good news though: If the targets are created in the same cmake project, you can simply use the name of the cmake target as parameter for target_link_libraries:
...
add_library(app_lib STATIC
${APPLICATION_HEADERS}
${APPLICATION_SRC})
# note: this should be a property of the library, not of the target created in the parent dir
target_include_directories(app_lib PUBLIC .)
...
add_subdirectory(modules/application)
target_link_libraries(${PROJECT_NAME} PUBLIC app_lib)
You don't need to do find_library for your own targets, just link directly to app_lib:
target_link_libraries(${PROJECT_NAME} PUBLIC app_lib)
Let's say I wanted to develop a simple library with the following structure:
My CMakeLists.txt then looks like this:
cmake_minimum_required(VERSION 3.17)
set(CMAKE_CXX_STANDARD 14)
set(INC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/")
set(SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src/")
file(GLOB INCLUDES "${INC_DIR}/httplib/*.hpp")
file(GLOB SOURCES "${SRC_DIR}/*.cpp")
project(httplib)
add_library(httplib STATIC ${INCLUDES} ${SOURCES})
include_directories(${INC_DIR})
This works fine but what is the usual way to configure CLion projects and CMake in order to test a library effectively ?
I'm new to cmake, so I'm having trouble figuring out how to model the following:
My program requires a third party library that I only have access to in the form of headers and *.so files. It also ships many of its dependencies as *.so files. How do I make sure that everything compiles and links correctly, ideally in the "correct" cmake way?
I've tried this
cmake_minimum_required (VERSION 3.8)
project ("Test")
add_executable (MyApp "MyApp.cpp")
link_directories("/path/to/lib")
target_include_directories(MyApp PUBLIC "/path/to/headers")
This compiles but fails at the linking stage.
Then I tried
cmake_minimum_required (VERSION 3.8)
project ("Test")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
add_executable (MyApp "MyApp.cpp")
find_package(Library REQUIRED)
target_link_libraries(MyApp PUBLIC Library::Library)
And cmake/FindLibrary.cmake following https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/ (truncated):
...
if(Library_FOUND AND NOT TARGET Library::Library)
add_library(Library::Library SHARED IMPORTED)
set_target_properties(Library::Library PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Library_INCLUDE_DIR}"
)
set_target_properties(Library::Library PROPERTIES
IMPORTED_LOCATION "/path/to/library.so"
)
endif()
This compiles and links, but the required other *.so files are not found at runtime. I suppose I need to add them also as libraries somehow, although I don't want them to be exported in FindLibrary.cmake. How do I do this correctly?
You can use IMPORTED libraries:
add_library(externalLib SHARED IMPORTED GLOBAL)
set_property(TARGET externalLib PROPERTY IMPORTED_LOCATION "/path/to/lib.so")
target_include_directories(externalLib INTERFACE "/path/to/headers")
target_link_directories(externalLib INTERFACE /path/to/libs)
target_link_libraries(externalLib INTERFACE -ldep1 -ldep2)
Then you can just depend on it:
target_link_libraries(MyApp PRIVATE externalLib)
I have a library which uses Qt5. I build it with this CMakeLists.txt file:
cmake_minimum_required (VERSION 3.0)
project (testLib LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Qt5 REQUIRED Widgets)
add_library(testLib SHARED
src/TestClass.cpp
src/TestClass.h
)
target_include_directories(testLib PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)
target_link_libraries(testLib PUBLIC Qt5::Widgets)
export(TARGETS testLib FILE testLib-exports.cmake)
Now I try to link an executable against this library in the build path. This is what I've tried so far:
cmake_minimum_required (VERSION 3.0)
project(TestProject)
add_executable(myexec src/main.cpp )
include(/path/to/testLib-exports.cmake)
target_link_libraries(myexec testLib)
I am getting this error:
Target "myexec" links to target "Qt5::Widgets" but the target was not
found. Perhaps a find_package() call is missing for an IMPORTED
target, or an ALIAS target is missing?
I don't want to explicitly use find_package() in myexec's cmake file, but want it to be transitive. How should I link against testLib then? If the answer is not in the build path it is fine.
EDIT: I changed the export line to:
export(TARGETS testLib FILE testLib-exports.cmake EXPORT_LINK_INTERFACE_LIBRARIES)
This seems to be exactly what I need, but the generated files with and without EXPORT_LINK_INTERFACE_LIBRARIES are identical. Both if them say
This file does not depend on other imported targets which have
been exported from the same project but in a separate export set.
As far as I understand, it should use testLib's target LINK_INTERFACE_LIBRARIES property, but I noticed that it is NOTFOUND. maybe this is the problem? However, INTERFACE_LINK_LIBRARIES has Qt5::Widgets.