Index | Thread | Search

From:
Marc Espie <marc.espie.openbsd@gmail.com>
Subject:
having fun with ports tree introspection
To:
ports@openbsd.org
Date:
Mon, 22 Jun 2026 18:59:09 +0200

Download raw body.

Thread
  • Marc Espie:

    having fun with ports tree introspection

I've been playing with three scripts for identifying variables in the ports
tree and locating their doc.

I've attached all 3.

Basically: I go
make show=.VARIABLES | lister >fullwords
to extract every relevant public variable name from the tree.

The second script, locatewords, will try to find all variables in manpages.

Coincidentally, it's great at finding badly annotated variables !

The third script, mgrep, looks in MAKEFILE_LIST for a given construct,
helping the user figure out where a variable is defined/used
#! /usr/bin/perl

use v5.36;
my %hash;
my ($pkgpath, $fullpkgpath);

while (<>) {
	chomp;
	if (m/^\=\=\=\>\s+(.*)/) {
		$fullpkgpath = $1;
		$pkgpath = $fullpkgpath;
		$pkgpath =~ s/,.*//;
		say STDERR $pkgpath;
		next;
	}
	for my $word (split /\s+/, $_) {
		next if $word =~ m/^\_/;
		next if $word eq "BULK_$pkgpath";
		next if $word eq "BULK_DO_$pkgpath";
		next if $word eq "BULK_TARGETS_$pkgpath";
		next if $word eq "CLEANDEPENDS_$pkgpath";
		next if $word eq "WRKOBJDIR_$pkgpath";
		next if $word eq "FAKEOBJDIR_$pkgpath";
		next if $word =~ m/^LIB.*_VERSION$/;

		for my $w (qw(COMMENT PKG_ARCH PERMIT_PACKAGE PKGFILE
		    PKGNAME PKGSTEM FULLPKGNAME REVISION EPOCH FULLPKGPATH
		    RUN_DEPENDS WANTLIB LIB_DEPENDS IGNORE ONLY_FOR_ARCHS
		    NOT_FOR_ARCHS PKG_ARGS PREFIX CATEGORIES MESSAGE
		    UNMESSAGE DESCR PLIST STATIC_PLIST PKGSPEC SUBST_CMD)) {
		    	$word =~ s/^(\Q$w\E)\-.*/$1/;
		}
		for my $w (qw(SITES DISTFILES SUPDISTFILES PATCHFILES
		    TEMPLATE_DISTFILES TEMPLATE_HOMEPAGE)) {
		    	$word =~ s/^(\Q$w\E)\..*/$1/;
		}
		$hash{$word} //= $pkgpath;
	}
}

for my $w (sort keys %hash) {
	say "$w => $hash{$w}";
}
#! /usr/bin/perl

use v5.36;

my %keys;

sub find_manpage($man, $section)
{
	my $f;
	for my $d ("/usr/share/man/", "/usr/X11R6/man", "/usr/local/man") {
		for my $try (split(/,\s+/, $man)) {
			if (open($f, '<', "$d/man$section/$try.$section")) {
				return $f;
			}
		}
	}
	return undef;
}

sub cache_file($man, $section)
{
	my $f = find_manpage($man, $section);
	if (!defined $f) {
		say STDERR "Can't locate $man($section)";
		return;
	}
	my $h = {};
	while (<$f>) {
		chomp;
		if (m/^\.Ev\s+(.*)/) {
			for my $word (split(/\s+/, $1)) {
				$h->{$word} = 1;
			}
		} elsif (m/^\.It\s+Ev\s+(.*)/) {
			for my $word (split(/\s+/, $1)) {
				$h->{$word} = 1;
			}
		}
	}
	$keys{$man.$section} = $h;
	close($f);
}

sub contains($man, $section, $ev)
{
	if (!defined $keys{$man.$section}) {
		cache_file($man, $section);
	}
	if ($keys{$man.$section}{$ev}) {
		return 1;
	} else {
		return 0;
	}
}



open(my $g, '<', "fullwords") or die;
while (<$g>) {
	chomp;
	if (m/^(.*)(\s\=\>.*)/) {
		my ($var, $rest) = ($1, $2);
		my @mans;
		open(my $pipe, "man -k Ev=$var 2>/dev/null|");
		while (<$pipe>) {
			chomp;
			if (m/^(.*)\((\dp?)\)\s+\-\s+/) {
				my ($man, $section) = ($1, $2);
				if (contains($man, $section, $var)) {
					push(@mans, "$man($section)");
			    	}
			}
	    	}
		close($pipe);
		if (@mans) {
			say $var.' ('.join(', ', @mans).')'.$rest;
		} else {
			say $var.$rest;
		}
	}
}

#! /bin/sh
for i in `make show=MAKEFILE_LIST`
do
	grep -l "$1" $i
done