overview
divifetch is a build-time fetch script compiler.
instead of fetching system information on each run and dynamically assembling the output like the other fetch scripts, divifetch generates a C source file from a config, which you can compile into a standalone binary.
this approach allows for the resulting code to be mostly hard-coded at generation time. this also allows for the resulting binary to not have any runtime dependencies, to not waste time reading a config file at startup and to be as small as it should be.
how it works
config.conf -> generator -> fetch.c and Makefile -> make -> divifetch
- you create a config file
generator sourcereads the config and producesfetch.cgenerator makereads the make config and produces the Makefile- you compile
fetch.cwith make
usage
./generator [OPTIONS] SUBCOMMAND
subcommands
| subcommand | description |
|---|---|
source |
read config.conf and write the fetch source to fetch.c |
config |
generate an example config file (auto-detects OS, CPU, GPUs, mounts) |
make |
read make.conf and generate the Makefile |
multiple commands can be specified (default:
source,make)
options
| flag | long form | argument | description |
|---|---|---|---|
-h |
--help |
print help and exit | |
-c |
--config |
<file> |
config file to read for source (default: config.conf) |
-cm |
--make-config |
<file> |
config file to read for make (default: make.conf) |
-os |
--out-source |
<file> |
where to write the generated source (default: fetch.c) |
-oc |
--out-config |
<file> |
where to write the example config (default: config.conf.example) |
-om |
--out-make |
<file> |
where to write the Makefile (default: Makefile) |
-ccan be specified multiple times for random runtime selection between them
typical workflow
# clone the repo:
git clone https://git.divio.city/me/divifetch.git
cd divifetch
# compile the generator:
g++ -std=c++17 -o generator generator.cpp
# generate, copy and edit the example config file:
./generator config -oc config.conf
vi config.conf
# generate the source code and Makefile:
./generator source make
# build the fetch script:
make
config file format
configs use a simple s-expression syntax.
-
a form is
(name arg1 arg2 ...) -
comments start with
;and run to end of line -
string values are quoted with
" -
-and_can be interchangeably used in field names
configuring the fetch
global config
there is one config form per file. it sets global defaults that apply to all entries.
(config
(art-width 45)
(art-file "ascii/cat.txt")
(art-prefix "\033[1m")
(art-suffix "\033[0m")
(key-prefix "\033[1m\033[34m")
(key-suffix "\033[0m: ")
(value-prefix "")
(value-suffix ""))
| field | type | description |
|---|---|---|
art-width |
integer | column width reserved for the ascii art (including padding); prefixes and suffixes are not counted |
art-file |
string | path to a text file containing the ascii art (if missing or empty the art column is left blank) |
art-prefix |
string | escape sequence inserted before each art line |
art-suffix |
string | escape sequence inserted after each art line |
key-prefix |
string | default escape sequence before each entry's key |
key-suffix |
string | default text/sequence after each entry's key (e.g. : ) |
value-prefix |
string | default escape sequence before each entry's value |
value-suffix |
string | default text/sequence after each entry's value |
entry types
each entry occupies one line of output.
lines are paired with art lines from top to bottom.
if there are more entries than art lines the art column is padded with spaces.
static entries
values are known at generation time and baked into the binary.
| entry type | description |
|---|---|
static |
the standard key/value entry |
plain |
like static but with all prefixes and suffixes stripped. useful for decorative lines or headers |
break |
an empty line (no key, no value, no prefixes) |
colors_normal |
prints the 8 standard terminal palette colors as colored blocks |
colors_bright |
prints the 8 bright terminal palette colors as colored blocks |
dynamic entries
values are resolved at runtime by calling a module function. the entry type must match a subdirectory name inside modules/.
if a module takes an argument (like mount), pass it as the second positional value or via the value field.
(mount "Mount (/)" "/")
; ^ ^
; key argument
per-entry fields
every entry (static or dynamic) accepts optional field overrides in named or positional form.
positional form
(static "OS" "Artix GNU/Linux")
; ^ ^
; key value
values are assigned in the following order:
key, value, key-prefix, key-suffix, value-prefix, value-suffix
named form
use (field-name value) pairs inside the entry form
(static
(key "OS")
(value "Artix GNU/Linux"))
both forms can be mixed
positional arguments come first, named pairs after
(static "OS"
(value "Artix GNU/Linux"))
| field | description |
|---|---|
key |
the label shown before the value; defaults to the entry type with a capitalised first letter |
value |
for static entries: the displayed value. for dynamic entries: optional argument passed to the module function |
key-prefix |
overrides the global key-prefix for this entry |
key-suffix |
overrides the global key-suffix for this entry |
value-prefix |
overrides the global value-prefix for this entry |
value-suffix |
overrides the global value-suffix for this entry |
ansi escape codes
strings in the config support ANSI escape sequences written as \033[...m.
for a reference on color and formatting codes see: https://jakob-bagterp.github.io/colorist-for-python/ansi-escape-codes/
common examples:
| code | effect |
|---|---|
\033[0m |
reset all formatting |
\033[1m |
bold |
\033[30m-\033[37m |
foreground colors (black → white) |
\033[90m-\033[97m |
bright foreground colors |
\033[38;2;R;G;Bm |
RGB foreground color |
module system
a dynamic module lives in a subdirectory of modules/ and consists of three files:
modules/
\--- kernel/
|--- module.h # header for the module
|--- module.c # source defining the module function
\--- module.conf # configuration for the make template
the generator reads only the modules that are actually referenced in your config, so unused ones produce no code in the output binary.
module function signature
every module must export exactly one function following this convention:
// module without arguments
const char* kernel_module_preset(char* buf, size_t buf_size);
// module with an argument
const char* mount_module_preset(const char* arg, char* buf, size_t buf_size);
the function writes its result into buf (256 bytes) and returns a pointer to it.
writing a new module
- create a directory
modules/$NAME/ - add a
module.hheader file - add a
module.cfile implementing$NAME_module_preset - add a
module.conffile specifying which make template to use - use
$NAMEin your config
reference hello-world modules are provided for C, C++ and Rust.
entry type names with hyphens are converted to underscores when generating the function name
compilation
for each dynamic module referenced by the config
- the generator checks whether the module contains
module.conf - the module language is determined (
cby default) - the matching template is loaded from
make.conf - build rules are generated automatically
- only modules used in the config are compiled
unused modules generate no build rules and are not linked into the final binary.
configuring make templates
make.conf
make.conf contains one or more template forms.
each template defines how modules written in a specific language are compiled into object files and optionally archived into static libraries.
(template c
(source-file "module.c")
(command "gcc -O2 -static -s -c $< -o $@"))
(template cpp
(source-file "module.cpp")
(command "g++ -std=c++17 -O2 -c $< -o $@")
(linker-flags "-lstdc++"))
| field | description |
|---|---|
| source-file | source file inside the module directory (required) |
| command | Makefile command used to compile the module (required) |
| object-extension | extension of the generated object file |
| archive | if yes, the object file is first packed into a static library before linking |
| linker-flags | additional flags appended to the final link command |
module.conf
a module may contain a module.conf file, where the language and per-module template overrides are specified.
(language c)
(template
(command "gcc -O3 -march=native -flto -pipe -funswitch-loops -static -s -c $< -o $@"))
if no language is specified, the generator defaults to using the c template.
generated targets
the generated Makefile provides the following targets:
| target | description |
|---|---|
all |
build the final binary |
clean |
remove the build directory |
install |
install the binary into $(DESTDIR) |
uninstall |
remove the installed binary |
run |
build and execute the binary |
default variables
DESTDIR = /usr/local/bin
TARGET = divifetch
directory structure
divifetch/
|--- generator.cpp # the generator source
|--- config.conf # your config file
|--- config.conf.example # generated example config
|--- fetch.c # generated fetch source (do not edit manually)
|--- Makefile # generated Makefile
|--- ascii/
| \--- *.txt # ascii art files
\--- modules/
|--- kernel/
| |--- module.h
| \--- module.c
| |--- module.conf
|--- shell/
| |--- module.h
| \--- module.c
| |--- module.conf
\--- ... # one directory per dynamic module