Setup linking between shared and static lib in cmake - c++

After reading some articles on static and shared libs I think I know the difference but I still cannot figure out a way to fix my use case. I have the following project structure
Project
├── A
│   ├── A.cpp
│   ├── A.h
│   └── CMakeLists.txt
├── B
│   ├── CMakeLists.txt
│   └── B.cpp
├── CMakeLists.txt
in folder A I have:
//content of A.h
#include <opencv2/opencv.hpp>
cv::Mat A_load_image(std::string file_path);
//content of A.cpp
#include "A.h"
cv::Mat A_load_image(std::string file_path) {
return cv::imread(file_path);
}
// content of CMakelists.txt in A
set(TARGET A)
add_library( ${TARGET} STATIC A.cpp )
target_include_directories(${TARGET} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
/path/to/opencv/include/folder )
link_directories( /path/to/opencv/lib/folder)
target_link_libraries( ${TARGET} PUBLIC libopencv_core.a )
then in my folder B I have:
//content of B.cpp
#include "A.h"
cv::Mat B_load_image() {
return A_load_image("img.bmp");
}
// content of CMakelists.txt in B
set(TARGET B)
add_library(A STATIC IMPORTED)
set_target_properties(A PROPERTIES IMPORTED_LOCATION /PATH/TO/libA.a)
add_library(${TARGET} SHARED B.cpp)
target_include_directories(${TARGET} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../A
/path/to/opencv/include/folder )
target_link_libraries(${TARGET} PUBLIC A libopencv_core.a)
And of course I have the CMakelists.txt file in the project root has:
cmake_minimum_required(VERSION 3.4.1)
add_subdirectory(B)
add_subdirectory(A)
I have questions below regarding this project.
How to tell cmake to compile B first so that when I import B for A, it is already updated if any changes
The above setup does not work as I got error when linking B: "cannot find -lopencv_core", I already used PUBLIC for linking A, I also tried to add link_directories( /path/to/opencv/lib/folder) to the CMakelists.txt for B, but still not working.
I believe "cannot find -lopencv_core" failed because it is looking for dynamic lib, e.g., libopencv_core.so rather than the static one. But why is that and how I force to link to the static lib?

