65

I Ubuntu, I am learning about cmake and make, and just trying a simple example. I have two directories: src and build. In src, I have two files: main.cpp, and CMakeLists.txt, which has (only) the following text:

add_executable(test main.cpp)
link_directories(/usr/lib/x86_64-linux-gnu)
target_link_libraries(test protobuf)

In /usr/lib/x86_64-linux-gnu, there is a shared library called libprotobuf.so, which I want to link against. My main.cpp uses functions in this library, by including the releveant header file, #include <google/protobuf/message.h>.

Now, in my build directory, I run cmake ../src, and then make. However, I then get linker errors telling me that there are undefined references to some of the functions in the protobuf library. If I do a search through all the files and subdirectories in build, there is not mention of anything related to protobuf.

However, if I remove the link_directories line in my CMakeLists.txt file, and instead write the full path to the library when specifying the executable, i.e. target_link_libraries(test /usr/lib/x86_64-linux-gnu/libprotobuf.so), it compiles and links fine.

Why is link_directories not allowing cmake to find this library?

Karnivaurus
  • 22,823
  • 57
  • 147
  • 247
  • 5
    The cmake manual says about link_libraries "The command will apply only to targets created after it is called." Is the target created with the add_executable() command? Maybe you need to move the add_executable() command after the link_directories(). – Alain Jul 15 '15 at 19:11
  • 1
    If I do this, I get the error `Cannot specify link libraries for target "test" which is not built by this project.` – Karnivaurus Jul 15 '15 at 19:25
  • 2
    Just to be sure: the order of your commands is now 1) link_directories(), 2) add_executable(), and 3) target_link_libraries()? – Alain Jul 15 '15 at 19:54
  • Yes, in that order -- and it does not add libprotobuf.so to any of the files generated by cmake – Karnivaurus Jul 15 '15 at 20:01
  • 3
    Use `find_package(Protobuf)` instead of trying to locate it directly. See http://www.cmake.org/cmake/help/v3.3/module/FindProtobuf.html for further information. – tamas.kenez Jul 15 '15 at 20:08
  • 1
    And if find_package() does not work for you, the next best option would be find_library(PROTOBUF protobuf /usr/lib/x86_64-linux-gnu) and then use ${PROTOBUF} in your target_link_libraries() command. Looking around the net you will see many people recommending (sometimes strongly) against using link_directories(). Even the CMake doc seems to recommend using find_library() instead. – Alain Jul 15 '15 at 20:15

4 Answers4

93

Make sure that your call to link_directories takes place before your call to the relevant add_executable.

I had mistakenly believed it only needed to be before the call to target_link_libraries, but that's not the case. After moving the call, the library is linked properly.

aardvarkk
  • 14,955
  • 7
  • 67
  • 96
  • 1
    Thank you for you answer ! I have a FindSomething.cmake file that uses link_directories and in fact when using target_link_libraries on a static library to link it with library "Something" then link that static library with a binary program, the linker fails to link the libraries because link_directories is not taken in account... – Aminos Nov 22 '19 at 11:08
  • 1
    This is the correct answer to the problem. The keyword here is `before your call` ... – daparic Jul 09 '20 at 10:02
90

Do not use link_directories like this in CMake.

This is a common beginner's mistake, as many other build environments work like this, but in CMake it's just asking for trouble. Even the official documentation specifically advises against it:

Note that this command [link_directories] is rarely necessary. Library locations returned by find_package() and find_library() are absolute paths. Pass these absolute library file paths directly to the target_link_libraries() command. CMake will ensure the linker finds them.

So instead, always pass absolute paths to target_link_libraries and use find_library to resolve the link directory:

find_library(PROTOBUF_LIBRARY protobuf HINTS /usr/lib/x86_64-linux-gnu)
target_link_libraries(test PUBLIC ${PROTOBUF_LIBRARY})

This has the huge benefit that you will probably get a diagnostic at CMake configure time if the expected library cannot be found, instead of a random linker error at compile time. Also, this allows the user to specify a library location via the GUI if the target machine has a non-standard directory layout.

So if it doesn't work right away, be sure to check the result of the find_library call and consult the official documentation to track down why it doesn't find your library as intended.

starball
  • 20,030
  • 7
  • 43
  • 238
ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • 30
    ... but, why doesn't it work? I realize it's advised against, but shouldn't it still work? – aardvarkk Nov 11 '16 at 19:11
  • If you use pkg_check_modules to find a library you already have all the information you need and it makes sense to use pkg_check_modules. – wojciii May 25 '17 at 19:47
  • 5
    @aardvarkk the doc says `The command will apply only to targets created after it is called. ` – landerlyoung Mar 04 '20 at 11:56
  • FYI on Windows I have found you may need to add `/*` to the hint if the library is in a subdirectory – Den-Jason Oct 13 '20 at 14:17
9

Make sure that the order will be link_directories, set PROJECT_LINK_LIBS, add_executable and then target_link_libraries.

Below is example to demonstarte it:

cmake_minimum_required(VERSION 2.8.9)
project (Logging)
include_directories(include)
file(GLOB LOGGINGSOURCES "libsrc/*.cpp")
file(GLOB SOURCES "src/*.cpp")
add_library(convertString SHARED ${LOGGINGSOURCES})
install(TARGETS convertString DESTINATION /root/Deepak/)
link_directories( /root/Deepak/ )
set(PROJECT_LINK_LIBS libconvertString.so)
add_executable(hello ${SOURCES})
target_link_libraries(hello ${PROJECT_LINK_LIBS} )
user6882413
  • 331
  • 2
  • 9
1

Perhaps it's very old topic but none of proposed solutions worked for me. So I had to make my own dirty hack. I do crosscompiling with buildroot and include toolchainfile.cmake.

#...
set(LIB_PATH ${PROJECT_SOURCE_DIR}/relative/path/to/your/lib)
#...
include_directories(/path/to/library/include)
set(LIB_MYLIB ${LIB_PATH}/libmylib.so)
#...
add_executable(${PROJECT_NAME} ${APP_SOURCES})
target_link_libraries(${PROJECT_NAME}
        ${LIB_MYLIB}
)

Hope this will help

Peter
  • 123
  • 1
  • 6