Cmake target_link_libraries not linking my library

58,076

For the original CMakeLists.txt file, the problem is not with target_link_libraries but with add_executable

add_executable(uart ${uart_SRCS})

where did you set your uart_SRCS variable? Do you have

set(uart_SRCS src/blahblah.cpp src/somethingblahblah.cpp)

I think you might misunderstand what add_subdirectory does. It does not add the source files inside. It tells CMake to descend into that folder and look for another CMakeLists.txt. You typically use it when you have a sub-project inside of your project folder.

If you have many source files which you don't want to manually set, you can also do

file(GLOB uart_SRCS src/*.cpp src/*.c)

The downside is you need to manually re-run CMake in order for it to detect new files. See Jack's comment on why this might not be what you want to use.

Your CMakeLists.txt will most likely be

project(uart)

find_package(Qt4 REQUIRED)
include (${QT_USE_FILE})
find_package(KDE4 REQUIRED)
include (KDE4Defaults)

include_directories( ${KDE4_INCLUDES} ${QT_INCLUDES} src/include src/include/QSerialDevice )
link_directories(/usr/lib) 

file(GLOB uart_SRCS src/*.cpp src/*.h)
file(GLOB uart_HDRS include/*.h include/QSerialDevice/*.h)

find_library(SERIALDEVICE_LIB qserialdeviced)

add_executable(uart ${uart_SRCS} ${uart_HDRS})
target_link_libraries(uart ${SERIALDEVICE_LIB} ${QT_LIBRARIES})
Share:
58,076
j4x
Author by

j4x

I believe I have climbed as many steps as possible in C++ programming a few years ago when I decided to switch career from any-level-developer to FAE in Embedded Systems. Five years later I may have to look for updates but having to keep my kids fed might impose a barrier... Ok. I can take any free lancing from Embedded Systems to Expert Systems and Data/Signal/Image Processing if it makes everybody happy.

Updated on July 09, 2022

Comments

  • j4x
    j4x almost 2 years

    I'll begin stating that I'm almost complete dumb in Cmake matter.

    I have the following CMakeLists.txt for a Kdevelop 4.1 project:

    project(uart)
    
    find_package(KDE4 REQUIRED)
    include (KDE4Defaults)
    
    include_directories( ${KDE4_INCLUDES} ${QT_INCLUDES} src/include src/include/QSerialDevce )
    
    add_subdirectory(doc)
    add_subdirectory(src)
    add_subdirectory(icons)
    
    link_directories(/usr/lib)
    
    find_library(SERIALDEVICE_LIB qserialdeviced)
    
    add_executable(uart ${uart_SRCS})
    target_link_libraries(uart ${SERIALDEVICE_LIB})
    

    When I try to build my project I see:

    uart/build> make -j2
    -- Found Qt-Version 4.6.3 (using /usr/bin/qmake-qt4)
    -- Found X11: /usr/lib64/libX11.so
    -- Found KDE 4.5 include dir: /usr/include/kde4
    -- Found KDE 4.5 library dir: /usr/lib64/kde4/devel
    -- Found the KDE4 kconfig_compiler4 preprocessor: /usr/bin/kconfig_compiler4
    -- Found automoc4: /usr/bin/automoc4
    CMake Error at CMakeLists.txt:16 (add_executable):
      add_executable called with incorrect number of arguments
    
    
    CMake Error: Attempt to add link library "/usr/lib/libqserialdeviced.so" to target "uart" which is not built by this project.
    -- Configuring incomplete, errors occurred!
    make: *** [cmake_check_build_system] Error 1
    *** Failed ***
    

    Everything I read says that add_executable and target_link_libraries should look like the last two lines of my file:

    add_executable(uart ${uart_SRCS})
    target_link_libraries(uart ${SERIALDEVICE_LIB})
    

    If I change those two lines of CMakeLists.txt leaving it as:

    project(uart)
    
    find_package(KDE4 REQUIRED)
    include (KDE4Defaults)
    
    include_directories( ${KDE4_INCLUDES} ${QT_INCLUDES} src/include src/include/QSerialDevce )
    
    add_subdirectory(doc)
    add_subdirectory(src)
    add_subdirectory(icons)
    
    link_directories(/usr/lib)
    
    find_library(SERIALDEVICE_LIB qserialdeviced)
    
    target_link_libraries(${SERIALDEVICE_LIB})
    

    I see:

    uart/build> make -j2
    -- Found Qt-Version 4.6.3 (using /usr/bin/qmake-qt4)
    -- Found X11: /usr/lib64/libX11.so
    -- Found KDE 4.5 include dir: /usr/include/kde4
    -- Found KDE 4.5 library dir: /usr/lib64/kde4/devel
    -- Found the KDE4 kconfig_compiler4 preprocessor: /usr/bin/kconfig_compiler4
    -- Found automoc4: /usr/bin/automoc4
    -- Configuring done
    -- Generating done
    -- Build files have been written to: uart/build
    [ 11%] Built target doc-handbook
    [ 11%] Built target uart_automoc
    Linking CXX executable uart
    CMakeFiles/uart.dir/uart.o: In function `uart::setupSerial()':
    uart/src/uart.cpp:126: undefined reference to `AbstractSerial::AbstractSerial(QObject*)'
    CMakeFiles/uart.dir/uart.o: In function `uart::setupEnumerator()':
    uart/src/uart.cpp:108: undefined reference to `SerialDeviceEnumerator::SerialDeviceEnumerator(QObject*)'
    CMakeFiles/uart.dir/uart.o: In function `uart::setupSerial()':
    uart_/uart/src/uart.cpp:136: undefined reference to `AbstractSerial::enableEmitStatus(bool)'
    CMakeFiles/uart.dir/uart.o: In function `uart::setupEnumerator()':
    uart_/uart/src/uart.cpp:112: undefined reference to `SerialDeviceEnumerator::setEnabled(bool)'
    collect2: ld returned 1 exit status
    make[2]: *** [src/uart] Error 1
    make[1]: *** [src/CMakeFiles/uart.dir/all] Error 2
    make: *** [all] Error 2
    *** Failed ***
    

    That clearly shows that target_link_libraries is not linking my qserialdeviced.

    qserialdeviced is at /usr/lib/libqserialdeviced.so.1.0.0, correctly simlinked to /usr/lib/libqserialdeviced.so and easily found if I manually add it in the Makefile.

    I obviously tried:

    target_link_libraries(-lqserialdeviced)
    

    with no change.

    I also tried:

    if ("${SERIALDEVICE_LIB}" STREQUAL "SERIALDEVICE_LIB-NOTFOUND")
        message(FATAL_ERROR "'qserialdeviced' wasn't found!")
    else()
        message("'qserialdeviced' found: " ${SERIALDEVICE_LIB})
    endif ()
    

    But this test succeeds. The library is found:

    'qserialdeviced' found: /usr/lib/libqserialdeviced.so
    

    Can anybody please help me to understand what happens here?

    I am using Linux Fedora 13, cmake version 2.8.0, gcc (GCC) 4.4.5 20101112 (Red Hat 4.4.5-2) and kdevelop-4.1.0-1.fc13.x86_64.

    Thanks i advance.


    EDIT:

    As suggested by @DatChu, I split my CMakeLists.txt across my subdirectories and everything makes sense to me now.

    Thanks everbody!

  • j4x
    j4x about 13 years
    No, I haven't set it, but tried to do something like add_executable(uart src/main.cpp src/uart.cpp ...). I stopped because I find no much clever to repeat the names of all source files after add_subdirectory(src). Is there a better way of doing this? I must mention had erros even when I tried adding all the ".cpp" files manually. If you want, I can put the error in my post. Thanks.
  • Dat Chu
    Dat Chu about 13 years
    If you could update the original CMakeLists.txt with what you have right now + the files in your project, I can suggest what you need for CMake
  • Jack Kelly
    Jack Kelly about 13 years
  • Dat Chu
    Dat Chu about 13 years
    @Jack: Aside from the downside of CMake not detecting file changes, is there another reason against file(GLOB ...)?
  • Jack Kelly
    Jack Kelly about 13 years
    @Dat Chu: From the top of my head: you run the risk of accidentally adding files to targets that don't need them. Because CMake doesn't share object files between targets, this always adds redundant compilation, lengthening the build time. Besides, once a project's reasonably mature, new files don't come along all that often.
  • Dat Chu
    Dat Chu about 13 years
    @Jack Kelly: Thanks. The way that I structure my project has no extra files in each folder. Plus, the argument of lacking of file changes when the project matures go both ways. I have updated my answer to note this.
  • j4x
    j4x about 13 years
    I'm starting to understand what you all are saying. It reminds me one episode where Simpson's dog began to understand what people say. Unfortunately I'll have to wait til tomorrow to give a new try. Thanks people!
  • j4x
    j4x about 3 years
    Have you declared your "c" functions (in your ".h") inside #if defined(__cplusplus) extern "C"? isocpp.org/wiki/faq/mixing-c-and-cpp#include-c-hdrs-personal
  • skittlebiz
    skittlebiz about 3 years
    they are only declared inside extern (without the "C"). Adding "C" will require the c files to be compiled with a C++ compiler, hence somewhat reiterating my "solution." (Also, rather than changing the filename to end in cpp, I believe you can also change a file's property by using: set_source_files_properties(file0.c file1.c PROPERTIES LANGUAGE CXX) )
  • j4x
    j4x about 3 years
    No, you don't. The #ifdef directives in the example I gave you take care of this. You can use either C or C++ compiler on your headers. Furthermore, why do you need to declare a function as pure extern in your header?