Try an object library:
cmake_minimum_required(VERSION 3.11)
add_subdirectory(A)
add_subdirectory(B)
# A/CMakeLists.txt
find_package(OpenCV REQUIRED)
add_library(A OBJECT A.cpp)
target_include_directories(A PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(A PUBLIC ${OpenCV_LIBS})
# B/CMakeLists.txt
add_library(B SHARED B.cpp)
target_include_directories(B PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(B PUBLIC A)
# also try
target_link_options(B PRIVATE "-Wl,--whole-archive")
# or maybe
target_link_options(B PRIVATE "-Wl,--export-all-symbols")
# or maybe also
target_sources(B $<TARGET_OBJECTS:A>)

Related

How to cross link libraries in CMake

In a c++ CMake project I have an executable main and two libraries lib1 and lib2. A function in lib1 needs a function from lib2 and visa versa. Also, lib1 only contains .h files. The main executable will use both libraries. When I try and "make" the project, I get an error:
error: redefinition of ‘void lib1()’.
The file structure looks somewhat like this
/path/to/my/project
├── CMakeLists.txt # Project directory
├── main.cpp
├── Lib1
│ ├── ...files (.h only)...
│ ├── CMakeLists.txt # lib1 cmake
├── Lib2
│ ├── ...source files (.cpp & .h)...
│ ├── CMakeLists.txt # lib2 cmake
The CMakeLists.txt in the Project directory includes the following:
add_executable(${PROJECT_NAME} main.cpp)
add_subdirectory(Lib1)
add_subdirectory(Lib2)
target_link_libraries(${PROJECT_NAME}
lib2
lib1
)
The CMakeLists.txt in the Lib1 directory includes the following:
add_library(lib1 INTERFACE)
target_include_directories(lib1
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(lib1 INTERFACE
lib2
)
The CMakeLists.txt in the Lib2 directory includes the following:
add_library(lib2 ${SOURCES} ${HEADERS}) # SOURCES and HEADERS set in lines above
target_include_directories(lib2
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(lib2
lib1
)
If I had to guess, the issue is it is trying to import lib1 headers twice. Once from lib2 and once in my main executable. How do I link the libraries so that isn't an issue?
I dont think it's anything related to cmake. Although convoluted (I'd do it in another way but hey it's your code) I think you are defining the body of a function in lib1 where it should reside in a cpp file.
Make that function lib1 inline.
inline void lib1() {
...
}
or alternatively defined it in the header and implement it in a body file
//lib1.h
void lib1();
Then
//lib1.cpp
#include "lib1.h"
void lib1() {
...
}

How to add include search path to a subdirectory without ’CMakeLists.txt' in CMake?

I have a CMake project with a directory structure below:
.
├── b.h
├── CMakeLists.txt
├── src
└── test
├── CMakeLists.txt
├── impl
└── include
└── a.h
I want to include b.h in a.h but the CMake cannot find b.h in a.h. I know it's wrong if I include a.h in b.h while including b.h in a.h. However, I'm sure that a.h will only be used in files in test/impl.
The content of ./CMakeLists.txt simply adds test/ as a subdirectory.
The content of ./test/CMakeLists generates an executable target which used googletest.
Is there anyway that I can include b.h in a.h without adding an extra CMake file in ./test/include?
./CMakeLists.txt
file(GLOB_RECURSE SOURCES src/*.cpp)
add_library(core SHARED ${SOURCES})
target_include_directories(core PUBLIC ${PROJECT_SOURCE_DIR})
add_subdirectory(test)
./test/CMakeLists.txt
file(GLOB_RECURSE SOURCES impl/*.cpp)
enable_testing()
add_executable(
core_test
${SOURCES}
)
target_link_libraries(
core_test
gtest_main
)
include(GoogleTest)
gtest_discover_tests(core_test)
target_include_directories(core_test PUBLIC ${PROJECT_SOURCE_DIR})

'torch/torch.h' file not found in header file

I'm new in Linux C++ programming. I have to use libtorch (pytorch C++ version), but I kept struggling with C++ and cmake problems. These problems really make me mad, and I need some help.
Here is a simple C++ project:
.
├── build.sh
├── CMakeLists.txt
├── load_data
│   ├── CMakeLists.txt
│   ├── include
│   │   └── load_data
│   │   └── load_data.hpp
│   └── src
│   └── load_data.cpp
└── src
└── main.cpp
But I can't build it because load_data/include/load_data/load_data.hpp cannot #include <torch/torch.h>.
The build error message:
[build] /my/path/load_data/include/load_data/load_data.hpp:4:10: fatal error: 'torch/torch.h' file not found
[build] #include <torch/torch.h>
[build] ^~~~~~~~~~~~~~~
However, if I move the #include <torch/torch.h> from the header file to the corresponding source file, the error message goes away. It makes me confused.
Files' contents:
load_data/include/load_data/load_data.hpp
#pragma once
#include <torch/torch.h>
namespace load_data
{
void try_torch();
// torch::Tensor rand_tensor();
}
load_data/src/load_data.cpp
# include "load_data/load_data.hpp"
// If I remove it from load_data.hpp and use it here, the error goes away.
// #include <torch/torch.h>
namespace load_data {
void try_torch(){
torch::Tensor tensor = torch::rand({2, 3});
std::cout << tensor << std::endl;
}
}
src/main.cpp
#include <string>
#include "load_data/load_data.hpp"
int main(int argc, char **argv)
{
load_data::try_torch();
return 0;
}
CMakeLists.txt
cmake_minimum_required (VERSION 3.21)
set(CMAKE_CXX_COMPILER clang++-13)
project(ldt-cpp-experiments VERSION 1.0.0 LANGUAGES CXX)
find_package(Torch REQUIRED)
message(STATUS "Torch_FOUND: ${Torch_FOUND}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
add_subdirectory("load_data")
set(EXECUTABLE_NAME main)
add_executable(${EXECUTABLE_NAME} src/main.cpp)
target_link_libraries(${EXECUTABLE_NAME}
PRIVATE load_data
)
set_target_properties(${EXECUTABLE_NAME} PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
)
load_data/CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project (load_data VERSION 1.0.0 LANGUAGES CXX)
find_package(Torch REQUIRED)
message(STATUS "Torch_FOUND: ${Torch_FOUND}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
set(LIB_NAME "load_data")
add_library(${LIB_NAME}
SHARED
)
target_sources(${LIB_NAME}
PRIVATE
${PROJECT_SOURCE_DIR}/src/load_data.cpp
)
target_sources(${LIB_NAME}
PRIVATE
${PROJECT_SOURCE_DIR}/src/load_data.cpp
${PROJECT_SOURCE_DIR}/include/load_data/load_data.hpp
)
target_include_directories(${LIB_NAME}
PUBLIC
${PROJECT_SOURCE_DIR}/include
)
target_link_libraries(${LIB_NAME}
PRIVATE
${TORCH_LIBRARIES}
)
set_target_properties(${LIB_NAME} PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
)
build.sh
cmake -S . -B build

How do you add external libraries to 'self-made' libraries using CMake?

I'm not able to link external libraries with the libraries I wrote using CMake. I'm wondering if there's something that is needed to be added to my CMakeLists.txt? Or if I need to add another CMakeLists.txt in a lower level (inside src) and what would that need to contain?
I have the following project structure:
ProjectFolder
│   ├── CMakeLists.txt
│   ├── build
│   │   └──
│   ├── include
│   │   └── helper.h
│   └── src
│   ├── helper.cpp
   └── main.cpp
My CMakeList.txt is:
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
project(object_detection)
find_package(PCL 1.5 REQUIRED)
find_package(OpenCV REQUIRED)
file(GLOB SOURCES src/*.cpp)
file(GLOB INCLUDE include/*.h)
include_directories(${PCL_INCLUDE_DIRS} ${INCLUDE})
link_directories(${PROJECT_NAME} ${PCL_LIBRARY_DIRS} ${SOURCES} )
add_definitions(${PCL_DEFINITIONS})
add_executable (${PROJECT_NAME} src/main.cpp)
target_link_libraries (${PROJECT_NAME} ${OpenCV_LIBS} ${PCL_LIBRARIES} ${SOURCES})
In my file helper.cpp I have:
#include <pcl/io/pcd_io.h>
Which gives the error:
fatal error: 'pcl/io/pcd_io.h' file not found
#include <pcl/io/pcd_io.h>
^~~~~~~~~~~~~~~~~
However I have the same include in main.cpp with no errors.
I would be very grateful for any help, please let me know if I need to clarify my question or error.
Thank you.
There are several errors in the CMakeLists.txt with the following changes the project loads appropriate libraries and builds properly. Another note is that before, to include helper.h I needed to write:
#include "../include/helper.h".
Now it works as expected with #include "helper.h".
Here is the modified CMakeLists.txt:
cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
project(object_detection)
find_package(PCL 1.5 REQUIRED)
find_package(OpenCV REQUIRED)
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)
include_directories(${PCL_INCLUDE_DIRS} include)
link_directories(${PROJECT_NAME} ${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
#add_executable (${PROJECT_NAME} src/helper.cpp src/main.cpp )
add_executable (${PROJECT_NAME} ${SRC_FILES} )
target_link_libraries (object_detection ${PCL_LIBRARIES} ${OpenCV_LIBS})

CMake cannot detect symbol conflicts in different static libraries

I created a simple cmake project to reproduct it.
├── CMakeLists.txt
├── lib1
│   ├── CMakeLists.txt
│   ├── lib1.cpp
│   └── lib1.h
├── lib2
│   ├── CMakeLists.txt
│   ├── lib2.cpp
│   └── lib2.h
└── main.cpp
lib1/CMakeLists.txt:
add_library(lib1 "")
target_include_directories(lib1
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
target_sources(lib1
PRIVATE
lib1.cpp
lib1.h
)
In lib1.cpp, there is a function "void say()":
#include <stdio.h>
void say()
{
printf("hello from lib1\n");
}
lib2/CMakeLists.txt:
add_library(lib2 "")
target_include_directories(lib2
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
target_sources(lib2
PRIVATE
lib2.cpp
lib2.h
)
And in lib2/lib2.cpp, there is a function of the same signature:
#include <stdio.h>
void say()
{
printf("hello from lib2\n");
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(shell LANGUAGES CXX)
add_subdirectory(lib1)
add_subdirectory(lib2)
add_executable(test2
main.cpp
)
target_link_libraries(test2
PRIVATE
lib1
lib2
)
Here is the main.cpp:
void say();
int main()
{
say();
return 0;
}
The output:
hello from lib1
There is no compile or link error, not even a warning.
The linker just picked one and symply ignored the other one.
I'm using cmake 3.16, and tested it with msvc 2017/2019 and g++ 7.5.
How to make the linker prompts errors when there are symbol conflicts in static libraries?
Thanks!
How to make the linker prompts errors when there are symbol conflicts in static libraries?
With gcc use the --whole-archive option to include every object file in the archive rather then search the archives for the required symbol.
As there is no cmake support that I know of, I find it's typically done when linking the executable:
target_link_libraries(test2 PRIVATE
-Wl,--whole-archive
lib1
lib2
-Wl,--no-whole-archive
)
"Symbol conflicts" is rather vague term. This will only detect multiple symbol definitions. Types of symbols are not stored anywhere after compilation.