How to properly use CMake to create a complex project with dependencies on custom libraries - cmake

I'm trying to create a complex project that becomes a single executable file that uses the following libraries: two libraries BHV and HAL that use one interface library.
I have this project structure:
.
├── BHV
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── cmake_install.cmake
│   ├── CMakeLists.txt
│   ├── include
│   ├── libBHV_Library.so
│   ├── Makefile
│   └── sources
├── HAL
│   ├── check_libraries.cmake
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── cmake_install.cmake
│   ├── CMakeLists.txt
│   ├── include
│   ├── libHAL_Library.so
│   ├── Makefile
│   └── sources
├── Interface
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── cmake_install.cmake
│   ├── CMakeLists.txt
│   ├── include
│   ├── libInterface_Library.a
│   ├── Makefile
│   └── sources
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── CMakeLists.txt
├── main.cpp
├── Makefile
├── README.md
Unfortunately, I can't connect the individual libraries to each other.
In Interface_lib CMakeList.txt I have this:
cmake_minimum_required(VERSION 3.10)
project(Interface_Library)
#requires at least C++17
set(CMAKE_CXX_STANDARD 17)
# Add all .cpp files from sources folder
file(GLOB SOURCES "sources/*.cpp")
# Add all .h files from include folder
file(GLOB HEADERS "include/*.h")
# Add main.cpp to the project
add_library(Interface_Library STATIC ${SOURCES} ${HEADERS})
In HAL_lib CMakeList.txt I have this:
cmake_minimum_required(VERSION 3.10)
project(HAL_Library)
# requires at least C++17
set(CMAKE_CXX_STANDARD 17)
################################### DIR_MANAGMENT #########################################
# Get the parent directory
get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} DIRECTORY)
#Set the directory of the parent file as the current directory
set(CMAKE_CURRENT_LIST_DIR ${PARENT_DIR})
message("MYPROJECT_DIR directory: ${CMAKE_CURRENT_LIST_DIR}")
################################### HAL_LIB #########################################
# Add all .cpp files from sources folder
file(GLOB SOURCES "sources/*.cpp")
# Add all .h files from include folder
file(GLOB HEADERS "include/*.h")
# Add main.cpp to the project
add_library(HAL_Library SHARED ${SOURCES} ${HEADERS})
################################### INTERFACE_LIB #########################################
target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/Interface)
# Link the Interface_Library into the HAL_Library
target_link_libraries(HAL_Library Interface_Library)
# check if libraries were included
set(TARGET_NAME HAL_Library)
include(check_libraries.cmake)
this is the code i use to check_libraries.cmake (from the internet)
# Get a list of referenced libraries
get_target_property(LINK_LIBS ${TARGET_NAME} LINK_LIBRARIES)
# Print the list of referenced libraries
message("Odkazované knihovny: ${LINK_LIBS}")
# Verify that libraries are available on the system
foreach(LIB ${LINK_LIBS})
execute_process(COMMAND ldd $<TARGET_FILE:${TARGET_NAME}> | grep ${LIB}
RESULT_VARIABLE res
OUTPUT_QUIET ERROR_QUIET)
if(res EQUAL "0")
message("Library ${LIB} was successfully linked with ${TARGET_NAME}")
else()
message(FATAL_ERROR "Error: Library ${LIB} not found.")
endif()
endforeach()
As an output I keep getting the library not found. What am I doing wrong?
And is my approach to project structure correct?
Thank you.

