arduino-cmake / Arduino-CMake-NG

CMake-Based framework for Arduino platforms
MIT License
138 stars 39 forks source link

Undefined reference error when linking 3rd party libraries. #72

Closed dominicap closed 5 years ago

dominicap commented 5 years ago

OS: macOS OS Version: 10.14.2 Platform: Arduino Platform SDK Version: [Arduino SDK version 1.8.8]

When trying to link 3rd party libraries into my project, every time I instantiate an object I receive this error.

====================[ Build | auto_car | Default ]==============================
/usr/local/bin/cmake --build /Users/Dominic/Projects/Code/AutoCar/build --target auto_car -- -j 4
[ 75%] Built target mega_atmega2560_core_lib
[ 78%] Built target adafruit_bno055
[ 81%] Built target adafruit_sensor
[ 90%] Built target Wire
[ 93%] Built target adafruit_vl53l0x
[ 96%] Linking CXX executable auto_car.elf
/var/folders/vh/n3x031n547v4qhvxs1p_wtf00000gn/T//ccSQ9BRK.ltrans0.ltrans.o: In function `__static_initialization_and_destruction_0':
/Users/Dominic/Projects/Code/AutoCar/src/auto_car.cpp:9: undefined reference to `Adafruit_BNO055::Adafruit_BNO055(long, unsigned char)'
/var/folders/vh/n3x031n547v4qhvxs1p_wtf00000gn/T//ccSQ9BRK.ltrans0.ltrans.o: In function `setup':
/Users/Dominic/Projects/Code/AutoCar/src/auto_car.cpp:16: undefined reference to `Adafruit_VL53L0X::begin(unsigned char, bool, TwoWire*)'
/Users/Dominic/Projects/Code/AutoCar/src/auto_car.cpp:20: undefined reference to `Adafruit_BNO055::begin(Adafruit_BNO055::adafruit_bno055_opmode_t)'
collect2: error: ld returned 1 exit status
make[3]: *** [auto_car.elf] Error 1
make[2]: *** [CMakeFiles/auto_car.dir/all] Error 2
make[1]: *** [CMakeFiles/auto_car.dir/rule] Error 2
make: *** [auto_car] Error 2

My CMakeLists.txt file is as such.

cmake_minimum_required(VERSION 3.13.3)
project(auto_car LANGUAGES C CXX)

set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/cmake/Arduino-Toolchain.cmake)

get_board_id(board_id mega atmega2560)

add_arduino_executable(auto_car ${board_id} src/auto_car.cpp)

link_platform_library(auto_car Wire ${board_id})

add_arduino_library(adafruit_sensor ${board_id} libs/Adafruit_Sensor/src/Adafruit_Sensor.h)
add_arduino_library(adafruit_bno055 ${board_id} libs/Adafruit_BNO055/src/Adafruit_BNO055.h)
add_arduino_library(adafruit_vl53l0x ${board_id} libs/Adafruit_VL53L0X/src/Adafruit_VL53L0X.h)

link_arduino_library(auto_car adafruit_sensor ${board_id})
link_arduino_library(auto_car adafruit_bno055 ${board_id})
link_arduino_library(auto_car adafruit_vl53l0x ${board_id})

And the source file is as such.

#include <Arduino.h>

#include <Adafruit_BNO055.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_VL53L0X.h>

#define BAUD_RATE 9600

Adafruit_BNO055 imuSensor = Adafruit_BNO055(55);
Adafruit_VL53L0X lidarSensor = Adafruit_VL53L0X();

void setup()
{
    Serial.begin(BAUD_RATE);

    if (!lidarSensor.begin()) {
        Serial.println("Error: Lidar sensor is not setup properly.");
    }

    if (!imuSensor.begin()) {
        Serial.println("Error: IMU sensor is not setup properly.");
    }
}

void loop()
{
    // Loop
}

