I am working to switch an existing project from manual Makefiles to CMake (because the project is growing quite large), but there is an issue when I run an executable which is linked to a shared library which I create.
The my directory structure is as follows:
--CMakeLists.txt
--include
|
--include_files.h
--src
|
--src_files.cpp
--test
|
--CMakeLists.txt
--test1.cpp
The CMakeLists.txt in the root directory is as follows:
project(Project)
include_directories(${PROJECT_SOURCE_DIR}/include/)
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp")
add_library(Project SHARED ${SOURCES})
include_directories($ENV{EXTERN_INCLUDE}/lnInclude)
target_link_libraries(Project PRIVATE "$ENV{EXTERN_LIBBIN}/libextern.so")
add_subdirectory(test)
The CMakeLists.txt in the test directory is as follows:
cmake_minimum_required(VERSION 2.8.9)
include_directories(${PROJECT_SOURCE_DIR}/include/)
add_executable(test1 test1.cpp)
include_directories($ENV{EXTERN_INCLUDE}/lnInclude)
target_link_libraries(test1 PRIVATE "$ENV{EXTERN_LIBBIN}/libextern.so")
add_dependencies(test1 Project)
target_link_libraries(test1 PUBLIC ${CMAKE_BINARY_DIR}/libProject.so)
When I run cmake, then make, the shared library libProject.so is built (no errors), and the executable test1 is created, but when test1 is run, there are errors during runtime.
If I add these lines to the CMakeLists.txt in the test directory, the executable and shared library are built, and work perfectly:
file(GLOB SOURCES "${PROJECT_SOURCE_DIR}/src/*.cpp")
add_executable(test1 test1.cpp ${SOURCES})
This is not ideal though, because it means that I am compiling the src twice.
I have also tried to manually construct the Makefile for the test, but not the shared library, and this works fine without having to recompile the src. This makes me think that the issue is due to my use of cmake.
My question is: How do I correctly link the test executable with the shared library using CMake?
An example of the runtime error is as follows:
A was returned outside range, A=-nan
Inside the test function, I construct a class and pass it some info (node is defined in include and compiled into libProject.so):
Node& station1(*new Node(station1));
station1.X(X);
station1.Y(Y);
station1.Z(Z);
Then I call the functions of the class:
station1.A();
I expect this function to return a number in a predefined range, but it does not.
This is the output from gdb which makes me suspect that the shared library is not being loaded:
Function "Node::A()" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 3 (Node::A()) pending.
(gdb) continue
Continuing.
terminate called after throwing an instance of 'std::range_error'
what(): Error: A lower than 0
For the test1 target, don't manually add the dependency to Project or link with the libProject.so file.
Instead let CMake handle everything, simply by doing e.g.
include_directories(${PROJECT_SOURCE_DIR}/include/)
add_executable(test1 test1.cpp)
target_link_libraries(test1 Project)
Note that I also removed the extern dependency. If the test program doesn't actually use that library it's not needed..
Related
Probably the error is trivial for CMake experts but I seem to be blind to it. I've tried two approaches.
I have a library written in C and compiled using gcc 9.4.0 as both, static and dynamic library. I've compiled the library apart and it successfully builds and links against its main.c to produce an executable using a Makefile. When I do nm -D mylib.so I get all the symbols and functions I need.
Now, I am trying to use this library with ROS (meaning build and link with CMake) but the program fails to link. I've tried both approaches: use the library as is, and re-build the library using CMake. Both fail. For the sake of keeping the example short I've removed the lines used by catkin that basically tell CMake to link against all ROS infrastructure. Suffices to say that the main.cpp of the target (my_node) consumes a void* CreateEnvironment(); function from the library which is not found (same with any other function). Say:
#include <mylib/mylib.h>
int main(int argc, char** argv){
foo();
return 0;
}
void foo(){
void ptr = CreateEnvironment();
}
Approach #1: Use pre-built library (preferred)
With CMake it refuses to link. CMakeLists.txt as follows:
cmake_minimum_required(VERSION 3.0.2)
find_library(
MY_LIB
NAME mylib
PATHS "${PROJECT_SOURCE_DIR}/lib"
NO_DEFAULT_PATH # Can add or remove this, no difference
)
add_library(mylib SHARED IMPORTED)
set_target_properties(mylib PROPERTIES IMPORTED_LOCATION "${MY_LIB}")
add_executable(my_node src/main.cpp)
target_link_libraries(my_node mylib ${catkin_LIBRARIES})
It runs:
/usr/bin/c++ -rdynamic CMakeFiles/…/main.cpp.o -o /home/…/clips_node -Wl,-rpath,/home/…/lib:/opt/ros/noetic/lib /home/…/lib/libmylib.so […]
/usr/bin/ld: CMakeFiles/…/main.cpp.o: in function `foo()':
main.cpp:(.text+0x11): undefined reference to `CreateEnvironment()'
Approach #2: Build library with CMake
Tried this approach thinking maybe the library I have was built with different standard, or not all symbols were exported, or exports were incompatible or whatever. A build made by the same system and forcing C++ rather than C should make it linkable... but did not.
Once again, the SO is created and all symbols are visible with nm -D. It is the linking with the executable target what fails.
cmake_minimum_required(VERSION 3.0.2)
## Build library
SET(MYLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/include/mylib")
AUX_SOURCE_DIRECTORY(${PROJECT_SOURCE_DIR}/src/mylib MYLIB_SOURCES)
add_library(mylib ${MYLIB_SOURCES})
target_include_directories(mylib PRIVATE ${MYLIB_INCLUDE_DIR})
set_target_properties(mylib PROPERTIES
LINKER_LANGUAGE "CXX"
ENABLE_EXPORTS 1
)
## Build node
add_executable(my_node src/main.cpp)
target_include_directories(my_node PUBLIC ${MYLIB_INCLUDE_DIR})
target_link_libraries(my_node mylib ${catkin_LIBRARIES})
Output is almost identical to that of the Approach #1, only the path of the library changes.
What else have I tried
Changing the order of the elements in target_link_libraries
Changing PRIVATE for PUBLIC
Add -lmylib to target_link_libraries
Add the full path to libmylib.so to target_link_libraries
Replace the shared object with a static library
So far it seems that the shared object is passed to the linker but, for some reason, it refuses to link the file. With a Makefile I know I can change the order since some libraries must be prepended and some appended, but CMake seems to be all automagic and I failed to find the error so far.
My problem is relatively simple to explain. I have a CMake project(using CLion) running mainly on Windows.
A main CMakeLists.txt project, linking 2 subdirectories
src/Library shared library
src/Executable executable linking Library
See the project structure below.
# main CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(TestTest)
set(CMAKE_CXX_STANDARD 11)
add_subdirectory(src/Library)
add_subdirectory(src/Executable)
# src/Library/CMakeLists.txt
add_subdirectory(include)
add_subdirectory(src)
add_library(TestLib SHARED ${TESTLIB_H_FILES} ${TESTLIB_SRC_FILES} )
target_include_directories(TestLib PUBLIC include)
#src/Executable/CMakeLists.txt
add_subdirectory(src)
add_executable(Executable ${EXECUTABLE_SRC_FILES})
target_link_libraries(Executable PUBLIC TestLib)
My problem is that the executable can't find the shared library at runtime.
I tried to add link_directories( ${CMAKE_CURRENT_BINARY_DIR}/src/Library) but didn't work.
What am I missing?
Please note: I wouldn't like to copy the shared library next to the executable manually/automatically by CMake, since I believe I would loose the debugging "capability" of the shared library. I will be getting the following error message by the GDB: No source file named C:/Users/flatron/CLionProjects/TestTest/src/Library/src/library.cpp.
All suggestions are really welcome and appreciated,
Thank you for your help
I suggest to use the following cmake command:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
This will build all executables and shared libraries into the bin folder.
So there is no need to copy anything by hand.
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.
Here is a sample project I am trying to build with a "Packages" directory which includes all the libraries to be used in the main code.
I am trying to keep my root cmake file as clean as possible and avoid relative path such as
include_directory(packages/lib1)
but I am struggling. Is there a way of including sub-directories of a directory for the purposes of header inclusion.
First a few minor remarks:
always name the CMake configuration files CMakeLists.txt (because of)
bookmark the documentation on CMake: https://cmake.org/documentation/
Sometimes it's not that easy to read, but very specific once you adopt your head to the "CMake world" ;-)
make yourself comfortable with the scope of CMake variables
include_directories(DIR1 [DIR2 [...]])
Tells CMake where the compiler should look for header files, i.e. -IDIR1 -IDIR2 ....
add_library(NAME [STATIC|SHARED] SOURCES)
This command creates the required compiler commands to create a static or shared library out of a given list of source files. No need to add in the header files. The make target will be called NAME and the library target is known to CMake as NAME.
add_subdirectory(DIR)
Tells CMake to look into DIR and parse the included CMakeLists.txt with all its content.
target_link_libraries(TARGET LIB1 [LIB2 [...]])
Tells CMake to instruct the linker to link LIB1, LIB2, etc. to the TARGET, i.e. -LLIB1 -LLIB2 .... TARGET is a CMake/make target previously defined/created with a call to add_{library,executable,custom_target}.
CMakeLists.txt:
include_directories(libraries)
# a header file in `libraries/lib1/foo.hpp` can be included
# in the whole CMake project by `#include "lib1/foo.hpp"`.
add_subdirectory(libraries)
add_subdirectory(tests)
libraries/CMakeLists.txt:
add_subdirectory(lib1)
add_subdirectory(lib2)
libraries/lib1/CMakeLists.txt:
add_library(lib1 STATIC ${LIB1_SOURCES})
libraries/lib2/CMakeLists.txt:
add_library(lib2 STATIC ${LIB2_SOURCES})
tests/CMakeLists.txt:
add_executable(tests ${TEST_SOURCES})
target_link_libraries(tests lib1 lib2)
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.