Dennis Barzanoff
Dennis Barzanoff

Reputation: 443

CMake cannot link library correctly

I am doing my homework, and my project tree is

.
├── CMakeLists.txt
├── main.c
├── src
│   ├── CMakeLists.txt
│   ├── game
│   │   ├── game.c
│   │   └── game.h
│   └── main.c
└── test
    ├── CMakeLists.txt
    ├── homework_tests
    │   ├── CMakeLists.txt
    │   ├── gtest.cpp
    │   └── gtest.h
    └── lib
        └──googletest
            └──googletest
                └──[library files (not compiled)]

The main.c files just have a "Hello, world" inside them. Before you read: I am trying to:
1. Build target src.so from the source files
2. Build the google tests library (in the test/lib/ folder)
3. Build target runBasicTests from the tests/ folder
4. Link them together

I admit that I am not a CMake master, so be aware of that.

Files

Now, assuming that we are in the project root .

./ :

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(c_homework)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -std=c11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(SOURCE_FILES
        main.c)

add_executable(homework_run ${SOURCE_FILES})

add_subdirectory(src)
add_subdirectory(test)

target_link_libraries(homework_run src.so)

src/:

CMakeLists.txt

set(SOURCE_FILES
        game/game.c)

add_library(src.so ${SOURCE_FILES})

target_include_directories (src.so PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

game/game.h

#ifndef C_HOMEWORK_GAME_H
#define C_HOMEWORK_GAME_H

#include <stdbool.h>

struct game_t {
    int id, price;
    const char *country_code;
};

bool is_game_available(struct game_t, const char[4]);
int how_many_available(struct game_t*, size_t, const char[4]);

#endif //C_HOMEWORK_GAME_H

game/game.c

#include <string.h>
#include "game.h"

bool is_game_available(struct game_t game, const char country_code[4]) 
{
    return strcmp(game.country_code, country_code) == 0;
}

int how_many_available(struct game_t * games, size_t game_count, const 
char country_code[4]) {
    int available_games = 0;
    for (int i = 0; i < game_count; i++) {
        if (is_game_available(games[i], country_code)) {
            available_games++;
        }
    }
    return available_games;
}

test/:

CMakeLists.txt

project(homework_tests)

add_subdirectory(lib/googletest/googletest)
add_subdirectory(homework_tests)

homework_tests/CMakeLists.txt

include_directories(${gTest_SOURCE_DIR}/include ${gTest_SOURCE_DIR})

add_executable(runBasicTests
        gtest.cpp)

target_link_libraries(runBasicTests gtest gtest_main)
target_link_libraries(runBasicTests src.so)

homework_tests/gtest.cpp

#include "gtest.h"
#include <game/game.h>

int main(int argc, char **argv) {
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

TEST(gameTests, gameTest1) {
    struct game_t game{};
    game.country_code = "359";
    game.id = 1;
    game.price = 69;
    EXPECT_EQ(is_game_available(game, "359"), true);
}

homework_tests/gtest.h

#ifndef MYPROJECTNAME_GTEST_H
#define MYPROJECTNAME_GTEST_H

#include "gtest/gtest.h"

#endif //MYPROJECTNAME_GTEST_H

And the problem is that when I try to run a test, I get a linker error:

/opt/clion-2018.1/bin/cmake/bin/cmake --build 
/home/denis/CLionProjects/c_homework/cmake-build-debug --target 
runBasicTests -- -j 1

[ 25%] Built target gtest
Scanning dependencies of target src.so
[ 37%] Building C object src/CMakeFiles/src.so.dir/game/game.c.o
[ 50%] Linking C static library libsrc.so.a
[ 50%] **Built target src.so**
[ 75%] Built target gtest_main
[ 87%] Linking CXX executable runBasicTests
CMakeFiles/runBasicTests.dir/gtest.cpp.o: In function 
`gameTests_gameTest1_Test::TestBody()':
/home/denis/CLionProjects/c_homework/test/homework_tests/gtest.cpp:18:

          <---------------   Here   ----------------->   
undefined reference to `is_game_available(game_t, char const*)'
collect2: error: ld returned 1 exit status

test/homework_tests/CMakeFiles/runBasicTests.dir/build.make:98: recipe 
for target 'test/homework_tests/runBasicTests' failed
make[3]: *** [test/homework_tests/runBasicTests] Error 1
CMakeFiles/Makefile2:294: recipe for target 
'test/homework_tests/CMakeFiles/runBasicTests.dir/all' failed
make[2]: *** [test/homework_tests/CMakeFiles/runBasicTests.dir/all] 
Error 2
CMakeFiles/Makefile2:306: recipe for target 
'test/homework_tests/CMakeFiles/runBasicTests.dir/rule' failed
make[1]: *** [test/homework_tests/CMakeFiles/runBasicTests.dir/rule] 
Error 2
Makefile:170: recipe for target 'runBasicTests' failed
make: *** [runBasicTests] Error 2

Upvotes: 0

Views: 663

Answers (1)

Dennis Barzanoff
Dennis Barzanoff

Reputation: 443

The problem was that I was calling C code from C++ code and the C++ compiler was changing the names of the functions (name-mangling) and that was why I was getting and undefined reference error.

The solution was to tell the compiler that the following function declarations were externally "C" code.

test/homework_tests/gtest

#ifdef __cplusplus
extern "C" {
#endif
    /* Declarations of this file */
    bool is_game_available(struct game_t, const char[4]);
    int how_many_available(struct game_t *, size_t, const char[4]);
#ifdef __cplusplus
}
#endif

That solved the problem. Thanks to everyone. XD

Upvotes: 1

Related Questions