Injection of version of the computed libraries

Yes that’s possible using Advanced Scripting, we can scan the project’s library dependencies, and then make them available to the code as macros, which evaluate to strings.

E.g., the platformio.ini

[env:uno]
platform = atmelavr
board = uno
debug_tool = simavr
framework = arduino
lib_deps = 
    adafruit/Adafruit GFX Library @ ^1.10.6
    adafruit/Adafruit BusIO @ ^1.7.2
    Wire
    SPI
extra_scripts = add_lib_info.py

with the file add_lib_info.py in the root of the project

from platformio.builder.tools.piolib import ProjectAsLibBuilder, PackageItem, LibBuilderBase
from SCons.Script import ARGUMENTS  
from shlex import quote
Import("env", "projenv")


# from https://github.com/platformio/platformio-core/blob/develop/platformio/builder/tools/piolib.py
def _correct_found_libs(lib_builders):
    # build full dependency graph
    found_lbs = [lb for lb in lib_builders if lb.dependent]
    for lb in lib_builders:
        if lb in found_lbs:
            lb.search_deps_recursive(lb.get_search_files())
    for lb in lib_builders:
        for deplb in lb.depbuilders[:]:
            if deplb not in found_lbs:
                lb.depbuilders.remove(deplb)

print("Script entry point")

project = ProjectAsLibBuilder(env, "$PROJECT_DIR")

# rescan dependencies just like in py file above. otherwise dependenceis are empty
ldf_mode = LibBuilderBase.lib_ldf_mode.fget(project)
lib_builders = env.GetLibBuilders()
project.search_deps_recursive()
if ldf_mode.startswith("chain") and project.depbuilders:
    _correct_found_libs(lib_builders)

# for debugging
def _print_deps_tree(root, level=0):
    margin = "|   " * (level)
    for lb in root.depbuilders:
        title = "<%s>" % lb.name
        pkg = PackageItem(lb.path)
        if pkg.metadata:
            title += " %s" % pkg.metadata.version
        elif lb.version:
            title += " %s" % lb.version
        print("%s|-- %s" % (margin, title), end="")
        if int(ARGUMENTS.get("PIOVERBOSE", 0)):
            if pkg.metadata and pkg.metadata.spec.external:
                print(" [%s]" % pkg.metadata.spec.url, end="")
            print(" (", end="")
            print(lb.path, end="")
            print(")", end="")
        print("")
        if lb.depbuilders:
            _print_deps_tree(lb, level + 1)

# create a map of all used libraries and their version.
# the structure of the tree is not captured, just library names and versions. 
library_versions = dict()
def get_all_library_dependencies(root, level=0):
    global library_versions
    for lb in root.depbuilders:
        pkg = PackageItem(lb.path)
        lib_name = lb.name
        lib_version = pkg.metadata.version if pkg.metadata else lb.version
        library_versions[str(lib_name)] = str(lib_version)
        if lb.depbuilders:
            get_all_library_dependencies(lb, level + 1)

#print("PRINTING DEP TREE")
#_print_deps_tree(project)

get_all_library_dependencies(project)
print(library_versions)

# convert found library names and versions into macros for code.
# style and formating can be arbitrary: here I chose to hold everything in one big string.
macro_value = ""
for lib, version in library_versions.items(): 
    #print(lib + ": " + version)
    # primitive: simply 'library':'version' format. does not escape anything specifically.
    # this is chosen to work around passing the string as -D argument in the shell.
    # we have to additionally add a backslash before a quote to shell-escape it.
    #lib = lib.replace(" ", "\\ ")
    #lib = lib.replace(" ", "\\ ")
    #macro_value += "\"" + lib + "\":\""+  version +"\","
    #macro_value += "*" + lib + "*:*"+  version +"*,"
    macro_value += "'" + lib + "':'"+  version +"',"
# chop off last comma
macro_value = macro_value[:-1]
# escape it all in quotes
macro_value = "\\\"" + macro_value + "\\\""

# add to build system as macro
print("PLATFORMIO_USED_LIBRARIES = " + str(macro_value))
projenv.Append(CPPDEFINES=[
  ("PLATFORMIO_USED_LIBRARIES", macro_value)
])

def make_macro_name(lib_name):
    lib_name = lib_name.upper()
    lib_name = lib_name.replace(" ", "_")
    return lib_name

# also add all individual library versions
for lib, version in library_versions.items():
    projenv.Append(CPPDEFINES=[
     ("LIB_VERSION_%s" % make_macro_name(lib) , "\\\"" + version + "\\\"")
    ])
    print("LIB_VERSION_%s = %s" % (make_macro_name(lib), version))

With src\main.cpp

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <SPI.h>
#include <string.h>

/* use provided PLATFORMIO_USED_LIBRARIES macro */ 
/* place string in flash so that it doesn't use up RAM. 
*  but then also needs special handling of PROGMEM strings. */
const char used_libs[] PROGMEM = { PLATFORMIO_USED_LIBRARIES };
__FlashStringHelper* used_libs_str = (__FlashStringHelper *)used_libs; 

/* prints the string that contains all libraries. doesn't require you to know the specific macros. */
void print_used_libraries() {
    Serial.println(F("Used libraries (PLATFORMIO_USED_LIBRARIES)"));
    Serial.println(used_libs_str);
    /* you can search through the string per strchr_P() functions etc */
}

/* prints version of a library that we know is included. */
void print_known_libray() {
    //again uses F() to create a flash-string to save space on the poor Uno..
    Serial.print(F("Adafruit GFX library version: "));
    Serial.println(F(LIB_VERSION_ADAFRUIT_GFX_LIBRARY));
}

void setup(){
    Serial.begin(9600);
}

void loop(){
    print_used_libraries();
    print_known_libray();
    delay(1000);
}

Prints

Used libraries (PLATFORMIO_USED_LIBRARIES)
'Adafruit GFX Library':'1.10.6','Adafruit BusIO':'1.7.2','Wire':'1.0','SPI':'1.0'
Adafruit GFX library version: 1.10.6

The python script encodes all used library names and their exact used versions in the macro PLATFORMIO_USED_LIBRARIES and saves them in a string (that will be saved in PROGMEM / Flash).

It also encodes each library and their version in its own macro, so e.g. “Adafruit GFX Library” of version “1.10.6” is turned into the macro LIB_VERSION_ADAFRUIT_GFX_LIBRARY with value "1.10.6"

Does that solve your problem?

2 Likes