robert
robert

Reputation: 3726

Undefined reference with cmake using own libraries

I have the folllowing directory structure:

-project

  -helper
    -build
    -include
      -h_a.h
      -h_b.h
    -src
      -h_a
        -h_a.cpp
        -CMakeLists.txt
      -h_b
        -h_b.cpp
        -CMakeLists.txt
      -CMakeLists.txt

  -proj_c
    -build
    -src
      -main.cpp
      -CMakeLists.txt

In the helper project two libraries are generated: libh_a.a and libh_b.a. libh_a.a is used to build libh_b.a. The files are the following:

helper/src/CMakeLists.txt:

cmake_minimum_required(VERSION 2.6)

project(helper)

set(CMAKE_CXX_FLAGS "-Wall -g -std=c++11")

add_subdirectory(h_a)
add_subdirectory(h_b)

helper/src/h_a/CMakeLists.txt:

project(h_a)

add_library(h_a h_a.cpp)

helper/src/h_a/h_a.cpp

void func_a(){}

helper/src/h_b/CMakeLists.txt:

project(h_b)

add_library(h_b h_b.cpp)

target_link_libraries(
    h_b STATIC
    h_a
)

helper/src/h_b/h_b.cpp:

#include "../../include/h_a.h"

void func_b(){
    func_a();   
}

proj_c/src/CMakeLists.txt:

cmake_minimum_required(VERSION 3.2)

project(proj_c)

find_library(h_a PATHS ../../helper/build/h_a)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../helper/build/h_a)

find_library(h_b PATHS ../../helper/build/h_b)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../helper/build/h_b)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "../bin/")

add_executable(proj_c main.cpp)

target_link_libraries(
    proj_c
    h_a
    h_b
)

proj_c/src/main.cpp:

#include "../../helper/include/h_b.h"

int main(){
    func_b();
}

First I run cmake ../src from helper/build (no error messsages) than cmake ../src from proj_c/build and I got

proj_c/src/../../helper/build/h_b/libh_b.a(h_b.cpp.o): In function `func_b()':
helper/src/h_b/h_b.cpp:4: undefined reference to `func_a()'

It seems that the problem is with h_b.cpp, but libh_b.a was built previously without errors.

Upvotes: 4

Views: 576

Answers (1)

DevSolar
DevSolar

Reputation: 70213

The first problem is that your libraries aren't found. In proj_c/build/CMakeCache.txt:

//Path to a library.
h_a:FILEPATH=h_a-NOTFOUND

//Path to a library.
h_b:FILEPATH=h_b-NOTFOUND

(Use the DOC option to find_library() to get the generic "Path to a library" replaced with something more helpful.)

This kind of "silent failure" can be overcome by adding a check after find_library():

if ( NOT h_a )
    message( FATAL_ERROR "Library h_a not found in ${CMAKE_CURRENT_SOURCE_DIR}/../../helper/build/h_a" )
endif()

(For larger projects you will have a FindXyz.cmake module set up for finding dependencies, which you can call via find_package( Xyz REQUIRED ) to avoid silent failure. But that, and the helper functions available to you when writing such a module, are a subject all of its own.)

The failure itself is due to a problem with your use of find_library:

find_library(h_a PATHS ../../helper/build/h_a)

You tell CMake where to look, and in which variable to store the result, but not what to look for.

Use:

find_library(h_a NAMES h_a PATHS ../../helper/build/h_a)

This will resolve the -NOTFOUND. (Apply for h_b as well.)

You will still get the linker error though, because of...

target_link_libraries(
    proj_c
    h_a
    h_b
)

...linking order. proj_c looks for func_b(), does not find it in h_a, does find it in h_b, which in turn requires func_a(), which isn't found because the linker is making a single pass over the library list only.

It's the same with specifyling -lh_a -lh_b on the command line.

Switch the libraries around according to the dependency chain (proj_c depends on h_b depends on h_a):

target_link_libraries(
    proj_c
    h_b
    h_a
)

That will work.

Upvotes: 5

Related Questions