Paweł Kurzawa
Paweł Kurzawa

Reputation: 61

Mixing C++ and C/C++ static library with CMake

I have searched various forums and cannot find a solution to my problem.
I'm trying to write unit tests of a function in a C file. I'm using google test library.
Although I follow the guides, the project doesn't compile properly.
Tests written in C ++ do not see functions in C file. (undefined reference)
Below I am attaching my files, maybe someone will notice where I made a mistake. I'm out of ideas.

Project tree

pir_driver.h

#ifndef PIR_DRIVER_H_
#define PIR_DRIVER_H_

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdint.h>

uint32_t xTaskGetTickCount();


#ifdef __cplusplus
} /* extern "C" */
#endif

pir_driver.c

#include "pir_driver.h"

uint32_t xTaskGetTickCount()
{
    return 1000;
}

pir_test.cpp

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

TEST(PIR_Timer_Test, Start)
{
    uint32_t test =  xTaskGetTickCount() * 2;
    EXPECT_EQ(test, test);

}

Project root CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
set(This pir_driver)
project(${This} C CXX)
include(Dart)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
enable_testing()
add_subdirectory(googletest)
set(Headers
    pir_driver.h
)
set(Source
    pir_driver.c
)
add_library(${This} STATIC ${Sources} ${Headers})
set_target_properties(${This} PROPERTIES LINKER_LANGUAGE C)
add_subdirectory(test)

test CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
set(This pir_test)
set(Sources pir_test.cpp )

add_executable(${This} ${Sources})
set_target_properties(${This} PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(${This} PUBLIC pir_driver gtest_main )

add_test( NAME ${This} COMMAND ${This})

Compile log:

[proc] Wykonywanie polecenia: "C:\Program Files\CMake\bin\cmake.EXE" --build c:/Users/xxx/ansi_c_projects/pir_test/build --config Debug --target all -- -j 10
[build] [ 14%] Linking C static library libpir_driver.a
[build] [ 28%] Building CXX object googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.obj
[build] [ 28%] Built target pir_driver
[build] [ 42%] Linking CXX static library ..\lib\libgtestd.a
[build] [ 42%] Built target gtest
[build] [ 57%] Building CXX object googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.obj
[build] [ 71%] Linking CXX static library ..\lib\libgtest_maind.a
[build] [ 71%] Built target gtest_main
[build] [ 85%] Building CXX object test/CMakeFiles/pir_test.dir/pir_test.cpp.obj
[build] [100%] Linking CXX executable pir_test.exe
[build] CMakeFiles\pir_test.dir/objects.a(pir_test.cpp.obj): In function `PIR_Timer_Test_Start_Test::TestBody()':
[build] C:/Users/xxx/ansi_c_projects/pir_test/test/pir_test.cpp:13: undefined reference to `xTaskGetTickCount'
[build] collect2.exe: error: ld returned 1 exit status
[build] mingw32-make.exe[2]: *** [test\CMakeFiles\pir_test.dir\build.make:108: test/pir_test.exe] Error 1
[build] mingw32-make.exe[1]: *** [CMakeFiles\Makefile2:1004: test/CMakeFiles/pir_test.dir/all] Error 2
[build] mingw32-make.exe: *** [Makefile:113: all] Error 2

Upvotes: 1

Views: 2387

Answers (1)

Corristo
Corristo

Reputation: 5520

The reason it doesn't work is that you have a typo in the main CMakeLists.txt: You define the variable Source, but use the variable Sources when populating the sources of the pir_driver target. Consequently the .c file isn't compiled and the linker can't find the symbol defined within.

The missing .c file is also the reason why you needed to manually set the linker language in the first place. Once you add the source file you can remove the set_target_properties(${This} PROPERTIES LINKER_LANGUAGE C) line as CMake will figure it out itself based on the extensions of the source files.

To avoid such problems in the future you can use

add_library(pir_driver STATIC)
target_sources(pir_driver
  PRIVATE
    pir_driver.h
    pir_driver.c
)

instead of CMake variables to collect sources and headers in CMake 3.11 and later.

Upvotes: 2

Related Questions