~sircmpwn/hare-dev

hare: cmd/hare: Print dependencies to a t v1 REJECTED

Dridi Boukelmoune: 1
 cmd/hare: Print dependencies to a t

 3 files changed, 25 insertions(+), 4 deletions(-)
#988824 alpine.yml success
#988825 freebsd.yml success
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~sircmpwn/hare-dev/patches/41087/mbox | git am -3
Learn more about email & git

[PATCH hare] cmd/hare: Print dependencies to a t Export this patch

The new hare deps -t option changes the output to a plain text one:

   mod child1
   mod child2
   mod ...
Unlike other output formats, it is not recursive by default, so a
new -r option allows dependencies to also have their dependencies
listed:

   child1::sub other
   child1 child1::sub
   mod child1
   mod child2
   mod ...
The preexisting output formats remain recursive only, leaving the
decision whether or not to change the current behavior outside the
scope of this change.

This format can easily be consumed by field-based programs such as
tsort(1) (to a t!) or the more frequent utilities like sh(1) (while
read loops) or awk(1).

A familiar example:

    hare deps -rt crypto |
    awk '
        BEGIN { print "strict digraph deps {" }
        { print "\t\"" $1 "\" -> \"" $2 "\";" }
        END { print "}" }
    ' |
    dot -T svg >crypto.svg

The motivation is to enable module processing in package managers,
in the non-recursive mode, to derive virtual provides from the first
field and virtual requires from the second one.
Signed-off-by: Dridi Boukelmoune <dridi.boukelmoune@gmail.com>
---
 cmd/hare/deps.ha    | 10 +++++++---
 cmd/hare/main.ha    |  2 ++
 cmd/hare/subcmds.ha | 17 ++++++++++++++++-
 3 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/cmd/hare/deps.ha b/cmd/hare/deps.ha
index f54c9d1e..b29fef43 100644
--- a/cmd/hare/deps.ha
+++ b/cmd/hare/deps.ha
@@ -16,7 +16,8 @@ type depnode = struct {
type dep_cycle = !size;

// depth-first initial exploration, cycle-detection, reverse topological sort
fn explore_deps(ctx: *module::context, stack: *[]str, visited: *[]depnode, ident: str) (size | dep_cycle) = {
fn explore_deps(ctx: *module::context, stack: *[]str, visited: *[]depnode,
		ident: str, recursive: bool) (size | dep_cycle) = {
	// check for cycles
	for (let i = 0z; i < len(stack); i += 1) {
		if (ident == stack[i]) {
@@ -41,8 +42,11 @@ fn explore_deps(ctx: *module::context, stack: *[]str, visited: *[]depnode, ident
	for (let i = 0z; i < len(ver.depends); i += 1) {
		const name = strings::join("::", ver.depends[i]...);
		defer free(name);
		const child = explore_deps(ctx, stack, visited, name)?;
		append(this.depends, child);
		if (recursive) {
			const child = explore_deps(ctx, stack, visited, name,
				true)?;
			append(this.depends, child);
		};
	};
	// reverse-sort depends so that we know the last in the list is the
	// "final" child during show_deps
diff --git a/cmd/hare/main.ha b/cmd/hare/main.ha
index 0164779e..d9140135 100644
--- a/cmd/hare/main.ha
+++ b/cmd/hare/main.ha
@@ -37,6 +37,8 @@ const help: []getopt::help = [
		"prints dependency information for a Hare program",
		('d', "print dot syntax for use with graphviz"),
		('M', "build-dir", "print rules for POSIX make"),
		('r', "print a recursive text output"),
		('t', "print text output for field-based parsing"),
		('T', "tags...", "set build tags"),
		('X', "tags...", "unset build tags"),
		"<path|module>",
diff --git a/cmd/hare/subcmds.ha b/cmd/hare/subcmds.ha
index 7b672765..7ef2849c 100644
--- a/cmd/hare/subcmds.ha
+++ b/cmd/hare/subcmds.ha
@@ -194,6 +194,7 @@ type deps_goal = enum {
	DOT,
	MAKE,
	TERM,
	TEXT,
};

fn deps(cmd: *getopt::command) void = {
@@ -203,6 +204,7 @@ fn deps(cmd: *getopt::command) void = {

	let build_dir: str = "";
	let goal = deps_goal::TERM;
	let text_recursive = false;
	for (let i = 0z; i < len(cmd.opts); i += 1) {
		let opt = cmd.opts[i];
		switch (opt.0) {
@@ -211,6 +213,10 @@ fn deps(cmd: *getopt::command) void = {
		case 'M' =>
			goal = deps_goal::MAKE;
			build_dir = opt.1;
		case 'r' =>
			text_recursive = true;
		case 't' =>
			goal = deps_goal::TEXT;
		case 'T' =>
			tags = match (addtags(tags, opt.1)) {
			case void =>
@@ -270,7 +276,9 @@ fn deps(cmd: *getopt::command) void = {
	for (let i = 0z; i < len(ver.depends); i += 1) {
		const name = strings::join("::", ver.depends[i]...);
		defer free(name);
		const child = match (explore_deps(&ctx, &stack, &visited, name)) {
		let recursive = goal != deps_goal::TEXT || text_recursive;
		const child = match (explore_deps(&ctx, &stack, &visited, name,
				recursive)) {
		case let index: size => yield index;
		case let start: dep_cycle =>
			const chain = strings::join(" -> ", stack[start..]...);
@@ -300,6 +308,13 @@ fn deps(cmd: *getopt::command) void = {
			};
		};
		fmt::println("}")!;
	case deps_goal::TEXT =>
		for (let i = 0z; i < len(visited); i += 1) {
			for (let j = 0z; j < len(visited[i].depends); j += 1) {
				const child = visited[visited[i].depends[j]];
				fmt::printfln("{} {}", visited[i].ident, child.ident)!;
			};
		};
	case deps_goal::MAKE =>
		abort("-M option not implemented yet");
	};
-- 
2.40.1
hare/patches: SUCCESS in 1m40s

[cmd/hare: Print dependencies to a t][0] from [Dridi Boukelmoune][1]

[0]: https://lists.sr.ht/~sircmpwn/hare-dev/patches/41087
[1]: mailto:dridi.boukelmoune@gmail.com

✓ #988824 SUCCESS hare/patches/alpine.yml  https://builds.sr.ht/~sircmpwn/job/988824
✓ #988825 SUCCESS hare/patches/freebsd.yml https://builds.sr.ht/~sircmpwn/job/988825
here: https://git-send-email.io/#step-4

side note: if you print it out right, the tsort shouldn't be necessary, 
since the exploration function sorts in reverse topological order anyway 
(so you can print in topological order by iterating in reverse).

additionaly: beware that the -T option (build tags) has the possibility 
to alter the list of dependencies that are printed (because some modules 
only rely on others for testing, or under libc, etc. so you probably 
actually want to create a completely custom function if you would like 
to know an exhaustive list of modules that a certain module may depend 
on. suggestions for this in a moment...