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?