The project can be cloned here. I have already tried moving the header/cpp files of the libraries used directly into the properly named folder without the src subdirectory folder. The object instantiation is as given by the examples in the libraries themselves. I've also tried passing the toolchain as a command line argument. Any help would be much appreciated.

Thanks!

MrPointer commented 5 years ago

@dominicap Hello and sorry for the long delay, been extremely busy lately. First of, the toolchain file must be passed by command line. While setting it as a variable inside a CMake script may work - It's not officially supported by the framework and in-fact is quite discouraged.

As for the actual error - I'd bet on misuse of the add_arduino_library function, as it expects sources, not headers. It's explained by the natural way of CMake - Each target must consist of at least one source file, otherwise it just won't be a valid binary (Might compile but definitely not link). I'm not familiar with any Arduino libraries at all, so I must point it out - If these libraries you're using are header-only libs, i.e. they're consisted of a single (maybe multi) header file. When that is the case, Arduino-CMake expects you to call the add_arduino_header_only_library function instead to create a header-only library target. You can read more about it here.

dominicap commented 5 years ago

@MrPointer Thank you for helping me out! I updated my project as defined in the wiki and it is available to clone here. I'm still having issues however.

Building results in this:

====================[ Build | auto_car | Default ]==============================
/usr/local/bin/cmake --build /Users/Dominic/Desktop/auto-car/build --target auto_car -- -j 4
[ 73%] Built target mega_atmega2560_core_lib
Scanning dependencies of target adafruit_vl53l0x
Scanning dependencies of target adafruit_bno055
[ 82%] Built target Wire
[ 85%] Building CXX object CMakeFiles/adafruit_vl53l0x.dir/libs/Adafruit_VL53L0X/src/Adafruit_VL53L0X.cpp.obj
[ 88%] Building CXX object CMakeFiles/adafruit_bno055.dir/libs/Adafruit_BNO055/src/Adafruit_BNO055.cpp.obj
In file included from /Users/Dominic/Desktop/auto-car/libs/Adafruit_BNO055/src/Adafruit_BNO055.cpp:29:0:
/Users/Dominic/Desktop/auto-car/libs/Adafruit_BNO055/src/Adafruit_BNO055.h:33:19: fatal error: Wire.h: No such file or directory
compilation terminated.
In file included from /Users/Dominic/Desktop/auto-car/libs/Adafruit_VL53L0X/src/Adafruit_VL53L0X.cpp:32:0:
/Users/Dominic/Desktop/auto-car/libs/Adafruit_VL53L0X/src/Adafruit_VL53L0X.h:28:18: fatal error: Wire.h: No such file or directory
compilation terminated.
make[3]: *** [CMakeFiles/adafruit_bno055.dir/libs/Adafruit_BNO055/src/Adafruit_BNO055.cpp.obj] Error 1
make[3]: *** [CMakeFiles/adafruit_vl53l0x.dir/libs/Adafruit_VL53L0X/src/Adafruit_VL53L0X.cpp.obj] Error 1
make[2]: *** [CMakeFiles/adafruit_bno055.dir/all] Error 2
make[2]: *** Waiting for unfinished jobs....
make[2]: *** [CMakeFiles/adafruit_vl53l0x.dir/all] Error 2
make[1]: *** [CMakeFiles/auto_car.dir/rule] Error 2
make: *** [auto_car] Error 2

My CMakeLists.txt:

cmake_minimum_required(VERSION 3.13.3)
project(auto_car LANGUAGES C CXX)

get_board_id(board_id mega atmega2560)

add_arduino_executable(auto_car ${board_id} src/auto_car.cpp)

add_arduino_header_only_library(adafruit_sensor ${board_id} libs/Adafruit_Sensor/src/Adafruit_Sensor.h)
add_arduino_library(adafruit_bno055 ${board_id} libs/Adafruit_BNO055/src/Adafruit_BNO055.cpp)
add_arduino_library(adafruit_vl53l0x ${board_id} libs/Adafruit_VL53L0X/src/Adafruit_VL53L0X.cpp)

