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.
Related
I have trouble including and linking SDL_ttf and SDL_image to my project. I have a cmake file that works only for SDL and SDL_gfx on Clion. I guess the problem is from the cmake file.
I got several errors when I build the project:
undefined reference to `FUNCTION'
The libraries which I used for my project: https://github.com/satayyeb/sdl2-libraries
sdl2 folder (where library files are located) tree:
sdl2-|
|-sdl2-image-include
| |-SDL_image.h
|
|-sdl2-image-lib
| |-libSDL2_image.a
| |-libSDL2_image.dll.a
| |-libSDL2_image.la
|
|-sdl2-ttf-include
| |-SDL_ttf.h
|
|-sdl2-ttf-lib
|-libSDL2_ttf.a
|-libSDL2_ttf.dll.a
|-libSDL2_ttf.la
CMakeLists.txt :
set(SOURCE src/main.c)
set(PROJECT_NAME state.SAT)
cmake_minimum_required(VERSION 3.19)
project(${PROJECT_NAME} C)
set(CMAKE_C_STANDARD 11)
set(SDL2_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-include")
set(SDL2_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-lib")
set(SDL2_GFX_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-gfx-include")
set(SDL2_GFX_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-gfx-lib")
set(SDL2_IMAGE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-image-include")
set(SDL2_IMAGE_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-image-lib")
set(SDL2_TTF_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-ttf-include")
set(SDL2_TTF_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-ttf-lib")
set(SDL2_FLAGS "-mwindows -Wl,--no-undefined -static-libgcc")
add_library(SDL2 STATIC IMPORTED)
add_library(SDL2main STATIC IMPORTED)
add_library(SDL2_GFX STATIC IMPORTED)
add_library(SDL2_IMAGE STATIC IMPORTED)
add_library(SDL2_TTF STATIC IMPORTED)
set_property(TARGET SDL2 PROPERTY IMPORTED_LOCATION "${SDL2_LIB_DIR}/libSDL2.a")
set_property(TARGET SDL2main PROPERTY IMPORTED_LOCATION "${SDL2_LIB_DIR}/libSDL2main.a")
set_property(TARGET SDL2_GFX PROPERTY IMPORTED_LOCATION "${SDL2_GFX_LIB_DIR}/libsdl-gfx.a")
set_property(TARGET SDL2_IMAGE PROPERTY IMPORTED_LOCATION "${SDL2_IMAGE_LIB_DIR}/libSDL2_image.a")
set_property(TARGET SDL2_TTF PROPERTY IMPORTED_LOCATION "${SDL2_TTF_LIB_DIR}/libSDL2_ttf.a")
set(SDL2_LIBS SDL2 SDL2main SDL2_GFX SDL2_TTF SDL2_IMAGE m dinput8 dxguid dxerr8 user32 gdi32 winmm imm32 ole32 oleaut32 shell32 version uuid)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${SDL2_FLAGS}")
file(GLOB_RECURSE SOURCE "src/*.c" "src/*.h")
add_executable("${PROJECT_NAME}" "${SOURCE}")
include_directories(${SDL2_INCLUDE_DIR} ${SDL2_GFX_INCLUDE_DIR} ${SDL2_IMAGE_INCLUDE_DIR} ${SDL2_TTF_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBS})
Short answer is libraries you use are incompatible, as SDL2_ttf and SDL2_image are built against newer version of SDL2 than you have in your archive (linked in question). Get newer SDL2, or older SDL2_ttf and SDL2_image, then you don't have to change anything. Correction after writing lengty explaination i noted that in your archive even static and dynamic versions of the same library have different versions; i would suggest redownloading entire things from SDL website, because what you have is a mess.
Note that my answer doesn't cover your CMakeLists.txt. What you have is very windows-specific; if you don't care about other systems - be it your way. Your program could be built with CMakeLists you have in your original question thought, that's not a source of your problem.
Whether you want to use static libraries in the first place is questionable. If there is no good reason to specifically use static library i suggest you switch to dynamic ones, i.e. linking with .dll.a (or just .dll directly, mingw toolchain supports that). That way you don't need your long list of dinput8 dxguid dxerr8 user32 gdi32 winmm imm32 ole32 oleaut32 shell32 version uuid, which are, if you're not aware, dependencies of SDL2 version you use. Meaning if you e.g. update your static libSDL2.a, this list could be different as it now depends on some more things (that is not a theoretical - latest SDL 2.0.20 actually have longer dependency list).
Explaination of library incompatibility (very long, quite technical, and mostly mingw/gcc specific; if you're not interested in that details, just check final note at the end):
You mentioned undefined reference to 'FUNCTION' in your question, but which FUNCTION is very important; don't omit that information from your future questions, if you ever have that. Thankfully you later provided a link to full libraries archive, and we can see the result of that build attempt ourselves, which is:
../sdl2/sdl2-ttf-lib/libSDL2_ttf.a(libSDL2_ttf_la-SDL_ttf.o): In function `RWread':
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:1583: undefined reference to `SDL_RWseek'
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:1587: undefined reference to `SDL_RWread'
../sdl2/sdl2-ttf-lib/libSDL2_ttf.a(libSDL2_ttf_la-SDL_ttf.o): In function `TTF_CloseFont':
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:2544: undefined reference to `SDL_RWclose'
../sdl2/sdl2-ttf-lib/libSDL2_ttf.a(libSDL2_ttf_la-SDL_ttf.o): In function `TTF_OpenFontIndexDPIRW':
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:1614: undefined reference to `SDL_RWtell'
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:1647: undefined reference to `SDL_RWsize'
/Users/valve/release/SDL_ttf/SDL2_ttf-2.0.18-source/foo-x64/../SDL_ttf.c:1603: undefined reference to `SDL_RWclose'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__vecang':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2096: undefined reference to `SDL_acosf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__addActive':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:873: undefined reference to `SDL_floorf'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:874: undefined reference to `SDL_floorf'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:871: undefined reference to `SDL_floorf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__parseStrokeDashArray':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:1722: undefined reference to `SDL_fabsf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__parseRect':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2364: undefined reference to `SDL_fabsf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__parseCircle':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2413: undefined reference to `SDL_fabsf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__pathArcTo':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2112: undefined reference to `SDL_fabsf'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2113: undefined reference to `SDL_fabsf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o):/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvg.h:2115: more undefined references to `SDL_fabsf' follow
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__curveDivs':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:598: undefined reference to `SDL_acosf'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:599: undefined reference to `SDL_ceilf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__roundJoin':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:548: undefined reference to `SDL_atan2f'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:549: undefined reference to `SDL_atan2f'
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:556: undefined reference to `SDL_ceilf'
../sdl2/sdl2-image-lib/libSDL2_image.a(IMG_svg.o): In function `nsvg__flattenShapeStroke':
/Users/valve/release/SDL_image/SDL2_image-2.0.5-source/foo-x64/../nanosvgrast.h:786: undefined reference to `SDL_fmodf'
Ok, not a long list actually. So what's undefined? Few SDL_RW* functions and few SDL math functions. Mind you, that's not the only SDL functions SDL2_ttf and SDL2_image use, everything else is found. Why would that functions not be found in your build?.. Linker says there are not functions with that name, well, are there? Since you use mingw toolchain, you should have access for a bit more tools than just a compiler, e.g. nm tool (in console):
> nm -a sdl2/sdl-libs/libSDL2.a | grep SDL_RWseek
>
(use findstr if you don't have grep) Yeah, not there. Well, we have SDL2 source, including version histroy, let's see why could that be.
Let's start with e.g. SDL_RWseek. There is a function with that name in SDL2, which we can view at https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_rwops.h#L401 (just did a search for function name on github and opened a header file). And the comment before that function have an answer: Prior to SDL 2.0.10, this function was a macro. ... This function is available since SDL 2.0.10.. Oh. So whatever you have, seems to be lower than 2.0.10, and SDL2_ttf clearly was build for something newer, where this already was a function.
Ok, what about math functions used by SDL2_image. Well, github search again points to https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_stdinc.h#L572 , which seems to say that it was added at 2.0.2; unfortunately, comment only covers SDL_acos function, with others being uncommented. However, if you switch github view to "blame" (this is not specific to github by the way) to see when each line was last modified, you can see that a lot of those math functions were added with different commits, and across multiple years. What was not found? SDL_fmodf - added 4 years ago, first release is 2.0.8. Same for all other "undefined" functions.
So, those libraries require SDL2 2.0.10 and 2.0.8. What version you have? I don't see a quick way to check to be sure, but there is a SDL_GetVersion function, why don't make minimal program that calls it:
#include "SDL.h"
#include <stdio.h>
int main(int argc, char **argv) {
SDL_version v = {};
SDL_GetVersion(&v);
printf("%d.%d.%d\n", v.major, v.minor, v.patch);
return 0;
}
Build&execute (important part is it have to be built within the same environment you have! so, for all intents and purposes, this is a content of your main.c),
2.0.7
Oh. So yeah, there is no way it can work with that version, there are no functions it wants to call.
In your own answer you said you have built it by adding links to libSDL2_ttf.dll.a and libSDL2_image.dll.a, presumably due to library order. First, library order only matters for dinamic libraries (and even there - not for every linker implementation), so no that's not it. But why did it build? We're going to windows specific territory here, with its concept of "import libraries"; .dll.a is exactly that, a stub that contains symbols with "this will be loaded later at runtime". What it does is makes your resulting program depending on e.g. SDL2_ttf.dll. Dynamic libraries are very similar to executables and actually have dependency list for themselves (static libraries do not - that's why you have your long list of SDL2 dependencies in your cmakelists), so there is an entry in SDL2_ttf.dll that says it depends on SDL2.dll. Once again, you can check that with your trusty compiler toolchain:
> ldd sdl2/sdl2-ttf-bin/SDL2_ttf.dll | grep SDL2
SDL2.dll => /c/WINDOWS/SYSTEM32/SDL2.dll (0x5ccb0000)
What it means is it depends on SDL2.dll, and if i launch it in my current environment, it'll use C:\Windows\system32\SDL2.dll that I have in my default dll search path (your environment could be different). If you check your resulting exe with the same ldd tool, you'll see you now depend on SDL2_ttf.dll, but due to that - to SDL2.dll as well. So now you have statically linked SDL2 in your program, but also pull dynamic SDL2.dll due to dependency. So it builds, maybe even runs, but it is hard to imagine it would be working as intended. I'm pretty sure the moment you'll try to actually do texture or font processing it'll all collapse.
While writing all that I figured out your SDL2.dll is actually 2.0.20, but libSDL2.a is 2.0.7. Maybe best course of action is nuking your entire library archive and redownloading everything from SDL website.
I found the problem:
In sdl2-image-lib and sdl2-ttf-lib directory there are libSDL2_image.dll.a and libSDL2_ttf.dll.a binary files that must be linked instead of libSDL2_image.a and libSDL2_ttf.a files.
dll files must be copied to the directory where the EXE file is located. the dll files are in bin folders of the library.
The libraries which I used for my project:
https://github.com/satayyeb/sdl2-libraries
and the modified CMakeLists.txt:
set(SOURCE src/main.c)
set(PROJECT_NAME island_soldier)
cmake_minimum_required(VERSION 3.19)
project(${PROJECT_NAME} C)
set(CMAKE_C_STANDARD 11)
set(SDL2_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-include")
set(SDL2_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-lib")
set(SDL2_GFX_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-gfx-include")
set(SDL2_GFX_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-gfx-lib")
set(SDL2_IMAGE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-image-include")
set(SDL2_IMAGE_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-image-lib")
set(SDL2_TTF_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-ttf-include")
set(SDL2_TTF_LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sdl2/sdl2-ttf-lib")
set(SDL2_FLAGS "-mwindows -Wl,--no-undefined -static-libgcc")
add_library(SDL2 STATIC IMPORTED)
add_library(SDL2main STATIC IMPORTED)
add_library(SDL2_GFX STATIC IMPORTED)
add_library(SDL2_IMAGE_DLL STATIC IMPORTED)
add_library(SDL2_TTF_DLL STATIC IMPORTED)
set_property(TARGET SDL2 PROPERTY IMPORTED_LOCATION "${SDL2_LIB_DIR}/libSDL2.a")
set_property(TARGET SDL2main PROPERTY IMPORTED_LOCATION "${SDL2_LIB_DIR}/libSDL2main.a")
set_property(TARGET SDL2_GFX PROPERTY IMPORTED_LOCATION "${SDL2_GFX_LIB_DIR}/libsdl-gfx.a")
set_property(TARGET SDL2_IMAGE_DLL PROPERTY IMPORTED_LOCATION "${SDL2_IMAGE_LIB_DIR}/libSDL2_image.dll.a")
set_property(TARGET SDL2_TTF_DLL PROPERTY IMPORTED_LOCATION "${SDL2_TTF_LIB_DIR}/libSDL2_ttf.dll.a")
set(SDL2_LIBS mingw32 SDL2 SDL2main SDL2_GFX SDL2_TTF_DLL SDL2_IMAGE_DLL m dinput8 dxguid dxerr8 user32 gdi32 winmm imm32 ole32 oleaut32 shell32 version uuid)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${SDL2_FLAGS}")
file(GLOB_RECURSE SOURCE "src/*.c" "src/*.h")
IF(WIN32)
SET(GUI_TYPE WIN32)
ENDIF(WIN32)
IF (APPLE)
SET(GUI_TYPE MACOSX_BUNDLE)
ENDIF (APPLE)
add_executable("${PROJECT_NAME}" ${GUI_TYPE} "${SOURCE}")
include_directories(${SDL2_INCLUDE_DIR} ${SDL2_GFX_INCLUDE_DIR} ${SDL2_IMAGE_INCLUDE_DIR} ${SDL2_TTF_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBS})
I want to statically compile my program against another static library, for this example I'm using zeromq. Here is my CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
add_executable( test test.cpp )
find_library(ZMQ NAMES libzmq.a)
message(STATUS ${ZMQ})
target_link_libraries( test ${ZMQ} )
It finds the .a file when I run mkdir build && cd build && cmake ..
-- /usr/local/lib/libzmq.a
However, if I examine the link.txt file, the library is dynamically linked:
/usr/bin/c++ CMakeFiles/test.dir/test.cpp.o \
-o test -rdynamic /usr/local/lib/libzmq.a
The weird bit is that if I move the file to a different directory, say /usr/lib and run cmake .. once more, it locates the new path to the library:
-- /usr/lib/libzmq.a
But now it has magically changed to static linking:
/usr/bin/c++ CMakeFiles/test.dir/test.cpp.o \
-o test -rdynamic -Wl,-Bstatic -lzmq -Wl,-Bdynamic
The same thing applies to other libraries I'm linking to.
Why are all my libraries in /usr/local/lib being dynamically linked?
You should not use the path directly, and create an imported target instead, so you can explicitly declare it static:
cmake_minimum_required(VERSION 2.6)
add_executable( test test.cpp )
find_library(zmq_location NAMES libzmq.a)
message(STATUS ${zmq_location})
add_library(zmq STATIC IMPORTED)
set_target_properties(zmq PROPERTIES IMPORTED_LOCATION ${zmq_location})
target_link_libraries( test zmq )
This may lead to a situation where the library appears to be linked dynamically, but the cmake source code has the answer:
If the target is not a static library make sure the link
type is shared. This is because dynamic-mode linking can handle
both shared and static libraries but static-mode can handle only
static libraries. If a previous user item changed the link type to
static we need to make sure it is back to shared.
Essentially, it's letting the linker handle detecting that the library is static if currently in dynamic-mode.
The answer to my original question about the difference between /usr/local/lib and /usr/lib is that, by default, /usr/local/lib is not one of the implicit link directories. Therefore, a quick fix is to include this line in the config:
set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES /usr/local/lib ${CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES})
However, as pointed out in this other answer, referencing files directly is not the way to go, instead one should use add_library.
Here is my DEMO:
find_library(VLQ_LIB NAMES vlq-shared PATHS /usr/local/lib/ REQUIRED)
find_path(VLQ_HEADER vlq.hpp /usr/local/include)
target_link_libraries(myproj PUBLIC VLQ_LIB)
target_include_directories(myproj PUBLIC VLQ_HEADER)
but during run, you still need to copy the shared lib to /lib/ folder, for static lib, you can keep it under /usr/lib
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.
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 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.