version 1.95, 2020/09/15 07:18:45 |
version 1.96, 2023/06/08 08:55:27 |
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
|
use strict; |
use v5.36; |
use warnings; |
|
use Config; |
use Config; |
use Getopt::Long; |
use Getopt::Long; |
use File::Basename; |
use File::Basename; |
use File::stat; |
use File::stat; |
use OpenBSD::PkgConfig; |
use OpenBSD::PkgConfig; |
|
|
|
use constant { |
|
ONLY_I => 1, |
|
ONLY_l => 2, |
|
ONLY_L => 4, |
|
ONLY_OTHER => 8 |
|
}; |
|
|
my @PKGPATH = qw(/usr/lib/pkgconfig |
my @PKGPATH = qw(/usr/lib/pkgconfig |
/usr/local/lib/pkgconfig |
/usr/local/lib/pkgconfig |
/usr/local/share/pkgconfig |
/usr/local/share/pkgconfig |
|
|
|
|
if ($logfile) { |
if ($logfile) { |
open my $L, ">>" , $logfile or die; |
open my $L, ">>" , $logfile or die; |
print $L beautify_list($0, @ARGV), "\n"; |
say $L beautify_list($0, @ARGV); |
close $L; |
close $L; |
} |
} |
|
|
|
|
'help' => \&help, #does not return |
'help' => \&help, #does not return |
'usage' => \&help, #does not return |
'usage' => \&help, #does not return |
'list-all' => \$mode{list}, |
'list-all' => \$mode{list}, |
'version' => sub { print "$version\n" ; exit(0);} , |
'version' => sub { say $version ; exit(0);} , |
'errors-to-stdout' => sub { $mode{estdout} = 1}, |
'errors-to-stdout' => sub { $mode{estdout} = 1}, |
'print-errors' => sub { $mode{printerr} = 1}, |
'print-errors' => sub { $mode{printerr} = 1}, |
'silence-errors' => sub { $mode{printerr} = 0}, |
'silence-errors' => sub { $mode{printerr} = 0}, |
|
|
'print-requires' => \$mode{printrequires}, |
'print-requires' => \$mode{printrequires}, |
'print-requires-private' => \$mode{printrequiresprivate}, |
'print-requires-private' => \$mode{printrequiresprivate}, |
|
|
'cflags' => sub { $mode{cflags} = 3}, |
'cflags' => sub { $mode{cflags} = ONLY_I|ONLY_OTHER}, |
'cflags-only-I' => sub { $mode{cflags} |= 1}, |
'cflags-only-I' => sub { $mode{cflags} |= ONLY_I}, |
'cflags-only-other' => sub { $mode{cflags} |= 2}, |
'cflags-only-other' => sub { $mode{cflags} |= ONLY_OTHER}, |
'libs' => sub { $mode{libs} = 7}, |
'libs' => sub { $mode{libs} = ONLY_L|ONLY_l|ONLY_OTHER}, |
'libs-only-l' => sub { $mode{libs} |= 1}, |
'libs-only-l' => sub { $mode{libs} |= ONLY_l}, |
'libs-only-L' => sub { $mode{libs} |= 2}, |
'libs-only-L' => sub { $mode{libs} |= ONLY_L}, |
'libs-only-other' => sub { $mode{libs} |= 4}, |
'libs-only-other' => sub { $mode{libs} |= ONLY_OTHER}, |
'exists' => sub { $mode{exists} = 1} , |
'exists' => sub { $mode{exists} = 1} , |
'validate' => sub { $mode{validate} = 1}, |
'validate' => sub { $mode{validate} = 1}, |
'static' => sub { $mode{static} = 1}, |
'static' => sub { $mode{static} = 1}, |
|
|
|
|
# When we got here we're supposed to have had at least one |
# When we got here we're supposed to have had at least one |
# package as argument. |
# package as argument. |
if (!@ARGV){ |
if (!@ARGV) { |
say_error("No package name(s) specified."); |
say_error("No package name(s) specified."); |
exit 1; |
exit 1; |
} |
} |
|
|
# Return the next module from @ARGV, if it turns out to be a comma separated |
# Return the next module from @ARGV, if it turns out to be a comma separated |
# module list, take the first one and put the rest back to the front. |
# module list, take the first one and put the rest back to the front. |
sub get_next_module |
sub get_next_module() |
{ |
{ |
my $module = shift @ARGV; |
my $module = shift @ARGV; |
my $m; |
my $m; |
if ($module =~ m/,/) { |
if ($module =~ m/,/) { |
my @ms = split(/,/, $module); |
my @ms = split(/,/, $module); |
$m = shift @ms; |
$m = shift @ms; |
unshift(@ARGV, @ms) if (scalar(@ms) > 0); |
unshift(@ARGV, @ms) if @ms != 0; |
} else { |
} else { |
return $module; |
return $module; |
} |
} |
|
|
return $m; |
return $m; |
} |
} |
|
|
while (@ARGV){ |
while (@ARGV) { |
my $p = get_next_module(); |
my $p = get_next_module(); |
my $op = undef; |
my $op = undef; |
my $v = undef; |
my $v = undef; |
|
|
if ($mode{cflags} || $mode{libs} || $mode{variable}) { |
if ($mode{cflags} || $mode{libs} || $mode{variable}) { |
push @vlist, do_cflags($dep_cfg_list) if $mode{cflags}; |
push @vlist, do_cflags($dep_cfg_list) if $mode{cflags}; |
push @vlist, do_libs($dep_cfg_list) if $mode{libs}; |
push @vlist, do_libs($dep_cfg_list) if $mode{libs}; |
print join(' ', @vlist), "\n" if $rc == 0; |
say join(' ', @vlist) if $rc == 0; |
} |
} |
|
|
exit $rc; |
exit $rc; |
|
|
########################################################################### |
########################################################################### |
|
|
sub handle_config |
sub handle_config($p, $op, $v, $list) |
{ |
{ |
my ($p, $op, $v, $list) = @_; |
|
my $cfg = cache_find_config($p); |
my $cfg = cache_find_config($p); |
|
|
unshift @$list, $p if defined $cfg; |
unshift @$list, $p if defined $cfg; |
|
|
} |
} |
} |
} |
|
|
my $get_props = sub { |
my $get_props = sub($property) { |
my $property = shift; |
|
my $pkg; |
my $pkg; |
|
|
# See if there's anything in the environment that we need to |
# See if there's anything in the environment that we need to |
|
|
my $deps = $cfg->get_property($property, $variables); |
my $deps = $cfg->get_property($property, $variables); |
return unless defined $deps; |
return unless defined $deps; |
for my $dep (@$deps) { |
for my $dep (@$deps) { |
if ($dep =~ m/^(.*?)\s*([<=>]+)\s*([\d\.]+|[\d\.]+[\w]*[\d]+)$/) { |
if ($dep =~ m/^(.*?)\s*([<=>]+)\s*([\d\.]+|[\d\.]+\w*\d+)$/) { |
handle_config($1, $2, $3, $list); |
handle_config($1, $2, $3, $list); |
} else { |
} else { |
handle_config($dep, undef, undef, $list); |
handle_config($dep, undef, undef, $list); |
|
|
|
|
# look for the .pc file in each of the PKGPATH elements. Return the path or |
# look for the .pc file in each of the PKGPATH elements. Return the path or |
# undef if it's not there |
# undef if it's not there |
sub pathresolve |
sub pathresolve($p) |
{ |
{ |
my ($p) = @_; |
|
|
|
if ($allow_uninstalled && $p !~ m/\-uninstalled$/) { |
if ($allow_uninstalled && $p !~ m/\-uninstalled$/) { |
for my $d (@PKGPATH) { |
for my $d (@PKGPATH) { |
my $f = "$d/$p-uninstalled.pc"; |
my $f = "$d/$p-uninstalled.pc"; |
|
|
return undef; |
return undef; |
} |
} |
|
|
sub get_config |
sub get_config($f) |
{ |
{ |
my ($f) = @_; |
|
|
|
my $cfg; |
my $cfg; |
eval { |
eval { |
$cfg = OpenBSD::PkgConfig->read_file($f); |
$cfg = OpenBSD::PkgConfig->read_file($f); |
}; |
}; |
if (!$@) { |
if (!$@) { |
return validate_config($f, $cfg); |
return validate_config($f, $cfg); |
|
|
return undef; |
return undef; |
} |
} |
|
|
sub cache_find_config |
sub cache_find_config($name) |
{ |
{ |
my $name = shift; |
|
|
|
say_debug("processing $name"); |
say_debug("processing $name"); |
|
|
if (exists $configs{$name}) { |
if (exists $configs{$name}) { |
|
|
} |
} |
|
|
# Required elements for a valid .pc file: Name, Description, Version |
# Required elements for a valid .pc file: Name, Description, Version |
sub validate_config |
sub validate_config($f, $cfg) |
{ |
{ |
my ($f, $cfg) = @_; |
|
my @required_elems = ('Name', 'Description', 'Version'); |
my @required_elems = ('Name', 'Description', 'Version'); |
|
|
# Check if we're dealing with an empty file, but don't error out just |
# Check if we're dealing with an empty file, but don't error out just |
|
|
|
|
# pkg-config won't install a pkg-config.pc file itself, but it may be |
# pkg-config won't install a pkg-config.pc file itself, but it may be |
# listed as a dependency in other files. so prime the cache with self. |
# listed as a dependency in other files. so prime the cache with self. |
sub setup_self |
sub setup_self() |
{ |
{ |
my $pkg_pc = OpenBSD::PkgConfig->new; |
my $pkg_pc = OpenBSD::PkgConfig->new; |
$pkg_pc->add_property('Version', $version); |
$pkg_pc->add_property('Version', $version); |
|
|
$configs{'pkg-config'} = $pkg_pc; |
$configs{'pkg-config'} = $pkg_pc; |
} |
} |
|
|
sub find_config |
sub find_config($p) |
{ |
{ |
my ($p) = @_; |
|
|
|
# Differentiate between getting a full path and just the module name. |
# Differentiate between getting a full path and just the module name. |
my $f = ($p =~ m/\.pc$/ ? $p : pathresolve($p)); |
my $f = ($p =~ m/\.pc$/ ? $p : pathresolve($p)); |
|
|
|
|
return undef; |
return undef; |
} |
} |
|
|
sub stringize |
sub stringize($list, $sep = ',') |
{ |
{ |
my $list = shift; |
|
my $sep = shift || ','; |
|
|
|
if (defined $list) { |
if (defined $list) { |
return join($sep, @$list) |
return join($sep, @$list) |
} else { |
} else { |
|
|
} |
} |
|
|
#if the variable option is set, pull out the named variable |
#if the variable option is set, pull out the named variable |
sub do_variable |
sub do_variable($p, $v) |
{ |
{ |
my ($p, $v) = @_; |
|
|
|
my $cfg = cache_find_config($p); |
my $cfg = cache_find_config($p); |
|
|
if (defined $cfg) { |
if (defined $cfg) { |
|
|
|
|
#if the modversion or print-provides options are set, |
#if the modversion or print-provides options are set, |
#pull out the compiler flags |
#pull out the compiler flags |
sub do_modversion |
sub do_modversion($p) |
{ |
{ |
my ($p) = @_; |
|
|
|
my $cfg = cache_find_config($p); |
my $cfg = cache_find_config($p); |
|
|
if (defined $cfg) { |
if (defined $cfg) { |
my $value = $cfg->get_property('Version', $variables); |
my $value = $cfg->get_property('Version', $variables); |
if (defined $value) { |
if (defined $value) { |
if (defined($mode{printprovides})){ |
if (defined($mode{printprovides})){ |
print "$p = " . stringize($value) . "\n"; |
say "$p = " , stringize($value); |
return undef; |
return undef; |
} else { |
} else { |
print stringize($value), "\n"; |
say stringize($value); |
return undef; |
return undef; |
} |
} |
} |
} |
|
|
} |
} |
|
|
#if the cflags option is set, pull out the compiler flags |
#if the cflags option is set, pull out the compiler flags |
sub do_cflags |
sub do_cflags($list) |
{ |
{ |
my $list = shift; |
|
|
|
my $cflags = []; |
my $cflags = []; |
|
|
for my $pkg (@$list) { |
for my $pkg (@$list) { |
my $l = $configs{$pkg}->get_property('Cflags', $variables); |
my $l = $configs{$pkg}->get_property('Cflags', $variables); |
PATH: for my $path (@$l) { |
PATH: for my $path (@$l) { |
for my $sys_path (@sys_includes) { |
for my $sys_path (@sys_includes) { |
next PATH if ($path =~ /${sys_path}\/*$/); |
next PATH if $path =~ /\Q${sys_path}\E\/*$/; |
} |
} |
push(@$cflags, $path); |
push(@$cflags, $path); |
} |
} |
} |
} |
my $a = OpenBSD::PkgConfig->compress($cflags, |
my $a = OpenBSD::PkgConfig->compress($cflags, |
sub { |
sub($r) { |
local $_ = shift; |
if (($mode{cflags} & ONLY_I) && $r =~ /^-I/ || |
if (($mode{cflags} & 1) && /^-I/ || |
($mode{cflags} & ONLY_OTHER) && $r !~ /^-I/) { |
($mode{cflags} & 2) && !/^-I/) { |
|
return 1; |
return 1; |
} else { |
} else { |
return 0; |
return 0; |
} |
} |
}); |
}); |
if (defined($a) && defined($variables->{pc_sysrootdir})){ |
if (defined($variables->{pc_sysrootdir})){ |
$a =~ s/[\w]?-I/$&$variables->{pc_sysrootdir}/g; |
$a =~ s/[\w]?-I/$&$variables->{pc_sysrootdir}/g; |
} |
} |
|
|
|
|
} |
} |
|
|
#if the lib option is set, pull out the linker flags |
#if the lib option is set, pull out the linker flags |
sub do_libs |
sub do_libs($list) |
{ |
{ |
my $list = shift; |
|
|
|
my $libs = []; |
my $libs = []; |
|
|
# In static mode, we have to make sure we discover the libs in dependency |
# In static mode, we have to make sure we discover the libs in dependency |
|
|
|
|
# Get the linker path directives (-L) and store it in $a. |
# Get the linker path directives (-L) and store it in $a. |
# $b will be the actual libraries. |
# $b will be the actual libraries. |
my $a = OpenBSD::PkgConfig->compress($libs, |
my $r = OpenBSD::PkgConfig->compress_list($libs, |
sub { |
sub($r) { |
local $_ = shift; |
if (($mode{libs} & ONLY_L) && $r =~ /^-L/ || |
if (($mode{libs} & 2) && /^-L/ || |
($mode{libs} & ONLY_OTHER) && $r !~ /^-[lL]/) { |
($mode{libs} & 4) && !/^-[lL]/) { |
|
return 1; |
return 1; |
} else { |
} else { |
return 0; |
return 0; |
|
|
}); |
}); |
|
|
if (defined($variables->{pc_sysrootdir})){ |
if (defined($variables->{pc_sysrootdir})){ |
$a =~ s/[\w]?-[lL]/$&$variables->{pc_sysrootdir}/g; |
for my $i (@$r) { |
|
$i =~ s/[\w]?-[lL]/$&$variables->{pc_sysrootdir}/; |
|
} |
} |
} |
|
|
if ($mode{libs} & 1) { |
if ($mode{libs} & ONLY_l) { |
my $b = OpenBSD::PkgConfig->rcompress($libs, |
push(@$r, OpenBSD::PkgConfig->rcompress($libs, |
sub { shift =~ m/^-l/; }); |
sub($l) { $l =~ m/^-l/; })); |
return ($a, $b); |
|
} else { |
|
return $a; |
|
} |
} |
|
return @$r; |
} |
} |
|
|
#list all packages |
#list all packages |
sub do_list |
sub do_list() |
{ |
{ |
my ($p, $x, $y, @files, $fname, $name); |
my ($p, $x, $y, @files, $fname, $name); |
|
|
my $error = 0; |
my $error = 0; |
|
|
for my $p (@PKGPATH) { |
for my $p (@PKGPATH) { |
|
|
return $error; |
return $error; |
} |
} |
|
|
sub help |
sub help(@) |
{ |
{ |
print <<EOF |
print <<EOF |
Usage: $0 [options] |
Usage: $0 [options] |
|
|
} |
} |
|
|
# do we meet/beat the version the caller requested? |
# do we meet/beat the version the caller requested? |
sub self_version |
sub self_version($v) |
{ |
{ |
my ($v) = @_; |
|
my (@a, @b); |
my (@a, @b); |
|
|
@a = split(/\./, $v); |
@a = split(/\./, $v); |
|
|
} |
} |
} |
} |
|
|
sub compare |
sub parse_suffix($s) |
{ |
{ |
my ($a, $b) = @_; |
my @l = (); |
my ($full_a, $full_b) = ($a, $b); |
my $full = $s; |
my (@suffix_a, @suffix_b); |
|
|
|
return 0 if ($a eq $b); |
|
|
|
# is there a valid non-numeric suffix to deal with later? |
# is there a valid non-numeric suffix to deal with later? |
# accepted are (in order): a(lpha) < b(eta) < rc < ' '. |
# accepted are (in order): a(lpha) < b(eta) < rc < ' '. |
# suffix[0] is the 'alpha' part, suffix[1] is the '1' part in 'alpha1'. |
# suffix[0] is the 'alpha' part, suffix[1] is the '1' part in 'alpha1'. |
if ($a =~ s/(rc|beta|b|alpha|a)(\d+)$//) { |
if ($s =~ s/(rc|beta|b|alpha|a)(\d+)$//) { |
say_debug("valid suffix $1$2 found in $a$1$2."); |
@l = ($1, $2); |
$suffix_a[0] = $1; |
|
$suffix_a[1] = $2; |
|
} |
} |
|
# also deal with -stable extension |
if ($b =~ s/(rc|beta|b|alpha|a)(\d+)$//) { |
elsif ($s =~ s/(\-stable)$//) { |
say_debug("valid suffix $1$2 found in $b$1$2."); |
@l = ($1); |
$suffix_b[0] = $1; |
|
$suffix_b[1] = $2; |
|
} |
} |
|
|
# The above are standard suffixes; deal with single alphabetical |
# The above are standard suffixes; deal with single alphabetical |
# suffixes too, e.g. 1.0.1h |
# suffixes too, e.g. 1.0.1h |
if ($a =~ s/([a-zA-Z]){1}$//) { |
elsif ($s =~ s/([a-zA-Z]){1}$//) { |
say_debug("valid suffix $1 found in $a$1."); |
@l = ($1); |
$suffix_a[0] = $1; |
|
} |
} |
|
|
if ($b =~ s/([a-zA-Z]){1}$//) { |
if (@l) { |
say_debug("valid suffix $1 found in $b$1."); |
say_debug("valid suffix @l found in $full."); |
$suffix_b[0] = $1; |
} |
} |
|
|
|
|
return ($s, @l); |
|
} |
|
|
|
sub compare($full_a, $full_b) |
|
{ |
|
return 0 if $full_a eq $full_b; |
|
|
|
my ($a, @suffix_a) = parse_suffix($full_a); |
|
my ($b, @suffix_b) = parse_suffix($full_b); |
|
|
my @a = split(/\./, $a); |
my @a = split(/\./, $a); |
my @b = split(/\./, $b); |
my @b = split(/\./, $b); |
|
|
|
|
# directly compare suffixes, provided both suffixes |
# directly compare suffixes, provided both suffixes |
# are present. |
# are present. |
if (@suffix_a && @suffix_b) { |
if (@suffix_a && @suffix_b) { |
my $first_char = sub { |
my $first_char = sub($s) { |
return substr(shift, 0, 1); |
return substr($s, 0, 1); |
}; |
}; |
|
|
# suffixes are equal, compare on numeric |
# suffixes are equal, compare on numeric |
|
|
} |
} |
|
|
# simple numeric comparison, with optional equality test. |
# simple numeric comparison, with optional equality test. |
sub compare_numeric |
sub compare_numeric($x, $y, $eq) |
{ |
{ |
my ($x, $y, $eq) = @_; |
|
|
|
return 1 if $x > $y; |
return 1 if $x > $y; |
return -1 if $x < $y; |
return -1 if $x < $y; |
return 0 if (($x == $y) and ($eq == 1)); |
return 0 if (($x == $y) and ($eq == 1)); |
|
|
} |
} |
|
|
# got a package meeting the requested specific version? |
# got a package meeting the requested specific version? |
sub versionmatch |
sub versionmatch($cfg, $op, $want) |
{ |
{ |
my ($cfg, $op, $want) = @_; |
|
|
|
# can't possibly match if we can't find the file |
# can't possibly match if we can't find the file |
return 0 if !defined $cfg; |
return 0 if !defined $cfg; |
|
|
|
|
elsif ($op eq '<=') { return $value <= 0; } |
elsif ($op eq '<=') { return $value <= 0; } |
} |
} |
|
|
sub mismatch |
sub mismatch($p, $cfg, $op, $v) |
{ |
{ |
my ($p, $cfg, $op, $v) = @_; |
|
my $name = stringize($cfg->get_property('Name'), ' '); |
my $name = stringize($cfg->get_property('Name'), ' '); |
my $version = stringize($cfg->get_property('Version')); |
my $version = stringize($cfg->get_property('Version')); |
my $url = stringize($cfg->get_property('URL')); |
my $url = stringize($cfg->get_property('URL')); |
|
|
say_warning("You may find new versions of $name at $url") if $url; |
say_warning("You may find new versions of $name at $url") if $url; |
} |
} |
|
|
sub simplify_and_reverse |
sub simplify_and_reverse($reqlist) |
{ |
{ |
my $reqlist = shift; |
|
my $dejavu = {}; |
my $dejavu = {}; |
my $result = []; |
my $result = []; |
|
|
|
|
} |
} |
|
|
# retrieve and print Requires(.private) |
# retrieve and print Requires(.private) |
sub print_requires |
sub print_requires($p) |
{ |
{ |
my ($p) = @_; |
|
|
|
my $cfg = cache_find_config($p); |
my $cfg = cache_find_config($p); |
|
|
if (defined($cfg)) { |
if (defined($cfg)) { |
|
|
} |
} |
|
|
if (defined($value)) { |
if (defined($value)) { |
print "$_\n" for @$value; |
say $_ for @$value; |
return undef; |
return undef; |
} |
} |
} |
} |
|
|
$rc = 1; |
$rc = 1; |
} |
} |
|
|
sub beautify_list |
sub beautify_list(@p) |
{ |
{ |
return join(' ', map {"[$_]"} @_); |
return join(' ', map {"[$_]"} @p); |
} |
} |
|
|
sub say_debug |
sub say_debug($msg) |
{ |
{ |
say_msg(shift) if $mode{debug}; |
say_msg($msg) if $mode{debug}; |
} |
} |
|
|
sub say_error |
sub say_error($msg) |
{ |
{ |
say_msg(shift) if $mode{printerr} |
say_msg($msg) if $mode{printerr} |
} |
} |
|
|
sub say_warning |
sub say_warning($msg) |
{ |
{ |
say_msg(shift); |
say_msg($msg); |
} |
} |
|
|
sub say_msg |
sub say_msg($str) |
{ |
{ |
my $str = shift; |
|
|
|
# If --errors-to-stdout was given, close STDERR (to be safe), |
# If --errors-to-stdout was given, close STDERR (to be safe), |
# then dup the output to STDOUT and delete the key from %mode so we |
# then dup the output to STDOUT and delete the key from %mode so we |
# won't keep checking it. STDERR stays dup'ed. |
# won't keep checking it. STDERR stays dup'ed. |
|
|
delete($mode{estdout}); |
delete($mode{estdout}); |
} |
} |
|
|
print STDERR $str, "\n"; |
say STDERR $str; |
} |
} |