Skip to content

Commit 7141bca

Browse files
docs: refactor and complete build-plugin.md
Man life sure goes fast and gets really busy
1 parent 1bb7860 commit 7141bca

File tree

1 file changed

+129
-39
lines changed

1 file changed

+129
-39
lines changed

docs/build-plugin.md

Lines changed: 129 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,49 @@
1-
# How to develop a customfetch plugin (still WIP)
1+
# Developing a Customfetch Plugin
22

3-
Thanks to the new update since `v2.0.0-beta1`, customfetch is no longer just another neofetch-like program that fetches only system information. But it was made possible to develop and load user-made plugins for fetching any other information the user would like.
3+
> [!Note]
4+
> This guide is a work in progress and may change as customfetch continues to develop.
45
5-
Instead of installing different neofetch-like tools for different info fetching, e.g [onefetch](https://github.com/o2sh/onefetch) for viewing git tree infos or [githubfetch](https://github.com/isa-programmer/githubfetch) for viewing a GitHub user infos, you can use one neofetch-like tool (customfetch) and be able to view as many different infos as you would like to.
6+
Since version `v2.0.0-beta1`, **customfetch** has evolved beyond being just another neofetch-like program that displays system information.
7+
It now supports **user-made plugins**, allowing developers to fetch and display any type of information they want directly within customfetch.
68

7-
It's required having a knowledge about C++ programming (basic/mid) for creating a customfetch plugin.
9+
This means you no longer need separate tools like [onefetch](https://github.com/o2sh/onefetch) (for Git information) or [githubfetch](https://github.com/isa-programmer/githubfetch) (for GitHub profile information).
10+
With customfetch, you can display as much and different fetched infos you'd like in one single config.
811

9-
## What are customfetch plugins used for?
12+
Developing a customfetch plugin requires **basic to intermediate C++ knowledge**, as also Git.
1013

11-
Customfetch plugins are used for loading info tag `$<>` external modules, where at parsing it calls the module function automatically. For now, it doesn't let the user modify more the aspect of customfetch generally, but just fetching external infos other than system ones.
14+
---
1215

13-
## Developing and building the plugin
16+
## What Are Customfetch Plugins?
1417

15-
### Initializing
18+
Customfetch plugins are dynamically loaded modules that provide external info fetching, used in the info tag `$<>`.
19+
When the configuration is parsed, customfetch automatically calls the corresponding plugin function to fetch and display the data.
1620

17-
To make your plugins installable through customfetch's own plugin manager `cufetchpm`, you'll need to first create a git repository and a manifest file called `cufetchpm.toml`.
21+
Currently, plugins can only extend **data fetching** capabilities — they cannot modify the overall appearance or behavior of customfetch itself.
1822

19-
1. Create a git repository (`git init`)
2023

21-
2. run `cufetchpm gen-manifest` for generating a manifest template with comments explaning each config
24+
## 1. Initializing Your Plugin Project
2225

26+
If you want your plugin to be installable through customfetch’s plugin manager, `cufetchpm`, start by setting up your repository and manifest.
27+
28+
1. **Create a new Git repository**
29+
30+
2. **Generate a template manifest file:**
31+
32+
```bash
33+
$ cufetchpm gen-manifest
34+
```
35+
This command creates a `cufetchpm.toml` file with explanatory comments for each configuration option.
2336
After doing that, modify the manifest template with your repository name and url and other changes if needed.
2437

25-
### Developing
38+
## 2. Developing the Plugin
39+
Currently, plugins are only supported via the C++ API. Bindings for other languages like C, Rust, or Go are not yet available.
2640

27-
Now it's time to actually start developing the plugin. At the moment it's only compatible with C++ API, thus external languages such as C or Rust or Go, cannot create a binding of the libcufetch API.
41+
For this example, we will create a simple repository containing one plugin.
42+
If you want to see how to include multiple plugins in a single repository, refer to the [customfetch-plugin-github](https://github.com/Toni500github/customfetch-plugin-github/) example.
2843

29-
In this example, we gonna create repository that contains only one plugin, but `cufetchpm` supports installing multiple plugins in the same repository, you can checkout https://github.com/Toni500github/customfetch-plugin-github/ for creating a repository with multiple plguins.
44+
### Example Plugin Source
3045

31-
Let's create a simple `test-plugin.cc` source file. We're going to include the required libcufetch headers
46+
Create a new file called `test-plugin.cc` and include the required headers:
3247

3348
```c++
3449
#include <libcufetch/common.hh>
@@ -37,55 +52,130 @@ Let's create a simple `test-plugin.cc` source file. We're going to include the r
3752
```
3853

3954
<details>
40-
<summary><b>Click to expand for details about each header</b></summary>
55+
<summary><b>About the headers</b></summary>
4156

42-
* `<libcufetch/common.hh>` will include logging calls `die`, `error`, `warn`, `info` and `debug` (only if customfetch is run with `--debug=1`)
43-
and also macros such as `APICALL`, `EXPORT`, `PLUGIN_INIT` and `PLUGIN_FINISH`, which can be useful for facilitating the plugin development.
44-
* `<libcufetch/config.hh>` will include the `ConfigBase` class which is used for accessing the configs of the config file customfetch uses.
45-
* `<libcufetch/cufetch.hh>` will include the important structs `moduleArgs_t`, `callbackInfo_t`, `module_t` and `cfRegisterModule()` for creating/registering modules that will be called from the config file.
57+
* `<libcufetch/common.hh>` — includes logging utilities (`die`, `error`, `warn`, `info`, `debug`) and helpful macros such as `APICALL`, `EXPORT`, `PLUGIN_INIT`, and `PLUGIN_FINISH`.
58+
* `<libcufetch/config.hh>` — provides access to the `ConfigBase` class for reading configuration values.
59+
* `<libcufetch/cufetch.hh>` — defines important structs like `moduleArgs_t`, `callbackInfo_t`, `module_t`, and the `cfRegisterModule()` function used for module registration.
4660
</details>
4761

48-
And let's create our handler callback function:
62+
---
63+
64+
### Defining a Handler Function
65+
66+
A handler function must return a `std::string` and take a `const callbackInfo_t*` parameter.
67+
Let’s create a simple submodule handler:
4968

5069
```c++
5170
std::string test_submod_func(const callbackInfo_t* cb) {
5271
return "Sub module";
5372
}
5473
```
74+
<details>
75+
<summary><b>About callbackInfo_t</b></summary>
76+
77+
The struct `callbackInfo_t` contains:
78+
* `const moduleArgs_t* moduleArgs`: A linked list including module arguments. An argument may be specified for any part of the module path (e.g. `disk(/).used(GiB)`, `test.hi(a)`)
79+
* `parse_args_t& parse_args`: a context struct that can be used in the `parse()` function, for parsing strings with tags.
80+
```c++
81+
/* Context struct used when parsing tags in strings.
82+
* @param modules_info The modules fetched infos
83+
* @param config The config instance
84+
* @param pure_output The output of the string but without tags
85+
* @param layout The layout of customfetch
86+
* @param tmp_layout The temponary layout to be used for multiple-line modules
87+
* @param no_more_reset uhh let me see
88+
* @param parsing_layout Are we parsing the layout or the ASCII art logo?
89+
*/
90+
struct EXPORT parse_args_t
91+
{
92+
const moduleMap_t& modules_info;
93+
const ConfigBase& config;
94+
std::string& pure_output;
95+
std::vector<std::string>& layout;
96+
std::vector<std::string>& tmp_layout;
97+
bool parsing_layout;
98+
bool no_more_reset = false;
99+
bool firstrun_clr = true; // don't use it. Internal "flag"
100+
};
101+
```
102+
</details>
55103
56-
An handler function must return a `std::string` and take in parameters a `const callbackInfo_t*`.
57-
104+
---
58105
106+
### Plugin Initialization Function
59107
60-
Now the important part starts here. We going to create our plugin main start entry function, which will be called once the plugin got loaded successfully:
108+
Next, define the plugin’s main initialization entry point.
109+
This function is automatically called when the plugin is successfully loaded.
61110
62111
```c++
63112
APICALL EXPORT PLUGIN_INIT(void *handle, const ConfigBase& config) {
64-
    
113+
65114
}
66115
```
116+
* `handle` — The plugin (shared library) handle, as returned by `dlopen()`.
117+
* `config` — An instance of the parsed customfetch config file.
67118

68-
* `handle` is the plugin (library) main handler from `dlopen()`.
119+
Inside this function, you will declare and register the modules your plugin provides.
69120

70-
* `config` is the instance of the parsed config file from customfetch, where you can get parsed values of the whole file.
121+
---
71122

72-
Inside the main start entry (canonically `void start()`) we're going to declare and register all the modules the user can use.
123+
### Defining and Registering Modules
73124

74-
Since it's possible to create recursive modules, we're going to start defining modules from bottom to top
125+
customfetch allows defining **nested modules** (submodules).
126+
For this reason, it’s best to define modules from the bottom up.
75127

76128
```c++
77-
/* Here we create the 'submod' submodule. in customfetch there's no practical difference between a parent module and a submodule. So we just define it like any other module. */
78-
/* We will not register this, it will be implicitly added through its parent module (so we can't directly invoke `submod`, we can only invoke `root.submod`) */
79-
module_t submod_module = {"submod", "a generic submodule description", {}, test_submod_func};
129+
// Define a submodule. There’s no practical difference between a module and a submodule in customfetch.
130+
module_t submod_module = { "submod", "A generic submodule description", {}, test_submod_func };
80131

81-
/* And here we create the 'root' module. This is what we're actually going to register and it will include the 'submod' module as a submodule. */
82-
/* This module doesn't have a handler, so it can't be used in the config (`root` won't work). We'll instead use `root.submod` in the config (which does have a handler). */
83-
module_t root_module = { "root", "root module description", { std::move(submod_module) }, NULL };
132+
// Define a root module that contains the submodule.
133+
module_t root_module = { "root", "Root module description", { std::move(submod_module) }, NULL };
84134

85-
/* Register the module. */
86-
/* This will take the root module, recursively add it and its submodules to the list, and continue until its finished everything. */
135+
// Register the root module (and all its submodules recursively).
87136
cfRegisterModule(root_module);
88137
```
89138
90-
### Building
91-
Building a customfetch plugin doesn't require any specific build-system such as cmake or meson, heck you can straight just put `c++ test-plugin.cc -std=c++17 -shared -fPIC -o test-plugin.so -Wl,--export-dynamic -Wl,-undefined,dynamic_lookup` in your plugin table `build-steps` section if it's a super simple Linux/android plugin.
139+
In this example:
140+
- `root.submod` can be used in the customfetch configuration file.
141+
- The parent module `root` does not have a handler, so it will return `(unknown/invalid module)`
142+
143+
---
144+
145+
## 3. Building the Plugin
146+
> [!Note]
147+
> The build command shown is for Linux/Unix systems. Windows and macOS may require different compiler flags and file extensions (`.dll` for Windows, `.dylib` for macOS).
148+
149+
You can build a plugin using any build system, but a simple compiler command works fine:
150+
151+
```bash
152+
c++ test-plugin.cc -std=c++17 -shared -fPIC -o test-plugin.so -Wl,--export-dynamic -Wl,-undefined,dynamic_lookup
153+
```
154+
155+
If you defined this in your manifest’s `build-steps` plugin section, `cufetchpm` can build it automatically when installing.
156+
157+
---
158+
159+
## 4. Trying Out the Plugin
160+
Now save and commit all your changes and `git push` to the remote repository.
161+
To install your plugin, you need to run:
162+
```bash
163+
$ cufetchpm install <your-git-repo-url>
164+
```
165+
166+
If `cufetchpm` doesn't report any errors, congratulations!
167+
To try out your plugin, run:
168+
```bash
169+
$ customfetch -nm "\$<root.submod>"
170+
Sub module
171+
```
172+
173+
## Tips and Troubleshooting
174+
175+
- Ensure your handler functions strictly match the required signature.
176+
- Always call `cfRegisterModule()` for your top-level modules.
177+
- When debugging, use `--debug=1` to view additional logs from your plugin.
178+
- If your plugin doesn't load, verify symbol exports with:
179+
```bash
180+
nm -D test-plugin.so | grep "start"
181+
```

0 commit comments

Comments
 (0)