3

Lately, I have been using cmake as a generator for my projects. I have successfully generated many vtk and other application projects. However, I now face a problem when trying to link both dynamic and static precompiled libraries. In particular, I have been given some dynamic precompiled third party dlls along with their respective .lib files. Furthermore, I am trying to link some static precompiled libraries (only .lib files) to my project so as to check the software licences.

Let say that my project is called test_example and I have some precompiled dynamic libraries in libs directory. The structure of my project directory is:

Test_example
-/include
-/libs
-/build
-CMakeLists.txt

The CMakeLists.txt for linking the dynamic libaries has the following content:

cmake_minimum_required(VERSION 2.8.9)
project (test_example)
set(CMAKE_BUILD_TYPE Release)

#For the shared libraries:
set (PROJECT_LINK_LIBS dynamic_1.dll dynamic_2.dll )
set (PROJECT_LINK_DIR ${test_example_SOURCE_DIR}/libs/)
set (PROJECT_INCLUDE_DIR ${test_example_SOURCE_DIR}/include/)

link_directories(${PROJECT_LINK_DIR})
include_directories(${PROJECT_INCLUDE_DIR})

add_executable(test_example test_example.cpp)
target_link_libraries(test_example ${PROJECT_LINK_LIBS})  

When I generate the project with this cmake lists, I can successfully use methods from the precompiled dlls. However, I have not found a way to link against my static libraries, as well. Let say I have one static library which is called test_licence.lib. Should I drop it in the libs folder as well and simply refer to it like I do with the dynamic? When I do so and when opening my project solution in Visual Studio, I can see that both dynamic and static libraries have been added to Linker-->Input-->Additional DEpendencies. However, when I am trying to build the project, I have unresolved external dependencies which are methods from the static lib.
Does any of you have any idea what would be the most efficient way to accomplish that? Many thanks in advance!

GioR
  • 596
  • 2
  • 8
  • 19

2 Answers2

3

There is a couple of issues here.

Unlike to what you may be used to from VS, CMake prefers absolute paths for linking, instead of setting a link directory and giving the relative path.

Also, you do not link against .dll files. Dlls are loaded at runtime, not at link time. Many dlls are shipped with import libraries (with a .lib ending), that handle the runtime loading automatically for you. These are the ones you should link against.

Also try not to hardcode libraries in CMake code. The problem here is that if something goes wrong, you end up with a cryptic linker error. You should use find_library instead, which will usually make CMake complain early if something is off.

A cleaner version of your CMake script would be something like

cmake_minimum_required(VERSION 2.8.9)
project (test_example)

# note setting the build type does nothing on a visual studio build
# and should probably be left to the user for other generators
set(CMAKE_BUILD_TYPE Release)

#For the shared libraries:
# this call will succeed if it finds a dynamic_1.lib file
find_library(DYNAMIC_LIB1 dynamic_1 HINTS ${test_example_SOURCE_DIR}/libs)
if(NOT DYNAMIC_LIB1)
    message(FATAL_ERROR "Library dynamic_1 was not found!")
endif()
find_library(DYNAMIC_LIB2 dynamic_2 HINTS ${test_example_SOURCE_DIR}/libs)
if(NOT DYNAMIC_LIB2)
    message(FATAL_ERROR "Library dynamic_2 was not found!")
endif()

# for the static libraries:
# basically the same; again this looks for a static_1.lib file
find_library(STATIC_LIB1 static1 HINTS ${test_example_SOURCE_DIR}/libs)
if(NOT STATIC_LIB1)
    message(FATAL_ERROR "Library static_1 was not found!")
endif()


set (PROJECT_INCLUDE_DIR ${test_example_SOURCE_DIR}/include/)

include_directories(${PROJECT_INCLUDE_DIR})

add_executable(test_example test_example.cpp)
target_link_libraries(test_example ${DYNAMIC_LIB1} ${DYNAMIC_LIB2} ${STATIC_LIB1})

Double check in Visual Studio that all libraries were added as linker inputs, as expected. If you still get linker errors, it means that something is wrong with your third-party .lib file. Open a new question with the exact linker error that you get.

Community
  • 1
  • 1
ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • Hi, thanks a lot for your valuable answer. Your logic is much more clear. However, I still get the same unresolved externals which are coming from a specific static library. I guess it may be caused by the way this library has been built. For testing, I have produced one static and one dynamic library and with your way I can link them successfully. By the way, if I have a lot of dynamic and static libraries in the lib directory, is there any shorter way to link against all the *.lib files in libs direcory? I know there is a file (GLOB ...) command. How can I use it to link all my .lib files? – GioR Jul 19 '16 at 08:37
  • 1
    @gioR Please don't try to glob for `.lib` files :) If it's really a uniform process for you, where the only variable is the library name, put all the libraries in a list and [`foreach`](https://cmake.org/cmake/help/v3.5/command/foreach.html) over them. But don't try to have the build system figure out the library names. That's just asking for trouble down the road. As for the linker error you still get: Yes it's probably an issue with how the lib was built. As I said, feel free to open another question if you need more help with that. – ComicSansMS Jul 19 '16 at 10:43
  • [ComicSansMS](http://stackoverflow.com/users/577603/comicsansms) many thanks once again. The [foreach](https://cmake.org/cmake/help/v3.5/command/foreach.html) does the job now. As for the link errors, I found out that these are probably coming from the VS version that I am using to build my project. I have error LNK2001: unresolved external symbol __imp___iob_fuc, unresolved external symbol _sprintf, etc. I read this can be solved by recompiling these libs in VS 14 (2015). – GioR Jul 19 '16 at 13:50
  • @gioR That sounds plausible. Compiled library files [are in general not compatible](http://stackoverflow.com/questions/1600399/are-c-libs-created-with-different-versions-of-visual-studio-compatible-with-ea?noredirect=1&lq=1) between different Visual Studio versions. – ComicSansMS Jul 19 '16 at 15:44
1

Here is how we are doing it:

Firstly, use add_library with final arguments STATIC IMPORTED. Then subsequently use set_property to set the IMPORTED_LOCATION property, which is a path to the built library. For example, we pull in gtest like so:

add_library(gtest UNKNOWN IMPORTED)
set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${binary_dir}/googlemock/gtest/${CMAKE_FIND_LIBRARY_PREFIXES}gtest.a)

Then, gtest is a known library in your build system and you can link with it normally later on by just doing

target_link_libraries(target-name gtest)

See also: Cmake imported libraries documenation

Segfault
  • 8,036
  • 3
  • 35
  • 54