commit 78a82661a25a170ca566c13d8107431038430d8b
parent 63267da059803b24337e2f890d2cc0be8f627fc1
Author: Yersa Nordman <finwo@pm.me>
Date: Sat, 12 Sep 2026 22:00:34 +0200
Added versioning
Diffstat:
8 files changed, 190 insertions(+), 92 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -2,3 +2,4 @@
*.o
/dep
/license.c
+/.version
diff --git a/Makefile b/Makefile
@@ -5,6 +5,8 @@ FIND=$(shell which gfind find | head -1)
UNAME_S:=$(shell uname -s)
UNAME_M:=$(shell uname -m)
+VERSION?=0.1.0
+
SRC:=
INCLUDES:=
CFLAGS:=
@@ -55,10 +57,14 @@ OBJ:=$(OBJ:.cc=.o)
OBJ+=$(LICENSE_OBJ)
CFLAGS+=${INCLUDES}
+CFLAGS+=-DDEP_VERSION=\"$(VERSION)\"
.PHONY: default
default: dep
+.PHONY: FORCE
+FORCE:
+
license.o: LICENSE.md
$(OBJCOPY) --input binary --output $(ARCH_TARGET) --binary-architecture $(ARCH_BIN) $< $@
@@ -107,6 +113,14 @@ lib/tidwall/json.c:
.c.o:
${CC} $< ${CFLAGS} -c -o $@
+# Tracks the version we last built with, only rewritten when it actually
+# changes, so overriding VERSION on the command line rebuilds what it should
+.version: FORCE
+ @echo '$(VERSION)' | cmp -s - $@ || echo '$(VERSION)' > $@
+
+# Bakes in DEP_VERSION, so it must follow changes to VERSION
+src/command/version/main.o: .version
+
dep: $(LIBS) $(OBJ)
${CC} ${OBJ} ${CFLAGS} ${LDFLAGS} -o dep
strip -u -r dep 2>/dev/null || true
@@ -117,7 +131,7 @@ install: dep
.PHONY: clean
clean:
- rm -f $(OBJ)
+ rm -f $(OBJ) .version
.PHONY: format
format:
diff --git a/README.md b/README.md
@@ -9,25 +9,23 @@ Summary
```
Usage: dep [global options] <command> [command options]
+
Global options:
- n/a
+ -h, --help Show this help message and exit
+ -V, --version Show version information and exit
Commands:
- a(dd) Add a new dependency to the project
- i(nstall) Install all the project's dependencies
- init Initialize a new project with a .dep file
- license Show license information
- r(epo(sitory)) Repository management
- help [topic] Show this help or the top-level info about a command
-
-Help topics:
- global This help text
- add More detailed explanation on the add command
- install More detailed explanation on the install command
- init More detailed explanation on the init command
- repository More detailed explanation on the repository command
+ v(ersion) Show version information
+ help [command] Show this help or the top-level info about a command
+ r(epo(sitory)) Repository management
+ license Show license information
+ i(nstall) Install all the project's dependencies
+ init Initialize a new project with a .dep file
+ a(dd) Add a new dependency to the project
```
+Run `dep help <command>` for the details on a single command.
+
Installation
------------
diff --git a/src/command/help/main.c b/src/command/help/main.c
@@ -3,66 +3,47 @@
#include <string.h>
#include "command/command.h"
-
-static void print_global_help(void) {
- printf("Usage: dep [global options] <command> [command options]\n");
- printf("\n");
- printf("Global options:\n");
- printf(" n/a\n");
- printf("\n");
- printf("Commands:\n");
-
- struct cmd_struct *cmd = commands;
- while (cmd) {
- printf(" %-16s %s\n", cmd->display ? cmd->display : cmd->name[0], cmd->description ? cmd->description : "");
- cmd = cmd->next;
- }
-
- printf("\n");
- printf("Help topics:\n");
- printf(" global This help text\n");
-
- cmd = commands;
- while (cmd) {
- printf(" %-16s More detailed explanation on the %s command\n", cmd->name[0], cmd->name[0]);
- cmd = cmd->next;
- }
-}
+#include "common/usage.h"
static int cmd_help(int argc, const char **argv) {
if (argc < 1) {
- print_global_help();
+ dep_print_global_usage();
return 0;
}
if (argc == 1 && (!strcmp(argv[0], "help") || !strcmp(argv[0], "h") || !strcmp(argv[0], "global"))) {
- print_global_help();
+ dep_print_global_usage();
return 0;
}
const char *topic = (argc > 1) ? argv[1] : argv[0];
if (!strcmp(topic, "global")) {
- print_global_help();
+ dep_print_global_usage();
return 0;
}
struct cmd_struct *cmd = commands;
while (cmd) {
- if (!strcmp(topic, cmd->name[0])) {
- if (cmd->help_text) {
- printf("%s\n", cmd->help_text);
- } else {
- printf("dep %s - %s\n\n", cmd->name[0], cmd->description);
- printf(" %s\n", cmd->display);
+ // Aliases resolve to the same help as the command itself
+ const char **name = cmd->name;
+ while (*name) {
+ if (!strcmp(topic, *name)) {
+ if (cmd->help_text) {
+ printf("%s\n", cmd->help_text);
+ } else {
+ printf("dep %s - %s\n\n", cmd->name[0], cmd->description);
+ printf(" %s\n", cmd->display);
+ }
+ return 0;
}
- return 0;
+ name++;
}
cmd = cmd->next;
}
- fprintf(stderr, "Error: unknown help topic '%s'\n", topic);
- fprintf(stderr, "Run 'dep help' for available topics.\n");
+ fprintf(stderr, "Error: no help available for '%s'\n", topic);
+ fprintf(stderr, "Run 'dep help' for the available commands.\n");
return 1;
}
@@ -76,7 +57,7 @@ void __attribute__((constructor)) cmd_help_setup(void) {
cmd->fn = cmd_help;
static const char *help_names[] = {"help", "h", NULL};
cmd->name = help_names;
- cmd->display = "help [topic]";
+ cmd->display = "help [command]";
cmd->description = "Show this help or the top-level info about a command";
cmd->help_text =
"dep help - Show this help or the top-level info about a command\n"
@@ -91,6 +72,6 @@ void __attribute__((constructor)) cmd_help_setup(void) {
"Examples:\n"
" dep help # Show general help\n"
" dep help add # Show help for add command\n"
- " dep help install # Show help for install command\n";
+ " dep help install # Show help for install command\n";
commands = cmd;
}
diff --git a/src/command/version/main.c b/src/command/version/main.c
@@ -0,0 +1,43 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "command/command.h"
+
+static int cmd_version(int argc, const char **argv) {
+ (void)argc;
+ (void)argv;
+ printf("%s\n", DEP_VERSION);
+ return 0;
+}
+
+void __attribute__((constructor)) cmd_version_setup(void) {
+ struct cmd_struct *cmd = calloc(1, sizeof(struct cmd_struct));
+ if (!cmd) {
+ fprintf(stderr, "Failed to allocate memory for version command\n");
+ return;
+ }
+ cmd->next = commands;
+ cmd->fn = cmd_version;
+ static const char *version_names[] = {"version", "v", NULL};
+ cmd->name = version_names;
+ cmd->display = "v(ersion)";
+ cmd->description = "Show version information";
+ cmd->help_text =
+ "dep version - Show version information\n"
+ "\n"
+ "Usage:\n"
+ " dep version\n"
+ " dep v\n"
+ "\n"
+ "Description:\n"
+ " Print the version of dep and exit.\n"
+ "\n"
+ " The global --version flag is dispatched to this command, so both\n"
+ " produce the exact same output.\n"
+ "\n"
+ "Examples:\n"
+ " dep version # Show the version\n"
+ " dep --version # Same, as a global flag\n"
+ " dep -V # Same, short form\n";
+ commands = cmd;
+}
diff --git a/src/common/usage.c b/src/common/usage.c
@@ -0,0 +1,78 @@
+#include "usage.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "cofyc/argparse.h"
+#include "command/command.h"
+
+static int opt_version = 0;
+
+static const char *const usages[] = {
+ "dep [global options] <command> [command options]",
+ NULL,
+};
+
+static struct argparse_option options[] = {
+ OPT_GROUP("Global options:"),
+ OPT_BOOLEAN('h', "help", NULL, "Show this help message and exit", argparse_help_cb, 0, OPT_NONEG),
+ OPT_BOOLEAN('V', "version", &opt_version, "Show version information and exit", NULL, 0, OPT_NONEG),
+ OPT_END(),
+};
+
+// argparse prints the epilog at the end of its usage output, which is the only
+// way to get our command listing into the built-in --help handler
+static char *build_epilog(void) {
+ char *buffer = NULL;
+ size_t size = 0;
+ FILE *out = open_memstream(&buffer, &size);
+ if (!out) {
+ return NULL;
+ }
+
+ struct cmd_struct *cmd = commands;
+
+ // The %-17s aligns our descriptions with the ones argparse prints
+ fprintf(out, "\nCommands:\n");
+ while (cmd) {
+ fprintf(out, " %-17s %s\n", cmd->display ? cmd->display : cmd->name[0],
+ cmd->description ? cmd->description : "");
+ cmd = cmd->next;
+ }
+
+ fclose(out);
+
+ // argparse adds a newline of its own
+ if (size && buffer[size - 1] == '\n') {
+ buffer[size - 1] = '\0';
+ }
+
+ return buffer;
+}
+
+int dep_global_options(int argc, const char **argv, int *show_version) {
+ struct argparse argparse;
+ char *epilog = build_epilog();
+
+ opt_version = 0;
+ argparse_init(&argparse, options, usages, ARGPARSE_STOP_AT_NON_OPTION);
+ argparse_describe(&argparse, NULL, epilog);
+ argc = argparse_parse(&argparse, argc, argv);
+ free(epilog);
+
+ if (show_version) {
+ *show_version = opt_version;
+ }
+
+ return argc;
+}
+
+void dep_print_global_usage(void) {
+ struct argparse argparse;
+ char *epilog = build_epilog();
+
+ argparse_init(&argparse, options, usages, ARGPARSE_STOP_AT_NON_OPTION);
+ argparse_describe(&argparse, NULL, epilog);
+ argparse_usage(&argparse);
+ free(epilog);
+}
diff --git a/src/common/usage.h b/src/common/usage.h
@@ -0,0 +1,11 @@
+#ifndef USAGE_H
+#define USAGE_H
+
+// Parse the global options, returns the remaining argument count
+// Sets show_version when --version was given, handles --help by itself
+int dep_global_options(int argc, const char **argv, int *show_version);
+
+// Print the global usage: options, commands and help topics
+void dep_print_global_usage(void);
+
+#endif // USAGE_H
diff --git a/src/main.c b/src/main.c
@@ -2,55 +2,27 @@
#include <stdlib.h>
#include <string.h>
-#include "cofyc/argparse.h"
#include "command/command.h"
+#include "common/usage.h"
#include "erkkah/naett.h"
#include "rxi/microtar.h"
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
-
struct cmd_struct *commands = NULL;
-static void print_global_usage(void) {
- printf("Usage: dep [global options] <command> [command options]\n");
- printf("\n");
- printf("Global options:\n");
- printf(" n/a\n");
- printf("\n");
- printf("Commands:\n");
-
- struct cmd_struct *cmd = commands;
- while (cmd) {
- printf(" %-16s %s\n", cmd->display ? cmd->display : cmd->name[0], cmd->description ? cmd->description : "");
- cmd = cmd->next;
- }
+int main(int argc, const char **argv) {
+ int show_version = 0;
- printf("\n");
- printf("Help topics:\n");
- printf(" global This help text\n");
+ argc = dep_global_options(argc, argv, &show_version);
- cmd = commands;
- while (cmd) {
- printf(" %-16s More detailed explanation on the %s command\n", cmd->name[0], cmd->name[0]);
- cmd = cmd->next;
+ /* --version is nothing but another way of calling the version command */
+ if (show_version) {
+ static const char *version_argv[] = {"version", NULL};
+ argv = version_argv;
+ argc = 1;
}
-}
-
-static const char *const usages[] = {
- "dep [global options] <command> [command options]",
- NULL,
-};
-int main(int argc, const char **argv) {
- struct argparse argparse;
- struct argparse_option options[] = {
- OPT_HELP(),
- OPT_END(),
- };
- argparse_init(&argparse, options, usages, ARGPARSE_STOP_AT_NON_OPTION);
- argc = argparse_parse(&argparse, argc, argv);
if (argc < 1) {
- print_global_usage();
+ dep_print_global_usage();
return 0;
}