do not edit — generated by btf.
git.druid.rocksindexdruid520mpplugins/depcheck.pl

plugins/depcheck.pl


#!/usr/bin/env perl
use v5.16;
use strict;
use warnings FATAL => 'all';
use FindBin;
use lib "$FindBin::Bin";
use mplib::config qw($CFG);
use mplib::util qw(fail note slot_key bare_name strip_flag_value);
use mplib::db qw(read_db read_lines);
use mplib::version qw(parse_dep_token);
use mplib::resolve qw(init find_portdir pkgconf try_pkgconf effective_deps);
use mplib::portformat qw(resolve_slot);
 
# a real, post-build dependency sniffer: runs ldd on every ELF binary/
# library an ALREADY-INSTALLED port's own manifest lists, works out which
# installed package actually owns each linked .so (by checking every
# other package's own manifest for that path), and flags any owner that
# isn't covered by this port's declared pkg_deps -- this is the thing
# mplib::portlint's static check_missing_tool_deps CAN'T see (a real
# runtime link, not just a tool NAME appearing in add.sh's text), and
# would have caught tmux/libevent or postgresql's real library needs
# even if the port's own comments never mentioned the tool by name.
#
# requires the port to be genuinely installed right now (mp devtest
# --keep <port> first, if you just want to check one before removing it
# again).
#
# usage: mp depcheck <port> [--dir=<root>]
#   --dir  also search <root> (the same way CUSTOM_PORTS_DIR already is,
#          first, ahead of every configured repo) -- for reading the
#          port's own pkg_deps/pkg.conf; whether it's genuinely
#          installed is unrelated to --dir at all (that's the db, not
#          the port directory).
 
my $flags = mplib::config::parse_flags(\@ARGV);
mplib::config::load($flags);
my $dir = strip_flag_value(\@ARGV, 'dir');
$CFG->{CUSTOM_PORTS} = $dir if defined $dir;
fail("usage: mp depcheck <port> [--dir=<root>] (must be currently installed -- try: mp devtest --keep <port>)")
    unless defined $ARGV[0];
init();
 
my $tok = parse_dep_token($ARGV[0]);
my $portdir = find_portdir($tok->{name}, $tok->{slot});
fail("port $ARGV[0] not found") unless $portdir;
my $pc = pkgconf($portdir);
my $slot  = resolve_slot($pc, $pc->{pkg_name});
my $canon = slot_key($pc->{pkg_name}, $slot);
 
my @files = read_lines("$CFG->{MANDIR}/$canon");
fail("$canon has no manifest -- it isn't installed right now (mp devtest --keep $ARGV[0] first)")
    unless @files;
 
my $db = read_db();
my @deps = effective_deps($pc, $canon);
my %have_literal = map { $_ => 1 } @deps;
my @tag_deps = map { s/^%//r } grep { /^%/ } @deps;
 
# cache: canon -> pkg_tags of the port that provides it (only computed
# for owners actually encountered below, not the whole tree).
my %tags_of;
sub tags_of {
	my ($owner_canon) = @_;
	return $tags_of{$owner_canon} if exists $tags_of{$owner_canon};
	my $obase = bare_name($owner_canon);
	my $odir = find_portdir($obase);
	my $opc = $odir ? try_pkgconf($odir) : undef;
	return $tags_of{$owner_canon} = $opc ? { map { $_ => 1 } split ' ', ($opc->{pkg_tags} || '') } : {};
}
 
sub satisfied {
	my ($owner_canon) = @_;
	my $obase = bare_name($owner_canon);
	return 1 if $have_literal{$obase} || $have_literal{$owner_canon};
	my $otags = tags_of($owner_canon);
	return 1 if grep { $otags->{$_} } @tag_deps;
	return 0;
}
 
# which installed package owns this INSTPREFIX-relative path, by checking
# every other package's own manifest -- built lazily (only once, on first
# use) since depcheck usually only runs against one port at a time.
my %owner_of;
sub build_owner_index {
	for my $c (keys %$db)
	{
		next if $c eq $canon;
		for my $rel (read_lines("$CFG->{MANDIR}/$c"))
		{
			$owner_of{$rel} = $c;
		}
	}
	return;
}
build_owner_index();
 
my %flagged;
my $nchecked = 0;
for my $rel (@files)
{
	my $path = "$CFG->{INSTPREFIX}/$rel";
	next unless -f $path && !-l $path;
	# quick ELF sniff (first 4 bytes) instead of shelling out to file(1)
	# for every manifest entry -- most of a typical manifest is headers/
	# docs/man pages, not binaries.
	open(my $fh, '<:raw', $path) or next;
	my $magic;
	read($fh, $magic, 4);
	close($fh);
	next unless defined $magic && $magic eq "\x7fELF";
	$nchecked = $nchecked + 1;
	for my $line (`ldd \Q$path\E 2>/dev/null`)
	{
		next unless $line =~ m{=>\s+(\Q$CFG->{INSTPREFIX}\E/\S+)};
		my $libpath = $1;
		(my $rel_lib = $libpath) =~ s{^\Q$CFG->{INSTPREFIX}\E/}{};
		my $owner = $owner_of{$rel_lib};
		next unless defined $owner;
		next if satisfied($owner);
		push @{$flagged{$owner}}, "$rel -> $rel_lib";
	}
}
 
if(!$nchecked)
{
	print "no ELF binaries/libraries in ${canon}'s own manifest to check.\n";
	exit 0;
}
unless(%flagged)
{
	print "ok: every linked library $canon actually uses traces back to a declared dependency ($nchecked file(s) checked).\n";
	exit 0;
}
print "$canon links against packages not covered by its own pkg_deps:\n";
for my $owner (sort keys %flagged)
{
	print "  $owner (via " . join(', ', @{$flagged{$owner}}) . ")\n";
}
print "\nadd " . join(' or ', sort keys %flagged) . " to pkg_deps, or a %tag it provides, if this is a real runtime need.\n";
exit(1);
powered by btf.