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

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.
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

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.

Related

CMake include header file in different directory

So I have a directory that's formatted as follows:
Project:
- src
- a.cpp
- b.cpp
- c.cpp
- include
- h.cpp
- h.hpp
How would I get CMake to include h.hpp in the files in the source folder? I tried doing include_directories(include) but CMake is still unable to find the file. I also tried changing the include directive in a.cpp to #include "../include/h.hpp". However, none of these solutions have worked.
EDIT:
The output is:
[build] Consolidate compiler generated dependencies of target a
[build] Consolidate compiler generated dependencies of target c
[build] Consolidate compiler generated dependencies of target b
[build] [ 16%] Building CXX object CMakeFiles/b.dir/src/b.cpp.o
[build] [ 33%] Building CXX object CMakeFiles/c.dir/src/c.cpp.o
[build] [ 50%] Building CXX object CMakeFiles/a.dir/src/a.cpp.o
[build] [ 66%] Linking CXX executable a
[build] [ 83%] Linking CXX executable c
[build] [100%] Linking CXX executable b
[build] /usr/bin/ld: CMakeFiles/a.dir/src/a.cpp.o: in function `func(...)':
[build] ../a.cpp:55: undefined reference to `func(...)'
[build] /usr/bin/ld: a.cpp:58: undefined reference to `func(...)'
Note that func is a function with an implementation provided in h.cpp.
CMakeLists.txt:
cmake_minimum_required(VERSION 3.17)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake
CACHE STRING "Vcpkg toolchain file")
project(proj)
find_package(fmt CONFIG REQUIRED)
find_package(CUDAToolkit REQUIRED)
link_libraries(
fmt::fmt CUDA::nvrtc CUDA::cuda_driver CUDA::cudart
)
include_directories(include)
add_executable(a ${CMAKE_CURRENT_SOURCE_DIR}/src/a.cpp)
add_executable(b ${CMAKE_CURRENT_SOURCE_DIR}/src/b.cpp)
add_executable(c ${CMAKE_CURRENT_SOURCE_DIR}/src/c.cpp)
You should add h.cpp as a source for each executable that uses functions from h.hpp:
add_executable(a ${CMAKE_CURRENT_SOURCE_DIR}/src/a.cpp ${CMAKE_CURRENT_SOURCE_DIR}/include/h.cpp)
add_executable(b ${CMAKE_CURRENT_SOURCE_DIR}/src/b.cpp ${CMAKE_CURRENT_SOURCE_DIR}/include/h.cpp)
add_executable(c ${CMAKE_CURRENT_SOURCE_DIR}/src/c.cpp ${CMAKE_CURRENT_SOURCE_DIR}/include/h.cpp)
As you have said,
func is a function with an implementation provided in h.cpp
so you need to add that implementation to your executables

Created CMake static library has no symbols [duplicate]

This question already has answers here:
Why can templates only be implemented in the header file?
(17 answers)
Closed 1 year ago.
My project looks like this:
include/util/safety.h
include/opcodes/8bit_load_opcodes.h
include/CPU.h
src/util/safety.cpp
src/CPU.cpp
src/main.cpp
src/opcodes/8bit_load_opcodes.cpp
CMakeLists.txt
With the CMake script below
cmake_minimum_required(VERSION 3.17)
project(gameboy)
enable_testing()
FIND_PACKAGE(Boost 1.65.1 COMPONENTS unit_test_framework REQUIRED)
if(NOT Boost_FOUND)
message(FATAL_ERROR "Boost Not found")
endif()
set(CMAKE_CXX_STANDARD 20)
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR})
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include/opcodes)
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include/util)
ADD_LIBRARY(cpu_util src/util/safety.cpp)
ADD_LIBRARY(cpu src/CPU.cpp src/opcodes/8bit_load_opcodes.cpp)
TARGET_LINK_LIBRARIES(cpu ${Boost_LIBRARIES})
TARGET_LINK_LIBRARIES(cpu cpu_util)
add_executable(gameboy src/main.cpp)
TARGET_LINK_LIBRARIES(gameboy cpu)
When I run make I get:
Scanning dependencies of target cpu_util
[ 14%] Building CXX object CMakeFiles/cpu_util.dir/src/util/safety.cpp.o
[ 28%] Linking CXX static library libcpu_util.a
[ 28%] Built target cpu_util
Scanning dependencies of target cpu
[ 42%] Building CXX object CMakeFiles/cpu.dir/src/CPU.cpp.o
[ 57%] Building CXX object CMakeFiles/cpu.dir/src/opcodes/8bit_load_opcodes.cpp.o
[ 71%] Linking CXX static library libcpu.a
[ 71%] Built target cpu
Scanning dependencies of target gameboy
[ 85%] Building CXX object CMakeFiles/gameboy.dir/src/main.cpp.o
[100%] Linking CXX executable gameboy
CMakeFiles/gameboy.dir/src/main.cpp.o: In function `main':
/home/cedric/Programming/gameboy/src/main.cpp:6: undefined reference to `unsigned short validate_argument<unsigned short>(unsigned short, unsigned short)'
collect2: error: ld returned 1 exit status
CMakeFiles/gameboy.dir/build.make:106: recipe for target 'gameboy' failed
make[2]: *** [gameboy] Error 1
CMakeFiles/Makefile2:154: recipe for target 'CMakeFiles/gameboy.dir/all' failed
make[1]: *** [CMakeFiles/gameboy.dir/all] Error 2
Makefile:114: recipe for target 'all' failed
make: *** [all] Error 2
Running nm appears to show an empty output:
$ nm libcpu_util.a
safety.cpp.o:
Why aren't there any symbols in libcpu_util and how can I fix this make error?
For reference:
safety.cpp
#include <stdexcept>
#include "util/safety.h"
template<typename T>
T validate_argument(T value, T mask)
{
if((value & mask) != value)
throw std::runtime_error("Value outside of mask!");
return value;
}
safety.h
#ifndef GAMEBOY_SAFETY_H
#define GAMEBOY_SAFETY_H
template<typename T>
T validate_argument(T value, T mask);
#endif //GAMEBOY_SAFETY_H
The safety.h only has one template function, and the definition part is located in safety.cpp, which is wrong. Template functions should be defined in headers, so you get an empty library here. We need to put the definition part in the header file, and remove the library libcpu_util.a
template<typename T>
T validate_argument(T value, T mask);

Boost.Test linker error by use with precompiled headers (PCH)

I have a linker error when using Boost.Test with precompiled header (PCH) that does not occur without PCH. I use the dynamically linked library as described in Usage variants.
How can I fix the error to use Boost.Test also with PCH?
The problem occurs at least with Fedora and boost 1.73 (has only dynamic libraries) and g++ 10/clang 11.
$ cmake ../ && make
-- Configuring done
-- Generating done
-- Build files have been written to: /home/.../boost_test_pch/build
[ 33%] Building CXX object CMakeFiles/boost_utf_pch.dir/test_driver.cpp.o
[ 66%] Building CXX object CMakeFiles/boost_utf_pch.dir/test.cpp.o
[100%] Linking CXX executable boost_utf_pch
[100%] Built target boost_utf_pch
vs.
$ cmake -DEDA_ENABLE_PCH=TRUE ../ && make
-- Configuring done
-- Generating done
-- Build files have been written to: /home/.../boost_test_pch/build
[ 25%] Building CXX object CMakeFiles/boost_utf_pch.dir/cmake_pch.hxx.gch
[ 50%] Building CXX object CMakeFiles/boost_utf_pch.dir/test_driver.cpp.o
cc1plus: warning: /home/.../boost_test_pch/build/CMakeFiles/boost_utf_pch.dir/cmake_pch.hxx.gch: not used because `BOOST_TEST_DYN_LINK' is defined [-Winvalid-pch]
[ 75%] Building CXX object CMakeFiles/boost_utf_pch.dir/test.cpp.o
[100%] Linking CXX executable boost_utf_pch
/usr/bin/ld: /usr/lib/gcc/x86_64-redhat-linux/10/../../../../lib64/crt1.o: in function `_start':
(.text+0x24): undefined reference to `main'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/boost_utf_pch.dir/build.make:138: boost_utf_pch] Error 1
make[1]: *** [CMakeFiles/Makefile2:95: CMakeFiles/boost_utf_pch.dir/all] Error 2
make: *** [Makefile:103: all] Error 2
I can not do anything with the warning message before ...
Here the playground files:
CMakeLists.txt:
project(boost_utf_pch LANGUAGES CXX)
cmake_minimum_required(VERSION 3.18)
add_executable(${PROJECT_NAME} "")
find_package(Boost 1.73.0 REQUIRED COMPONENTS
unit_test_framework)
target_sources(${PROJECT_NAME} PRIVATE
test_driver.cpp test.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE
Boost::unit_test_framework)
set_source_files_properties(test_driver.cpp
APPEND PROPERTIES COMPILE_DEFINITIONS "BOOST_TEST_DYN_LINK")
option(EDA_ENABLE_PCH "Enable PCH" OFF)
if (EDA_ENABLE_PCH)
target_precompile_headers(${PROJECT_NAME} PRIVATE pch.hpp)
endif()
pch.hpp
#pragma once
#include <boost/test/unit_test.hpp>
test.cpp
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_SUITE( my_test )
BOOST_AUTO_TEST_CASE( test_case1 )
{
BOOST_TEST_WARN( sizeof(int) < 4U );
}
BOOST_AUTO_TEST_SUITE_END()
test_driver.cpp
#define BOOST_TEST_MODULE "Boost.UTF PCH Test Suite"
#include <boost/test/unit_test.hpp>
Alan Birtles got the hint into the right direction. I was not aware of the influence of the compiler switch BOOST_TEST_DYN_LINK for the single file in the context of PCH here. A definition for all files of the project in the style of:
target_compile_definitions(${PROJECT_NAME} PRIVATE
"BOOST_TEST_DYN_LINK")
incomprehensibly does not solve the problem. Only after setting the property SKIP_PRECOMPILE_HEADERS for driver 'main' it compiles and links as expected:
project(boost_utf_pch LANGUAGES CXX)
cmake_minimum_required(VERSION 3.18)
add_executable(${PROJECT_NAME} "")
find_package(Boost 1.73.0 REQUIRED COMPONENTS
unit_test_framework)
target_sources(${PROJECT_NAME} PRIVATE
test_driver.cpp test.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE
Boost::unit_test_framework)
set_source_files_properties(test_driver.cpp
APPEND PROPERTIES COMPILE_DEFINITIONS "BOOST_TEST_DYN_LINK")
set_source_files_properties(test_driver.cpp
PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
option(EDA_ENABLE_PCH "Enable PCH" ON)
if (EDA_ENABLE_PCH)
target_precompile_headers(${PROJECT_NAME} PRIVATE pch.hpp)
endif()

CMake linking with dll on windows - undefined reference

I am trying to link with a DLL executable using CMake. This works fine on Linux (of course with .so), but on Windows, it seems to be tricky. I thought it might be some other dependencies, so I tried creating a simple test program, but I couldn't get that to work either.
My folder structure is simple:
main.cpp
CmakeLists.txt
lib/libtest.dll
lib/CMakeLists.txt
I based this structure on the answer to another question that was asked.
Unfortunately, when I build, it it fails.
The content of the lib/CMakeLists.txt are:
message("-- Linking Test")
add_library(TEST libtest.dll)
set_target_properties(TEST PROPERTIES LINKER_LANGUAGE C)
And the content of the main CMakeLists.txt are:
cmake_minimum_required(VERSION 3.15)
project(Test_program)
set(CMAKE_CXX_STANDARD 14)
set(GCC_COVERAGE_COMPILE_FLAGS "-DDebug")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" )
# Contain the header files for testlib
include_directories("C:/test/utils/")
add_subdirectory(lib)
add_executable(Test_program main.cpp)
target_link_libraries(Test_program TEST)
This results in the following output:
-- Linking Test
-- Configuring done
-- Generating done
-- Build files have been written to: C:/Users/akda/Test_program/cmake-build-debug
[ 33%] Linking C static library libTEST.a
[ 33%] Built target TEST
[ 66%] Building CXX object CMakeFiles/Test_program.dir/main.cpp.obj
[100%] Linking CXX executable Test_program.exe
CMakeFiles\Test_program.dir/objects.a(main.cpp.obj): In function `powerOn()':
C:/Users/akda/Test_program/main.cpp:8: undefined reference to `My_Library_Function'
collect2.exe: error: ld returned 1 exit status
mingw32-make.exe[2]: *** [CMakeFiles\Test_program.dir\build.make:87: Test_program.exe] Error 1
mingw32-make.exe[1]: *** [CMakeFiles\Makefile2:77: CMakeFiles/Test_program.dir/all] Error 2
mingw32-make.exe: *** [Makefile:83: all] Error 2
Using tools as DLLExportViewer, I can confirm that the DLL, in fact, does contain the function called.
What am I doing wrong? I'll never claim to be an expert in CMake, but I really can't see what's wrong.
Update with link to .a file
As pointed out, I could try linking with the .a file.
I've added this to the library folder, and changed the main CMakeLists.txt to the following:
cmake_minimum_required(VERSION 3.15)
project(Test_program)
set(CMAKE_CXX_STANDARD 14)
set(GCC_COVERAGE_COMPILE_FLAGS "-DDebug")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" )
include_directories("C:/test/utils/")
find_library(TEST ${CMAKE_SOURCE_DIR}/lib/libtest.a)
add_executable(Test_program main.cpp)
target_link_libraries(Test_program ${TEST})
And this ends up in the same way, with the same errors.

Linking LLVM libraries on Windows with CMake and MinGW

I've been writing a compiler using LLVM as the backend. The CMake files I've written so far have worked on Linux, but I haven't had any luck on Windows. The project is split into a library and "driver" executable with their own CMakeLists.txt in separate subdirectories.
The top level CMakeLists.txt looks like this:
cmake_minimum_required (VERSION 3.7.0)
project (compiler)
set (CMAKE_CXX_STANDARD 14)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
find_package (LLVM REQUIRED CONFIG)
message (STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message (STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
include_directories (${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
add_subdirectory (Compiler_Lib)
add_subdirectory (Compiler_exe)
The CMakeLists.txt for the library:
cmake_minimum_required (VERSION 3.7.0)
add_library (compiler_lib
AST.cpp
AST.h
parser.cpp
parser.h
scanner.cpp
scanner.h
token.cpp
token.h
visualizer.cpp
visualizer.h
codegen.cpp
codegen.h)
target_include_directories (compiler_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(compiler_lib LLVM)
And the CMakeLists.txt for the executable (which is where linking to the libraries fails):
cmake_minimum_required (VERSION 3.7.0)
project (compiler_exe)
add_executable (compiler_exe Compiler_exe.cpp getopt.h getopt.cpp)
target_link_libraries (compiler_exe LINK_PUBLIC LLVM compiler_lib)
I run the command "c:\Program Files\CMake\bin\cmake.exe" .. -G"MinGW Makefiles" -DCMAKE_PREFIX_PATH=C:\Users\James\llvm+clang-7.0.0-win64-msvc-release where C:\Users\James\llvm+clang-7.0.0-win64-msvc-release is the path to prebuilt LLVM libraries. However, running mingw32-make afterwards fails with the output
Scanning dependencies of target compiler_lib
[ 10%] Building CXX object Compiler_Lib/CMakeFiles/compiler_lib.dir/AST.cpp.obj
[ 20%] Building CXX object Compiler_Lib/CMakeFiles/compiler_lib.dir/parser.cpp.obj
[ 30%] Building CXX object Compiler_Lib/CMakeFiles/compiler_lib.dir/scanner.cpp.obj
[ 40%] Building CXX object Compiler_Lib/CMakeFiles/compiler_lib.dir/token.cpp.obj
[ 50%] Building CXX object Compiler_Lib/CMakeFiles/compiler_lib.dir/visualizer.cpp.obj
[ 60%] Building CXX object Compiler_Lib/CMakeFiles/compiler_lib.dir/codegen.cpp.obj
[ 70%] Linking CXX static library libcompiler_lib.a
[ 70%] Built target compiler_lib
Scanning dependencies of target compiler_exe
[ 80%] Building CXX object Compiler_exe/CMakeFiles/compiler_exe.dir/Compiler_exe.cpp.obj
[ 90%] Building CXX object Compiler_exe/CMakeFiles/compiler_exe.dir/getopt.cpp.obj
[100%] Linking CXX executable compiler_exe.exe
c:/mingw/bin/../lib/gcc/mingw32/8.2.0/../../../../mingw32/bin/ld.exe: cannot find -lLLVM
collect2.exe: error: ld returned 1 exit status
Compiler_exe\CMakeFiles\compiler_exe.dir\build.make:102: recipe for target 'Compiler_exe/compiler_exe.exe' failed
mingw32-make[2]: *** [Compiler_exe/compiler_exe.exe] Error 1
CMakeFiles\Makefile2:176: recipe for target 'Compiler_exe/CMakeFiles/compiler_exe.dir/all' failed
mingw32-make[1]: *** [Compiler_exe/CMakeFiles/compiler_exe.dir/all] Error 2
Makefile:82: recipe for target 'all' failed
mingw32-make: *** [all] Error 2
This is the first time I've used CMake so I could have missed something obvious, but as I say it seems to work on Linux.
For the linking to succeed two things need to be true:
1) the file libLLVM.a needs to exist
2) that file has to be in a directory in the library search path
There should be a way to get cmake to tell you the list of places it searches for libraries, and you need to find a way to get wherever libLLVM.a exists into that list of dirs.
Apologies for the partial answer, but that's the troubleshooting path...