Collect2.exe linker error with: undefined reference to

Hi all

I am trying to modularize my code by crating git submodules for libraries/dependencies, but I am running into a linker problem: collect2.exe. I am compiling and building using PlatformIO in VScode.

The linking error occurs with the following structure. Please note I have simplified the names and structure for illustration.

|--include
|--lib
|  |--MyLibFolder
|    |--src
|      |-myLib.h
|      |-myLib.cpp
|    |--ExternalLibFolder
|      |--src
|        |-externalLib.h
|        |-externalLib.cpp
|--src
|  |-main.cpp

Where:

//main.cpp
#include "myLib.h"

and

//myLib.h
#include "../ExternalLibFolder/src/externalLib.h"
void myTask(void * parameter);

//myLib.cpp
#include "myLib.h"
void myTask(void * parameter) {
  aClass myClass(someVal);
  myClass.init();

  while(1) {
    //blah
  }
}

and

//externalLib.h
class aClass :anInheritance{
  aClass(someType parameter);
  void inti();
}

//externalLib.cpp
aClass::aClass(someType parameter){}
void aClass::init() {
  anInheritance::begin();
}

This structure returns the following error:

In function `MyTask()`
myLib.cpp:(.text._....): undefined reference to `aClass::aClass(someType &)'
myLib.cpp:(.text._....): undefined reference to `aClass::init()'
collect2.exe: error: ld returned 1 exit status
***[.pio\build\teensy41\firmware.elf] Error 1

NOTE: when I place all source files into the lib/MyLibFolder/src folder then the program runs and compiles perfectly. Alternatively if I place every submodule under the lib/ folder then the program also runs fine. Its only once I split the code into submodules in a recursive manner that I get this problem.

I am not sure how to approach this linker issue and any help would be great

In order for the build to be successful the following two lines had to be added to the platformio.ini file:

lib_extra_dirs = lib/MyLibFolder/ExternalLibFolder
lib_ldf_mode = chain+

I found one really strange thing while debugging to which I dont have an answer to. I am running FreeRTOS and the truly strange thing is that the program built successfully when:

  1. I remove lib_ldf_mode = chain+ line from platformio.ini file
  2. loop() looks like this:
void loop() {
  /*
  #include externalLib.h
  */
}

As soon as I remove the /* #include externalLib.h */ line from loop() OR I change it to //#include externalLib.h the linker ran into the issue again. I then had to add the lib_ldf_mode = chain+ line to platformio.ini.

This folder structure is not supported. MyLibFolder will be interpreted as having all relevant sources within src/, and ExternalLibFolder will be ignored. It would work if you renamed src to something else, I think, because then it would fall in the “all source files are directly under the library folder” case. Refer to the README that is automatically created in the libs/ folder.

@maxgerhardt thank you for your response. I see. What is the reason for this? VScode is the only IDE that does this. I might be lacking a different viewpoint to why this was done, but this is limiting.

To have measures in place that prevent easy modularity in code seems silly. This means one would have to create git submodules in the lib folder, but that both breaks modularity and just makes larger programs unworkable.

Libraries with git submodules on occasion can go three level deep, each level with its own list of submodules. This allows for perfect modularity and version control of every submodule. (down to fixing submodules/libraries to tags or just leave it on master for latest release)

PlatformIO has the best features when working with Arduino environment, hence I am building this in PlatformIO. But no other compiler/linker (VisualGDB, uVision Keil, STMCubeIDE,etc) has this issue.

I am by no measure someone who would call themselves knowligible in matters compiling/linking, perhaps you have some more insight in this?

It’s not about the IDE, it’s what the build system, PlatformIO, chooses to recognize as a valid source structure. This was mostly influenced by Arduino libraries and compatibility, I think. But especially with scripting, nearly every structure (and custom logic) becomes possible.