I'm pretty new to CMake, but I already encountered an error which I do not understand.
For given reasons, I have the following C++ setup: I have two own static libraries, multiple external static libraries and one executable. My own libraries a defines common stuff and all external libraries are linked into it. My library b defines more special stuff and has library a linked into it. Finally, b is linked into my executable c. Both b and c use functions from different external libraries, which should be visible through a.
I have the following CMakeLists.txt, where I reduced the problem by using only one external library: ZeroC Ice.
Root CMakeLists.txt
cmake_minimum_required (VERSION 3.3.0)
project (myproject)
# some settings
if (CMAKE_COMPILER_IS_GNUCC)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fmessage-length=0")
endif (CMAKE_COMPILER_IS_GNUCC)
if (CMAKE_COMPILER_IS_GNUCXX)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmessage-length=0")
endif (CMAKE_COMPILER_IS_GNUCXX)
set (CMAKE_VERBOSE_MAKEFILE ON)
set (CMAKE_SHARED_LINKER_FLAGS "-Wl,--export-all-symbols")
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
include_directories (${CMAKE_CURRENT_SOURCE_DIR})
# ZeroC Ice
find_package (Ice 3.6.0 REQUIRED COMPONENTS Ice IceUtil)
include_directories (${Ice_INCLUDE_DIRS})
# libraries and executable
add_subdirectory (a)
add_subdirectory (b)
add_subdirectory (c)
CMakeLists.txt for a
set (a_FILES
stuff.cpp
)
add_library (a STATIC ${a_FILES})
# link to third-party libraries
target_link_libraries (a PUBLIC
${Ice_LIBRARIES}
)
CMakeLists.txt for b
set (b_FILES
more_stuff.cpp
)
add_library (b STATIC ${b_FILES})
# link to a
target_link_libraries (b PUBLIC
a
)
CMakeLists.txt for c
set (c_FILES
main.cpp
)
add_executable (c ${c_FILES})
# link to b
target_link_libraries (c PUBLIC
b
)
In this example, stuff.cpp, more_stuff.cpp and main.cpp use classes from the Ice library. a and strangely even b compile and link without any problem. Only c throws a lot of similar linker errors:
C:/PROGRA~2/ZeroC/ICE-36~1.0/include/Ice/FactoryTableInit.h:27: undefined reference to `_imp___ZN11IceInternal16FactoryTableInitD1Ev'
For me, the CMakeLists.txt setup looks just fine and googleing for days did not change anything. I would appreciate any help!
I'm working on Windows 10 with CMake 3.3.1 and CMake Generator "Eclipse CDT4 - MinGW Makefiles" (g++ version 4.8.1).
Thanks in advance! :)
EDIT:
The g++ linker output for c shows that it tries to link against liba.a libb.a C:/.../ice.lib C:/.../iceutil.lib, which seems just fine to me. Anyways, the undefined reference linker errors occur in header files whose classes are implemented inside of ice.lib, so I don't know what is going wrong here...
a and b are static libraries which are just archieved objects with symbols for external references, no linking happens. c is an executable which finally resolves these symbols and links the dependencies. It doesn't look like CMake transports the Ice library link upwards, you can try to just link her for the executable.
I found a "workaround" by myself: Using another CMake generator. By using Visual Studio as underlying compiler and linker, the issue does not appear. It seems like the problem was mixing up .lib and .a libraries. Since the default Ice installation on windows only provides .lib files, I ended up switching to VS and everything worked.
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.
This question already has answers here:
CMake link to external library
(6 answers)
Closed 2 years ago.
I'm new to building with CMake. I'm using Ubuntu and I have a .cpp file (say xyz.cpp located somewhere in ~/mydir) which links to three custom shared libraries (libA.so, libB.so & libC.so libraries). These three *.so files are located in /usr/local/lib.
I want to create a CMakeLists.txt to get this compiled. The below script throws errors like:
cmake_minimum_required(VERSION 3.11)
project(xyz VERSION 1.0.0 LANGUAGES C CXX)
# Set C standard to C11
set(CMAKE_C_STANDARD 11)
set(SOURCES src/xyz.cpp)
include_directories(/usr/local/include)
#For the shared library:
target_link_libraries(libA -L/usr/local/lib/)
target_link_libraries(libB -L/usr/local/lib/)
target_link_libraries(libC -L/usr/local/lib/)
target_link_libraries(xyz libA libB libC )
add_executable(xyz src/xyz.cpp)
ERROR:
CMake Error at CMakeLists.txt: (target_link_libraries):
Cannot specify link libraries for target "libA" which is not built
by this project.
-- Configuring incomplete, errors occurred!
A couple important notes:
The first argument to target_link_libraries() should be a valid CMake target, created by add_library() or add_executable(). Therefore, any target_link_libraries() call should be placed after the add_executable() call in your code.
You only need one call to target_link_libraries() to link all of your *.so libraries to the executable. The first three calls are unnecessary (and invalid because libA, libB, and libC are all undefined). You should provide the full path to each of these libraries to ensure that the correct library is used.
It is good practice to include the scoping argument in target_* calls, such as target_link_libraries(). This specifies whether the libraries are linked-to (PRIVATE), added to the link interface (INTERFACE), or both (PUBLIC). This distinction becomes more important as your CMake project grows to include more targets.
With these notes, and a few others commented below, your CMake file could look something like this:
cmake_minimum_required(VERSION 3.11)
# No need to specify C and CXX for LANGUAGES here, these are enabled by default!
project(xyz VERSION 1.0.0)
# Set C standard to C11
set(CMAKE_C_STANDARD 11)
set(SOURCES src/xyz.cpp)
include_directories(/usr/local/include)
# Define your executable target. You can pass in the SOURCES variable defined above.
add_executable(xyz ${SOURCES})
# Link the other libraries to your executable, using their full path.
# Note, the '-L' flag is not necessary.
target_link_libraries(xyz PRIVATE
/usr/local/lib/libA.so
/usr/local/lib/libB.so
/usr/local/lib/libC.so
)
I'm trying to use glbinding in my own project. I'm using cmake to build everything. The problem is linker cannot find this library. Probably I don't build library thus it cannot be linked, but I don't know how to achive that.
I've written linking code according to https://github.com/hpicgs/glbinding#linking-binaries.
Cmake:
set(SOURCE_FILES main.cpp)
add_executable(AKOpenGLEngine ${SOURCE_FILES})
set(CMAKE_PREFIX_PATH ${CMAKE_MODULE_PATH} glbinding )
find_package(glbinding REQUIRED)
include_directories(${GLBINDING_INCLUDES})
target_link_libraries(AKOpenGLEngine glbinding ${GLBINDING_LIBRARIES})
Error:
Linking CXX executable AKOpenGLEngine
ld: library not found for -lglbinding
main.cpp:
#include <glbinding/gl/gl.h>
int main(void) {
glbinding::Binding::initialize();
exit(EXIT_SUCCESS);
}
My current project structure:
Have you tried to remove the glbinding from target_link_libraries? ${GLBINDING_LIBRARIES} should be sufficient; it passes <your_specific_file_path_to_glbinding_library> to the linker. With -lglbinding the linker searches for a library within some default directories, your glbinding or build directory not included, thus throwing a library not found. To verify the content of ${GLBINDING_LIBRARIES} you can print it to cmake output, e.g., via message(STATUS ${GLBINDING_LIBRARIES}). However, i also suggest to integrate glbinding as external project as suggested by #janisz.
EDIT: sorry, didn't see the valid, but collapsed answer of #jet47
I downloaded libboost1.50-all in Raspberry Pi and has successfully compiled and execute a program using threads. Libraries were also found in CMake. I then copied the libraries of the boost and its include from /usr/lib and /usr/include/boost respectively to C:\Boost such that the hierarchy becomes:
C:
-> Boost
-> lib
... files
-> include
-> boost
... files
I then used the same CMakeLists.txt and the source code but the library was not found.
NOTE: The cross compiler that I used is fully working and I was able to produce an executable with CMake in Cygwin using the std library. I even specified the location of the library and the user and the root.
Is there anything that I missed out?
cmake_minimum_required(VERSION 2.8)
set(BOOST_ROOT C:/Boost/)
set(BOOST_INCLUDEDIR C:/Boost/include/)
set(BOOST_LIBRARYDIR C:/Boost/lib/)
SET(Boost_DEBUG ON)
find_package(Boost 1.50.0 COMPONENTS thread system)
if (Boost_FOUND)
include_directories (${Boost_INCLUDE_DIRS})
add_executable (thread thread.cpp)
target_link_libraries(thread ${Boost_LIBRARIES})
endif()
Use CMAKE -GUI and then check whether your boost libraries are detected . If not then manually set in the CMAKE-GUI and configure again.
TEMPORARY SOLUTION that I was able to come up to!
I placed the boost lib and include to where the cross compiler is installed since the cross compile was able to link the source code to libstdc++.
The library is placed here:
C:\cygwin\opt\cross\x-tools\arm-unknown-linux-gnueabi\arm-unknown-linux-gnueabi\sysroot\lib
The include files are placed here:
C:\cygwin\opt\cross\x-tools\arm-unknown-linux-gnueabi\arm-unknown-linux-gnueabi\include\c++\4.6.3
The CMakeLists content is as follows now:
cmake_minimum_required(VERSION 2.8)
add_executable (thread main.cpp)
target_link_libraries(thread boost_thread boost_system)
Open Cygwin terminal and invoke cmake there, then make. Viola! It now compiles successfully! :>
The magic lies in here:
target_link_libraries(thread boost_thread boost_system)
I found this in one of the questions here in Stackoverflow where someone said to manually link the libraries..
Even though that it worked, why is it that CMake cannot detect the boost library both in Windows (Cygwin terminal) and Linux (VMware) -- These I tried -- but the library was found in Raspberry Pi (Raspbian) using the same CMakeLists.txt and main.cpp. Isn't it the purpose of CMake is to find the libraries itself? If I were just to link them manually, better do it like this then:
arm-unknown-linux-gnueabi-g++.exe -lboost_thread -lboost_system
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.