Linker error: undefined reference when symbol is defined

Hi all, I’m trying to include the example from here u8g2-hal-esp-idf/test_SSD1306_i2c.c at master · mkfrey/u8g2-hal-esp-idf · GitHub in my application to test the function of a display. The linker doesn’t complain about including u8g2.h, but it does complain about undefined references to symbols that are included in u8g2.h, which I don’t understand.

I’ve added the following build flag, which as I mentioned allows the import of u8g2.h without complaints.

-I/<system_path>/PlatformIO/Projects/<project_name>/.pio/libdeps/stable/U8g2/src/clib -DESP_IDF

My main.c looks like this:

#include <driver/gpio.h>
#include <driver/spi_master.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <stdio.h>
#include <string.h>

#include "u8g2.h"
#include "u8g2_esp32_hal.h"

#include "sdkconfig.h"

#define OLED_I2C_ADDRESS   0x3C

#define PIN_SDA 5
#define PIN_SCL 2

static const char* TAG = "ssd1306";

void app_main(void)
{
    // esp_log_set_vprintf(esp_apptrace_vprintf);

    ESP_LOGI(TAG, "Initializing...");

  u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
  u8g2_esp32_hal.sda = PIN_SDA;
  u8g2_esp32_hal.scl = PIN_SCL;
  u8g2_esp32_hal_init(u8g2_esp32_hal);

  u8g2_t u8g2;  // a structure which will contain all the data for one display
  u8g2_Setup_ssd1306_i2c_128x32_univision_f(
      &u8g2, U8G2_R0,
      // u8x8_byte_sw_i2c,
      u8g2_esp32_i2c_byte_cb,
      u8g2_esp32_gpio_and_delay_cb);  // init u8g2 structure
  u8x8_SetI2CAddress(&u8g2.u8x8, OLED_I2C_ADDRESS);

  ESP_LOGI(TAG, "u8g2_InitDisplay");
  u8g2_InitDisplay(&u8g2);  // send init sequence to the display, display is in
                            // sleep mode after this,

  ESP_LOGI(TAG, "u8g2_SetPowerSave");
  u8g2_SetPowerSave(&u8g2, 0);  // wake up display
  ESP_LOGI(TAG, "u8g2_ClearBuffer");
  u8g2_ClearBuffer(&u8g2);
  ESP_LOGI(TAG, "u8g2_DrawBox");
  u8g2_DrawBox(&u8g2, 0, 26, 80, 6);
  u8g2_DrawFrame(&u8g2, 0, 26, 100, 6);

  ESP_LOGI(TAG, "u8g2_SetFont");
  u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr);
  ESP_LOGI(TAG, "u8g2_DrawStr");
  u8g2_DrawStr(&u8g2, 2, 17, "Hi nkolban!");
  ESP_LOGI(TAG, "u8g2_SendBuffer");
  u8g2_SendBuffer(&u8g2);

  ESP_LOGI(TAG, "All done!");

  vTaskDelete(NULL);
}

When I compile, I see a ton of errors along the lines of main.c:67: undefined reference to 'u8g2_SendBuffer'. But if I look in u8g2.h, I clearly see those symbols defined. It appears as thought the linker can’t find any of the symbols defined in that header, despite successfully importing the header.

Here’s my full platformio.ini (I’m using a branch of esp-idf for some 4.0 features, which I don’t believe is related to this problem):

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:stable]
platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.3rc1/platform-espressif32-2.0.3.zip
board = esp32dev
framework = espidf
monitor_speed = 115200
lib_deps = 
	https://github.com/mkfrey/u8g2-hal-esp-idf.git
	olikraus/U8g2@2.33.9
build_flags = 
	-I/<system_path>/PlatformIO/Projects/<project_name>/.pio/libdeps/stable/U8g2/src/clib -DESP_IDF

GitHub - mkfrey/u8g2-hal-esp-idf: U8g2 compatibility component for esp-idf on ESP32 is a ESP-IDF component, don’t put it in “lib/”, but in the components/ folder of the project, like reference projects do.

So the error here doesn’t actually relate to u8g2-hal-esp-idf dependency, but rather the U8g2 dependency, which is a standard c library. I should also clarify that I’m using platformio via VSCode, and sticking dependencies in .pio/libdeps is the standard approach there. I did manage to get the linker to recognize u8g2 by adding the following build flags:

	-lu8g2
	-L/<path>/PlatformIO/Projects/<project_name>/.pio/libdeps/stable/U8g2/src/clib u8g2

But I am now seeing the following error:

*** [.pio/build/stable/bootloader.elf] Implicit dependency '<path>/.platformio/platforms/espressif32/builder/u8g2' not found, needed by target '.pio/build/stable/bootloader.elf'.

So it seems like the linker finds the resource, but then doesn’t actually link it? I feel like platformio’s library dependency finder should be handling all of this, but for some reason in the case of u8g2 it isn’t.

Remove that in the -L line, the -lu8g2 will already try and link libu8g2.a

Hrm, so if I remove the L, I get cannot find -lu8g2. Which sort of makes sense, because the linker needs to know where to find u8g2 to link it, no? libu8g2.a doesn’t exist, since I’m trying to the library from the source files in .pio/libdeps/stable/U8g2/src/clib.

I think the basic question here is, given if you specify a standard c library via the lib_deps flag in platformio.ini, and patformio then installs that dependency in .pio/libdeps/stable, how do you get platformio to then link that standard c library correctly?

No I’m saying,

	-lu8g2
	-L/<path>/PlatformIO/Projects/<project_name>/.pio/libdeps/stable/U8g2/src/clib

Hrm, so I’ve tried every combination I can of removing compiler commands. With that particular combination, I get this error:

In file included from .pio/libdeps/stable/u8g2-hal-esp-idf/src/u8g2_esp32_hal.c:10:
.pio/libdeps/stable/u8g2-hal-esp-idf/include/u8g2_esp32_hal.h:12:10: fatal error: u8g2.h: No such file or directory

So it appears that the u8g2_esp32_hal can’t find the u8g2 library. Very frustrating getting a normal C library to link with platformio.

If it can’t find the header file, then you’ve missed to install the u2g8 library into the components, like the README.md says.

You’ve put it in lib_deps but it hasn’t picked up the dependency there to it seems – ESP-IDF components should be preffered for ESP-IDF.

I figured it out! Turns out that the u8g2_esp32_hal imports u8g2.h, but in the readme for that component, the example also tells you imports u8g2.h in your own application. I believe the issue I was encountering was the result of this double import. Once I removed the import statement in my own application, these flags worked:

	-I<path>/PlatformIO/Projects/<project>/.pio/libdeps/stable/U8g2/src/clib
	-L<path>/PlatformIO/Projects/<project>/.pio/libdeps/stable/U8g2/src/clib

All of this platformio.ini hacking is not needed if done via components because the CMakeLists.txt build logic takes care of everything (include paths, lib,…). As I show in GitHub - maxgerhardt/pio-espidf-u8g2, this works with the bog-standard platform = espressif32. It’s much cleaner and in the spirit of ESP-IDF components.