Skip to content

Commit 76e40ba

Browse files
cli: generate man pages dynamically from command tree
Add dynamic man page generation by traversing the libecoli command tree at runtime. Add new option in grcli (--man and --man [COMMAND]) to walk through the registered command nodes and extract required data to generate properly formatted man pages in Markdown format. Do build system changes in meson to invoke grcli --man during the build phase. Convert the man pages in Markdown to groff format using go-md2man. Do changes for packaging files for both Debian and RPM Assisted-by: Cursor/Claude-4-Sonnet Signed-off-by: Abhiram R N <[email protected]>
1 parent 4eb7354 commit 76e40ba

File tree

12 files changed

+766
-88
lines changed

12 files changed

+766
-88
lines changed

cli/complete.c

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,33 @@
1414
#define FLAG(flags, help) with_help(help, EC_NODE_CMD(EC_NO_ID, flags))
1515
#define OPT(opts, help, ...) with_help(help, EC_NODE_CMD(EC_NO_ID, opts, __VA_ARGS__))
1616

17+
struct ec_node *grcli_options_node(void) {
18+
return EC_NODE_SUBSET(
19+
EC_NO_ID,
20+
FLAG("-h|--help", "Show usage help and exit."),
21+
OPT("-s|--socket " SOCK_PATH_ID,
22+
"Path to the control plane API socket.",
23+
ec_node("file", SOCK_PATH_ID)),
24+
FLAG("-e|--err-exit", "Abort on first error."),
25+
FLAG("-x|--trace-commands", "Print executed commands."),
26+
OPT("-f|--file PATH",
27+
"Read commands from _PATH_ instead of standard input.",
28+
ec_node("file", "PATH")),
29+
FLAG("-V|--version", "Print version and exit."),
30+
FLAG("-c|--bash-complete", "For use in bash completion."),
31+
OPT("-m|--man COMMAND",
32+
"Show man page for _COMMAND_ or list all commands if no argument.",
33+
ec_node("str", "COMMAND"))
34+
);
35+
}
36+
1737
static struct ec_node *bash_complete_node(struct ec_node *cmdlist) {
1838
return ec_node_sh_lex_expand(
1939
EC_NO_ID,
2040
EC_NODE_SEQ(
2141
EC_NO_ID,
2242
ec_node("any", "prog_name"),
23-
ec_node_option(
24-
EC_NO_ID,
25-
EC_NODE_SUBSET(
26-
EC_NO_ID,
27-
FLAG("-h|--help", "Show usage help and exit."),
28-
OPT("-s|--socket " SOCK_PATH_ID,
29-
"Path to the control plane API socket.",
30-
ec_node("file", SOCK_PATH_ID)),
31-
FLAG("-e|--err-exit", "Abort on first error."),
32-
FLAG("-x|--trace-commands", "Print executed commands.")
33-
)
34-
),
43+
ec_node_option(EC_NO_ID, grcli_options_node()),
3544
cmdlist
3645
)
3746
);

cli/complete.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@
66
#include <ecoli.h>
77

88
int bash_complete(struct ec_node *cmdlist);
9+
struct ec_node *grcli_options_node(void);

cli/main.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "exec.h"
66
#include "interact.h"
77
#include "log.h"
8+
#include "man.h"
89

910
#include <gr_api.h>
1011
#include <gr_api_client_impl.h>
@@ -27,6 +28,7 @@
2728
static void usage(const char *prog) {
2829
printf("Usage: %s [-e] [-f PATH] [-h] [-s PATH] [-V] [-x] ...\n", prog);
2930
printf(" %s -c|--bash-complete\n", prog);
31+
printf(" %s -m|--man [COMMAND]\n", prog);
3032
}
3133

3234
static void help(void) {
@@ -37,6 +39,8 @@ static void help(void) {
3739
puts(" -e, --err-exit Abort on first error.");
3840
puts(" -f PATH, --file PATH Read commands from file instead of stdin.");
3941
puts(" -h, --help Show this help message and exit.");
42+
puts(" -m, --man [COMMAND] Show man page for command or list all");
43+
puts(" commands.");
4044
puts(" -s PATH, --socket PATH Path to the control plane API socket.");
4145
puts(" Default: GROUT_SOCK_PATH from env or");
4246
printf(" %s).\n", GR_DEFAULT_SOCK_PATH);
@@ -171,8 +175,20 @@ int main(int argc, char **argv) {
171175
if ((cmdlist = init_commands()) == NULL)
172176
goto end;
173177

174-
if (argc >= 2 && (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--bash-complete")))
175-
return bash_complete(cmdlist);
178+
if (argc >= 2 && (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--bash-complete"))) {
179+
ret = bash_complete(cmdlist);
180+
goto end;
181+
}
182+
183+
if (argc >= 2 && (!strcmp(argv[1], "-m") || !strcmp(argv[1], "--man"))) {
184+
if (argc == 2) {
185+
print_main_man_page(cmdlist);
186+
ret = EXIT_SUCCESS;
187+
} else {
188+
ret = print_man_page(cmdlist, argc, argv);
189+
}
190+
goto end;
191+
}
176192

177193
if ((c = parse_args(argc, argv)) < 0)
178194
goto end;

cli/man.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2025 Robin Jarry
3+
4+
#pragma once
5+
6+
#include <ecoli.h>
7+
8+
int print_man_page(struct ec_node *cmdlist, int argc, char **argv);
9+
void print_main_man_page(struct ec_node *cmdlist);

0 commit comments

Comments
 (0)