examples: build system: add link-time registration plugin example

Closes https://github.com/espressif/esp-idf/issues/7682
This commit is contained in:
Ivan Grokhotkov
2022-03-30 00:40:52 +02:00
parent 273633ee31
commit ff6ccfff8e
13 changed files with 408 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS plugin_hello.c
PRIV_REQUIRES plugins
WHOLE_ARCHIVE)

View File

@@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include "plugins_api.h"
/**
* This is an example function implemented by the plugin.
*/
static void plugin_hello_greet(const char* arg)
{
if (arg == NULL) {
return;
}
printf("Hello, %s!\n", arg);
}
/* The code below demonstates both static and dynamic registration approaches. */
/**
* Static registration of this plugin can be achieved by defining the plugin description
* structure and placing it into .plugins_desc section.
* The name of the section and its placement is determined by linker.lf file in 'plugins' component.
*/
static const example_plugin_desc_t __attribute__((section(".plugins_desc"),used)) PLUGIN = {
.name = "Hello",
.greet = &plugin_hello_greet
};
/**
* Dynamic registration of this plugin can be achieved by calling plugin registration function
* ('example_plugin_register') from a "constructor" function. Constructor function is called automatically
* during application startup.
*/
static void __attribute__((constructor)) plugin_hello_self_register(void)
{
printf("Hello plugin performing self-registration...\n");
example_plugin_register(&(example_plugin_desc_t){
.name = "Hello",
.greet = &plugin_hello_greet
});
}

View File

@@ -0,0 +1,5 @@
idf_component_register(SRCS plugin_nihao.c
PRIV_REQUIRES plugins)
# This is equivalent to adding WHOLE_ARCHIVE option to the idf_component_register call above:
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE TRUE)

View File

@@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include "plugins_api.h"
/**
* This is an example function implemented by the plugin.
*/
static void plugin_nihao_greet(const char* arg)
{
if (arg == NULL) {
return;
}
printf("你好 %s!\n", arg);
}
/* The code below demonstates both static and dynamic registration approaches. */
/**
* Static registration of this plugin can be achieved by defining the plugin description
* structure and placing it into .plugins_desc section.
* The name of the section and its placement is determined by linker.lf file in 'plugins' component.
*/
static const example_plugin_desc_t __attribute__((section(".plugins_desc"),used)) PLUGIN = {
.name = "Nihao",
.greet = &plugin_nihao_greet
};
/**
* Dynamic registration of this plugin can be achieved by calling plugin registration function
* ('example_plugin_register') from a "constructor" function. Constructor function is called automatically
* during application startup.
*/
static void __attribute__((constructor)) plugin_nihao_self_register(void)
{
printf("Nihao plugin performing self-registration...\n");
example_plugin_register(&(example_plugin_desc_t){
.name = "Nihao",
.greet = &plugin_nihao_greet
});
}

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS plugins.c
INCLUDE_DIRS include
LDFRAGMENTS linker.lf)

View File

@@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/* This stucture describes the plugin to the rest of the application */
typedef struct {
/* A pointer to the plugin name */
const char* name;
/* A function which the plugin provides to the application.
* In this example, this function prints something to the console
* depending on the value of the argument 'arg'.
*/
void (*greet)(const char* arg);
} example_plugin_desc_t;
/**
* @brief Register the plugin with the application
* This function is called from each plugin's "constructor" function.
* It adds the plugin to the list.
* @param plugin_desc Pointer to the structure which describes the given plugin.
*/
void example_plugin_register(const example_plugin_desc_t* plugin_desc);
/**
* @brief Print the list of registered plugins to the console.
* This function is called from the application.
*/
void example_plugins_list(void);
/**
* @brief Invoke 'greet' function of each registered plugin with the given argument.
* This function is called from the application.
* @param arg argument to pass to plugins' greet functions.
*/
void example_plugins_greet(const char* arg);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,13 @@
[sections:plugins_desc]
entries:
.plugins_desc
[scheme:plugins_desc_default]
entries:
plugins_desc -> flash_rodata
[mapping:plugins_desc]
archive: *
entries:
* (plugins_desc_default);
plugins_desc -> flash_rodata KEEP() SORT(name) SURROUND(plugins_array)

View File

@@ -0,0 +1,71 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include <sys/queue.h>
#include "plugins_api.h"
/**
* Demonstration of dynamic registration (self-registration):
*
* - example_plugin_register function is called from "constructor" functions of each plugin.
* Information about the plugin is passed inside 'example_plugin_desc_t' structure.
* This function adds each plugin description into linked list (s_plugins_list).
*
* - example_plugins_greet function iterates over the linked list.
*/
struct plugin_record {
example_plugin_desc_t plugin_desc;
LIST_ENTRY(plugin_record) list_entry;
};
static LIST_HEAD(plugins_list, plugin_record) s_plugins_list = LIST_HEAD_INITIALIZER(s_plugins_list);
void example_plugin_register(const example_plugin_desc_t* plugin_desc)
{
struct plugin_record *record = (struct plugin_record *) malloc(sizeof(struct plugin_record));
if (record == NULL) {
abort();
}
memcpy(&record->plugin_desc, plugin_desc, sizeof(*plugin_desc));
struct plugin_record *head = LIST_FIRST(&s_plugins_list);
if (head == NULL) {
LIST_INSERT_HEAD(&s_plugins_list, record, list_entry);
} else {
LIST_INSERT_BEFORE(head, record, list_entry);
}
printf("Successfully registered plugin '%s'\n", plugin_desc->name);
}
void example_plugins_greet(const char* arg)
{
struct plugin_record *it;
LIST_FOREACH(it, &s_plugins_list, list_entry) {
printf("Calling greet function of plugin '%s'...\n", it->plugin_desc.name);
(*it->plugin_desc.greet)(arg);
printf("Done with greet function of plugin '%s'.\n", it->plugin_desc.name);
}
}
/**
* Demonstration of static registration.
* Symbols '_plugins_array_start' and '_plugins_array_end' mark the beginning and end
* of the array where 'example_plugin_desc_t' structures are placed by the linker.
* The names of these variables are determined by linker.lf in 'plugins' component,
* look for 'SURROUND(plugins_array)'.
*/
void example_plugins_list(void)
{
printf("List of plugins:\n");
extern const example_plugin_desc_t _plugins_array_start;
extern const example_plugin_desc_t _plugins_array_end;
for (const example_plugin_desc_t* it = &_plugins_array_start; it != &_plugins_array_end; ++it) {
printf("- Plugin '%s', function greet=%p\n", it->name, it->greet);
}
}