Typically you do:
# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(Interface_Library)
add_subdirectory(HAL)
add_subdirectory(Interface)
# HAL/CMakeListst.txt
file(srcs GLOB sources/*.c include/*.h)
add_library(HAL ${srcs})
target_include_directories(HAL PUBLIC include)
target_link_libraries(HAL PUBLIC Interface)
# Interface/CMakeLists.txt
file(srcs GLOB source/*.c include/*.h)
add_library(Interface ${srcs})
target_include_directories(HAL PUBLIC include)
That's all. Only one project() call, in the root. Note the target_include_directories missing in your code. Note the main root CMakeLists.txt that includes all. I find HEADERS and SOURCES separately confusing, I would also use lowercase everything. No target_link_directories, CMake will find everything. Also, everything is compiled as one big project, not separately.

Related

Cmake add include_dirs to a target, then link target to a executable

How do I set the include dirs of a target, then include that target in my executable the current project folder looks like this:
├── CMakeLists.txt
├── libs
│ ├── CMakeLists.txt
│ └── libs.h
├── src
│ ├── CMakeLists.txt
│ └── main.cpp
└── tests
└── tests.cpp
So I would like to puts the .h files of the libs on the libs folder and make that a target which I can link to, I currently have something like this:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.20.0)
project("Project")
add_subdirectory(libs)
add_subdirectory(src)
src/CMakeLists.txt:
add_executable(main main.cpp)
target_link_libraries(main libs)
libs/CMakeLists.txt:
add_custom_target(libs PUBLIC)
target_include_directories(libs ${CMAKE_CURRENT_SOURCE_DIR})

Library linking not working when CMake single build

using CMake I want to build in each directory A, B, and C.
The project structure is as follows:
.
├── A
│   ├── CMakeLists.txt
│   └── src
│   └── a.cpp
├── B
│   ├── CMakeLists.txt
│   └── src
│   └── b.cpp
├── common
│   ├── CMakeLists.txt
│   ├── include
│   │   ├── common.h
│   └── src
│   └── common.cpp
└── C
├── CMakeLists.txt
└── src
└── c.cpp
Create a library in the Common directory and install the created library.
common/CmakeLists.txt :
set(COMMON_LIB_NAME CommonTemp)
set(SRC_CODE
${CMAKE_CURRENT_SOURCE_DIR}/include/common.h
${CMAKE_CURRENT_SOURCE_DIR}/src/common.cpp
)
add_library (${COMMON_LIB_NAME} SHARED ${SRC_CODE})
install(TARGETS ${COMMON_LIB_NAME} DESTINATION ~/tempDir/lib)
I want to link the generated library to directory A.
a.cpp requires common.h.
A/CMakeLists.txt:
ADD_EXECUTABLE(ServiceA src/a.cpp)
target_include_directories (
ServiceA PUBLIC
include
)
target_sources (
ServiceA PRIVATE
src/a.cpp
)
target_link_libraries(
ServiceA PUBLIC
${COMMON_LIB_NAME} #### Location considered to be a problem
)
install(TARGETS ServiceA DESTINATION ~/tempDir/bin/A)
An error message occurs saying that common.h cannot be included.
My guess is that the ${COMMON_LIB_NAME} variable is defined in CMakeLists.txt in another directory, so it is expected that it cannot be linked.
But even if I put CommonTemp , the value of the variable, it says that common.h cannot be found.
How can I link library links even if I build CMake individually in each directory?
In common/CmakeLists.txt.
You need to include the headers in target_include_directores.
target_include_directories(
${COMMON_LIB_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
No need to mention it in add_library
In A/CMakeLists.txt. Include path is not correct.
target_include_directories(
ServiceA PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/<path to common>/include
)
For linking the library you have to mention the correct library path otherwise it will fail
target_link_libraries(
ServiceA PUBLIC
${COMMON_LIB_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/<path to tempDir>/lib
)

How to add include_directory in simple CMake Project

I have CMake project whose directory structure is as follows:
├── build
├── CMakeLists.txt
├── src
│   ├── CMakeLists.txt
│   ├── headers
│   │   └── utility.h
│   └── main.cpp
└── tests
├── CMakeLists.txt
├── testfeature_a
│   ├── CMakeLists.txt
│   └── test_me.cpp
└── test_main.cpp
In test_me.cpp I wanted to include utility.h as I wanted to test functions defined there. So I did #include "headers/utility.h" and in testfeature_a CMakeLists.txt I did this:
file(GLOB SRCS *.cpp)
ADD_EXECUTABLE(testfeature_a ${SRCS})
include_directories(src/headers)
TARGET_LINK_LIBRARIES(
testfeature_a
libgtest
libgmock
)
add_test(NAME testfeature_a
COMMAND testfeature_a)
But the make fails with the error message fatal error: headers/utility.h: No such file or directory.
How can I include the headers directory in test_me.cpp
Your path in include_directories() may be incorrect. Here are two things that you could check
The file seems to be the CMakeLists.txt in tests-folder so you need to go up one folder before you can go to src-folder, i.e. include_directories(../src/headers).
You repeat the headers-folder in the #include "headers/utility.h" when you have already specified it in include_directories(src/headers). Either use #include "utility.h" in cpp-file or include_directories(src) in CMakeLists.txt.
Other option is that you don't need to specify the headers-folder in the CMakeLists.txt at all. You can simply use #include "path/to/your/file.h without any other configuration.
For debugging your path in the CMakeLists.txt you can call message-function, e.g. message(${your_path}), so it's printed when executed and you can check if it's correct.
In addition you can use CMake built-in variables such as CMAKE_CURRENT_SOURCE_DIR and CMAKE_SOURCE_DIR, e.g. include_directories(${CMAKE_SOURCE_DIR}/src/headers)

CMake error : "include could not find load file GoogleTest"

I have a problem with adding googletests to CMakeLists.txt.
The problem is "Error:include could not find load file:
GoogleTest".
I have installed google tests using the commands:
sudo apt-get install libgtest-dev
sudo apt-get install cmake # install cmake
cd /usr/src/gtest
sudo cmake CMakeLists.txt
sudo make
sudo cp *.a /usr/lib
These commands I took from here.
There is my CMakeLists.txt:
cmake_minimum_required(VERSION 3.6.2)
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_STANDARD 17)
project(compiler)
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
find_package(FLEX REQUIRED)
FLEX_TARGET(lexer src/lexer.fl ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp)
find_package(GTest REQUIRED)
include(GoogleTest) <- There is a problem!
add_executable(run_tests src/tests/test.cpp src/main.h ${FLEX_lexer_OUTPUTS})
target_include_directories(run_tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(run_tests ${GTEST_LIBRARIES} stdc++fs)
#gtest_add_tests(TARGET run_tests)
add_executable(compiler src/main.h src/main.cpp src/common.h ${FLEX_lexer_OUTPUTS})
I will be really appreciate if you give me an advice!
If you already got libgtest installed, just add following in your CMakeLists.txt:
target_link_libraries($YOUR_TARGTET gtest)
But! I think you made it too complicated.
It's better to include the googletest source code directlly into your project as a third party library. I will go here this way here and after.
As you involved 'apt-get' in your code example, I assume you are on Debian or Debian decendants. Well, of caurse actually it does not matter which OS you are on.
Clone the googletest source code(check previous for link) into your project, add following into your main CMakeLists.txt:
add_subdirectory(googletest)
add following into sub-cmake projects where you are going to need gtest:
include_directories(${PARENT_PATH_OF_GTEST}/googltest/googltest/include)
# ...
target_link_libraries($YOUR_TARGET gtest)
e.g.
Let's say you have a project with following structure:
.
├── assets
│   └── ...
├── bin
│   ├── CMakeLists.txt
│   └── ...
├── CMakeLists.txt <== Your main CmakeLists.txt
├── src
│   ├── CMakeLists.txt
│   └── ...
├── tests <== Test cases
│   ├── CMakeLists.txt
│   └── ...
└── third_party
├── CMakeLists.txt
├── googletest <=== gtest
└── ...
In your main CMakeLists.txt, you should have:
add_subdirectory(tests)
add_subdirectory(third_party)
# ...
In the third_party CMakeLists.txt:
add_subdirectory(googletest)
# ...
In the tests CMakeLists.txt:
include_directories(${CMAKE_SOURCE_DIR}/third_party/googltest/googltest/include)
# ...
add_executable(my_test...)
target_link_libraries(my_test gtest)
# ...
A better example
Here is a video example for Gtest in CLion from Jetbrains. As CLion actually bases on CMake, so it also clearlly shows you how to integrate GTest into CMake.
Google Test support in CLion

cmake can't find the depending library

I have a demo project which rely on a logger library, The file structure of the relevant files are listed as below:
.
├── FancyLogger
│   ├── CMakeLists.txt
│   ├── FancyLogger.cpp
│   ├── FancyLogger.h
│   ├── bin
│   │   └── libFancyLogger.a
└── HelloClion
├── CMakeLists.txt
├── helloclion.cpp
I have build a static library named as libFancyLogger.a in the ./FancyLogger/bin directory.
My CMakeFiles of the project HelloClion is listed as below:
cmake_minimum_required(VERSION 3.3)
project(HelloClion)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
get_filename_component(CODE_ROOT ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
set(SOURCE_FILES helloclion.cpp)
add_executable(HelloClion ${SOURCE_FILES})
include_directories(${CODE_ROOT}/FancyLogger)
link_directories(${CODE_ROOT}/FancyLogger/bin)
target_link_libraries(HelloClion FancyLogger)
But the linker complains that library not found for -lFancyLogger, I don't know how to fix this, please help.
I seems to find out why. ;)
In the cmake documentation:https://cmake.org/cmake/help/v3.0/command/link_directories.html
link_directories(directory1 directory2 ...)
Specify the paths in which
the linker should search for libraries. The command will apply only to
targets created after it is called.
The doc says, the command only applys to targets created after it is called. So I just need to move link_directories(${CODE_ROOT}/FancyLogger/bin) to some position in front of the add_executable(HelloClion ${SOURCE_FILES}) command.
Problems solved!