0

I am trying to code a little C++ program, where I access a sensor through a C hardware library (the bmp280 in this example). For some reasons that are not important right now, I want to compile the measurement code firstly to a shared library (for Linux, therefore a libXXX.so file) and afterwards include the resulting library in a test executable. With the following CMakeLists I am actually able to compile the library target itself (wsh), but not the executable depending on it (main).

project(weather-station-hardware-driver)
message(STATUS "Selected C Compiler: ${CMAKE_C_COMPILER}")
message(STATUS "Selected C++ Compiler: ${CMAKE_CXX_COMPILER}")

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
set(CMAKE_CXX_STANDARD 20)

include(FetchContent)
FetchContent_Declare(json URL https://github.com/nlohmann/json/releases/download/v3.11.1/json.tar.xz)
FetchContent_Declare(bmp2 GIT_REPOSITORY https://github.com/BoschSensortec/BMP2-Sensor-API.git)
FetchContent_MakeAvailable(json bmp2)

add_library(wsh SHARED src/library.cpp src/bmp280.cpp src/bmp280.hpp)
set_target_properties(wsh PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(wsh PUBLIC i2c)
target_include_directories(wsh PUBLIC ${bmp2_SOURCE_DIR})

add_executable(main src/main.cpp)
target_link_libraries(main PRIVATE wsh nlohmann_json::nlohmann_json)

The result is the following error message:

: && /usr/bin/c++ -g  CMakeFiles/main.dir/src/main.cpp.o -o ../bin/main  -Wl,-rpath,/home/fabro122/workspace/intellij/weather-station-webserver/native/hardware-driver/bin  ../bin/libwsh.so  -li2c && :
/usr/bin/ld: ../bin/libwsh.so: undefined reference to `bmp2_get_config'
/usr/bin/ld: ../bin/libwsh.so: undefined reference to `bmp2_init'

where bmp2_get_config and bmp2_init are c functions from the source files in ${bmp2_SOURCE_DIR}.

I already tried to add the following line:

target_link_directories(wsh PUBLIC ${bmp2_SOURCE_DIR})

And to include the headers from the library as C code (which should be unnecessary, because the bmp280 source file includes a "cpp guard" anyways):

extern "C" {
   #include <bmp2.h>
}

And also to compile directly to the executable -> without compiling and linking my own shared library first.

Nothing helped and couldn't find any other tips on stackoverflow, so here I am, hoping for help. Thanks in advance!

Here are the other relevant source files (excluding the .hpp files):

main.cpp

#include <iostream>
#include <nlohmann/json.hpp>

#include "library.hpp"

using namespace std;
using namespace wsh;

namespace wsh {
    // The non-intrusive macro is used to avoid the shared library depending on nlohmann/json.hpp
    NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Measurement, temperature, pressure, humidity, airQuality, ambientLight)
}

int main() {
    Measurement data = doMeasurement();
    nlohmann::json j = data;

    cout << j << endl;

    return 0;
}

library.hpp

#include "library.hpp"

#include "bmp280.hpp"

wsh::Measurement wsh::doMeasurement() {
    auto allData = Measurement();

    bmp280 bmp;

    return allData;
}

bmp280.cpp

#include "bmp280.hpp"

#include <bmp2.h>

namespace wsh {
    BMP2_INTF_RET_TYPE i2c_read(uint8_t reg_addr, uint8_t* reg_data, uint32_t length, void* intf_ptr) {
        return 0;
    }

    BMP2_INTF_RET_TYPE i2c_write(uint8_t reg_addr, const uint8_t* reg_data, uint32_t length, void* intf_ptr) {
        return 0;
    }


    bmp280::bmp280() {
        // device and address are defined in .hpp
        device.chip_id = address;

        device.intf = BMP2_I2C_INTF;
        device.read = i2c_read;
        device.write = i2c_write;

        bmp2_init(&device);

        // configure
        bmp2_config conf{};
        bmp2_get_config(&conf, &device);
    }
}
fabro122
  • 1
  • 1
  • 1
    Are you building `bmp2`? Looks like you're just downloading the source. – Stephen Newell Aug 15 '22 at 19:15
  • Indeed, it looks like you just told it to look in bmp2's source files to find include files, but you did not tell it to compile bmp2. – user253751 Aug 15 '22 at 19:33
  • No? I am only including it through the `target_include_directories` statement in this configuration, but I also tried to include it directly via `target_sources(wsh PUBLIC ${bmp2_SOURCE_DIR})`, if that makes a difference. (I am pretty new to CMake and it's my first project with more than a standard CMakeLists) – fabro122 Aug 15 '22 at 19:34
  • If I am not entirely wrong, when using `target_sources` it should compile automatically with the library target, shouldn't it? – fabro122 Aug 15 '22 at 19:36
  • The command `target_sources` requires **files** to be listed, but `${bmp2_SOURCE_DIR}` is a **directory**. For collect all files in a directory (for further passing them into `target_sources` or directly into `add_library`/`add_executable`) use `file(GLOB)` as described in the [duplicate question](https://stackoverflow.com/questions/3201154/automatically-add-all-files-in-a-folder-to-a-target-using-cmake). Neither `target_include_directories` nor `target_link_directories` add **source** files: these commands only specify **directories** where compiler or linker will search **specific** files. – Tsyvarev Aug 15 '22 at 20:03
  • Thanks! As you pointed out, the problem was whenever I tried to add the source files directly to my library I used the variable `${bmp2_SOURCE_DIR}` and didn't recognize it as being only a directory and not the relevant source files themselves. Adding the files manually or using your proposed answer with GLOB solved the problem. – fabro122 Aug 15 '22 at 20:18

0 Answers0