link_platform_library(auto_car Wire ${board_id})

link_arduino_library(auto_car adafruit_sensor ${board_id} HEADER_ONLY)
link_arduino_library(auto_car adafruit_bno055 ${board_id})
link_arduino_library(auto_car adafruit_vl53l0x ${board_id})

My source file:

#include <Arduino.h>

#include <Adafruit_BNO055.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_VL53L0X.h>

#define BAUD_RATE 9600

Adafruit_BNO055 bno055 = Adafruit_BNO055(55);
Adafruit_VL53L0X vl53L0X = Adafruit_VL53L0X();

void setup()
{
    Serial.begin(BAUD_RATE);

    if (!vl53L0X.begin()) {
        Serial.println("Error: Lidar sensor is not setup properly.");
    }

    if (!bno055.begin()) {
        Serial.println("Error: IMU sensor is not setup properly.");
    }
}

void loop()
{
    // Loop
}

Further, CLion seems unable to find the Adafruit_BNO055.h and Adafruit_VL53L0X.h, hence why its probably failing to correctly link and build.

screen shot 2019-02-25 at 2 37 26 pm

Any help would be much appreciated, thanks!

MrPointer commented 5 years ago

@dominicap I've played a bit with your project and managed to get it working 😄

But first, let me make some things clear about CMake targets in general: CMake targets have certain rules which you must play by in order to get what you want. While in our heads we imagine that linking one target to another will propagate the linked target and all of its properties to all the other targets linked together (as you did with linking Wire to the executable), cmake doesn't work like that. Instead, it only propagates targets up the hierarchy tree, not across. What it means is that for a target to link to many other targets, the best strategy will be to link it to the most deep-nested target so it'll propagate it upwards. Just to make things a bit clearer, imagine the Wire target in your case, which is needed by all library targets as well as the executable. The correct strategy cmake-wise would be to link it to the library targets (Both of them as they do not depend on each other), and then link the libraries themselves to the executable target. Depending on the propagation scope (Public by default), the Wire library will or will not be linked against the executable as well.

Now, as for Arduino-CMake's framework behavior regarding libraries: The add_arduino_library function should be treated with great care, and it's probably best not to use it if an option to use the find_arduino_library exists. Some of the reasons for this are described here. What's "missing" from that page is the fact that add_arduino_library requires you to specify not only the main source file of the library, but all of them, including those in sub-dirs such as utility. find_arduino_library does that automatically, hence is more recommended to use.

Now for the fun part - Here's a CMakeLists.txt for your project which "just works", assuming your libraries are located under the libraries directory (as opposed to your libs directory, the reason for it is described here):

cmake_minimum_required(VERSION 3.13.3)
project(auto_car LANGUAGES C CXX)

get_board_id(board_id mega atmega2560)

add_arduino_executable(auto_car ${board_id} src/auto_car.cpp)

add_arduino_header_only_library(adafruit_sensor ${board_id} libraries/Adafruit_Sensor/src/Adafruit_Sensor.h)

find_arduino_library(adafruit_bno055 Adafruit_BNO055 ${board_id} 3RD_PARTY)
find_arduino_library(adafruit_vl53l0x Adafruit_VL53L0X ${board_id} 3RD_PARTY)

link_platform_library(adafruit_bno055 Wire ${board_id})
link_platform_library(adafruit_vl53l0x Wire ${board_id})

link_arduino_library(adafruit_bno055 adafruit_sensor ${board_id} HEADER_ONLY)

link_arduino_library(auto_car adafruit_bno055 ${board_id})
link_arduino_library(auto_car adafruit_vl53l0x ${board_id})
dominicap commented 5 years ago

@MrPointer Thank you for taking the time to help me fix the problem as well as understand the concepts behind it, I really appreciate it!