Mineplex/.FILES USED TO GET TO WHERE WE ARE PRESENTLY/xampp/perl/vendor/lib/PkgConfig.pm

1788 lines
47 KiB
Perl
Raw Normal View History

2023-05-17 21:44:01 +00:00
#!/usr/bin/perl
# lightweight no-dependency version of pkg-config. This will work on any machine
# with Perl installed.
# Copyright (C) 2012 M. Nunberg.
# You may use and distribute this software under the same terms and conditions
# as Perl itself.
package
PkgConfig::Vars;
# this is a namespace for .pc files to hold their variables without
# relying on lexical scope.
package
PkgConfig::UDefs;
# This namespace provides user-defined variables which are to override any
# declarations within the .pc file itself.
package PkgConfig;
#First two digits are Perl version, second two are pkg-config version
our $VERSION = '0.24026';
$VERSION =~ /([0-9]{2})$/;
my $compat_version = $1;
use strict;
use warnings;
use 5.006;
use Config;
use File::Spec;
use File::Glob 'bsd_glob';
use Class::Struct; #in core since 5.004
use Data::Dumper;
use File::Basename qw( dirname );
use Text::ParseWords qw( shellwords );
our $UseDebugging;
################################################################################
### Check for Log::Fu ###
################################################################################
BEGIN {
my $ret = eval q{
use Log::Fu 0.25 { level => "warn" };
1;
};
if(!$ret) {
my $log_base = sub {
my (@args) = @_;
print STDERR "[DEBUG] ", join(' ', @args);
print STDERR "\n";
};
*log_debug = *log_debugf = sub { return unless $UseDebugging; goto &$log_base };
*log_err = *log_errf = *log_warn = *log_warnf = *log_info = *log_infof =
$log_base;
}
}
our $VarClassSerial = 0;
################################################################################
### Sane Defaults ###
################################################################################
our @DEFAULT_SEARCH_PATH = qw(
/usr/local/lib/pkgconfig /usr/local/share/pkgconfig
/usr/lib/pkgconfig /usr/share/pkgconfig
);
our @DEFAULT_EXCLUDE_CFLAGS = qw(-I/usr/include -I/usr/local/include);
# don't include default link/search paths!
our @DEFAULT_EXCLUDE_LFLAGS = map { ( "-L$_", "-R$_" ) } qw( /lib /lib32 /lib64 /usr/lib /usr/lib32 /usr/lib/64 /usr/local/lib /usr/local/lib32 /usr/local/lib64 );
if($ENV{PKG_CONFIG_NO_OS_CUSTOMIZATION}) {
# use the defaults regardless of detected platform
} elsif($ENV{PKG_CONFIG_LIBDIR}) {
@DEFAULT_SEARCH_PATH = split $Config{path_sep}, $ENV{PKG_CONFIG_LIBDIR};
} elsif($^O eq 'msys') {
# MSYS2 seems to actually set PKG_CONFIG_PATH in its /etc/profile
# god bless it. But. The defaults if you unset the environment
# variable are different
@DEFAULT_SEARCH_PATH = qw(
/usr/lib/pkgconfig
/usr/share/pkgconfig
);
} elsif($^O eq 'solaris' && $Config{ptrsize} == 8) {
@DEFAULT_SEARCH_PATH = qw(
/usr/local/lib/64/pkgconfig /usr/local/share/pkgconfig
/usr/lib/64/pkgconfig /usr/share/pkgconfig
);
} elsif($^O eq 'linux' and -f '/etc/gentoo-release') {
# OK, we're running on Gentoo
# Fetch ptrsize value
my $ptrsize = $Config{ptrsize};
# Are we running on 64 bit system?
if ($ptrsize eq 8) {
# We do
@DEFAULT_SEARCH_PATH = qw!
/usr/lib64/pkgconfig/ /usr/share/pkgconfig/
!;
} else {
# We're running on a 32 bit system (hopefully)
@DEFAULT_SEARCH_PATH = qw!
/usr/lib/pkgconfig/ /usr/share/pkgconfig/
!;
}
} elsif($^O =~ /^(gnukfreebsd|linux)$/ && -r "/etc/debian_version") {
my $arch;
if(-x "/usr/bin/dpkg-architecture") {
# works if dpkg-dev is installed
# rt96694
($arch) = map { chomp; (split /=/)[1] }
grep /^DEB_HOST_MULTIARCH=/,
`/usr/bin/dpkg-architecture`;
} elsif(-x "/usr/bin/gcc") {
# works if gcc is installed
$arch = `/usr/bin/gcc -dumpmachine`;
chomp $arch;
} else {
my $deb_arch = `dpkg --print-architecture`;
if($deb_arch =~ /^amd64/) {
if($^O eq 'linux') {
$arch = 'x86_64-linux-gnu';
} elsif($^O eq 'gnukfreebsd') {
$arch = 'x86_64-kfreebsd-gnu';
}
} elsif($deb_arch =~ /^i386/) {
if($^O eq 'linux') {
$arch = 'i386-linux-gnu';
} elsif($^O eq 'gnukfreebsd') {
$arch = 'i386-kfreebsd-gnu';
}
}
}
if($arch) {
if(scalar grep /--print-foreign-architectures/, `dpkg --help`)
{
# multi arch support / Debian 7+
@DEFAULT_SEARCH_PATH = (
"/usr/local/lib/$arch/pkgconfig",
"/usr/local/lib/pkgconfig",
"/usr/local/share/pkgconfig",
"/usr/lib/$arch/pkgconfig",
"/usr/lib/pkgconfig",
"/usr/share/pkgconfig",
);
push @DEFAULT_EXCLUDE_LFLAGS, map { ("-L$_", "-R$_") }
"/usr/local/lib/$arch",
"/usr/lib/$arch";
} else {
@DEFAULT_SEARCH_PATH = (
"/usr/local/lib/pkgconfig",
"/usr/local/lib/pkgconfig/$arch",
"/usr/local/share/pkgconfig",
"/usr/lib/pkgconfig",
"/usr/lib/pkgconfig/$arch",
"/usr/share/pkgconfig",
);
}
} else {
@DEFAULT_SEARCH_PATH = (
"/usr/local/lib/pkgconfig",
"/usr/local/share/pkgconfig",
"/usr/lib/pkgconfig",
"/usr/share/pkgconfig",
);
}
} elsif($^O eq 'linux' && -r "/etc/redhat-release") {
if(-d "/usr/lib64/pkgconfig") {
@DEFAULT_SEARCH_PATH = qw(
/usr/lib64/pkgconfig
/usr/share/pkgconfig
);
} else {
@DEFAULT_SEARCH_PATH = qw(
/usr/lib/pkgconfig
/usr/share/pkgconfig
);
}
} elsif($^O eq 'linux' && -r "/etc/slackware-version") {
# Fetch ptrsize value
my $ptrsize = $Config{ptrsize};
# Are we running on 64 bit system?
if ($ptrsize == 8) {
# We do
@DEFAULT_SEARCH_PATH = qw!
/usr/lib64/pkgconfig/ /usr/share/pkgconfig/
!;
} else {
# We're running on a 32 bit system (hopefully)
@DEFAULT_SEARCH_PATH = qw!
/usr/lib/pkgconfig/ /usr/share/pkgconfig/
!;
}
} elsif($^O eq 'freebsd') {
# TODO: FreeBSD 10-12's version of pkg-config does not
# support PKG_CONFIG_DEBUG_SPEW so I can't verify
# the path there.
@DEFAULT_SEARCH_PATH = qw(
/usr/local/libdata/pkgconfig
/usr/libdata/pkgconfig
);
} elsif($^O eq 'netbsd') {
@DEFAULT_SEARCH_PATH = qw(
/usr/pkg/lib/pkgconfig
/usr/pkg/share/pkgconfig
/usr/X11R7/lib/pkgconfig
/usr/lib/pkgconfig
);
} elsif($^O eq 'openbsd') {
@DEFAULT_SEARCH_PATH = qw(
/usr/lib/pkgconfig
/usr/local/lib/pkgconfig
/usr/local/share/pkgconfig
/usr/X11R6/lib/pkgconfig
/usr/X11R6/share/pkgconfig
);
} elsif($^O eq 'MSWin32') {
# Caveats:
# 1. This pulls in Config,
# which we don't load on non MSWin32
# but it is in the core.
# 2. Slight semantic difference in that we are treating
# Strawberry as the "system" rather than Windows, but
# since pkg-config is rarely used in MSWin32, it is
# better to have something that is useful rather than
# worry about if it is exactly the same as other
# platforms.
# 3. It is a little brittle in that Strawberry might
# one day change its layouts. If it has and you are
# reading this, please send a pull request or simply
# let me know -plicease
require Config;
if($Config::Config{myuname} =~ /strawberry-perl/)
{
# handle PAR::Packer executables which have $^X eq "perl.exe"
if ($ENV{PAR_0})
{
my $path = $ENV{PAR_TEMP};
$path =~ s{\\}{/}g;
@DEFAULT_SEARCH_PATH = ($path);
}
else {
my($vol, $dir, $file) = File::Spec->splitpath($^X);
my @dirs = File::Spec->splitdir($dir);
splice @dirs, -3;
my $path = (File::Spec->catdir($vol, @dirs, qw( c lib pkgconfig )));
$path =~ s{\\}{/}g;
@DEFAULT_SEARCH_PATH = $path;
}
}
my @reg_paths;
eval q{
package
PkgConfig::WinReg;
use Win32API::Registry 0.21 qw( :ALL );
foreach my $top (HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER) {
my $key;
RegOpenKeyEx( $top, "Software\\\\pkgconfig\\\\PKG_CONFIG_PATH", 0, KEY_READ, $key) || next;
my $nlen = 1024;
my $pos = 0;
my $name = '';
while(RegEnumValue($key, $pos++, $name, $nlen, [], [], [], [])) {
my $type;
my $data;
RegQueryValueEx($key, $name, [], $type, $data, []);
push @reg_paths, $data;
}
RegCloseKey( $key );
}
};
unless($@) {
unshift @DEFAULT_SEARCH_PATH, @reg_paths;
}
if($Config::Config{cc} =~ /cl(\.exe)?$/i)
{
@DEFAULT_EXCLUDE_LFLAGS = ();
@DEFAULT_EXCLUDE_CFLAGS = ();
}
else
{
@DEFAULT_EXCLUDE_LFLAGS = (
"-L/mingw/lib",
"-R/mingw/lib",
"-L/mingw/lib/pkgconfig/../../lib",
"-R/mingw/lib/pkgconfig/../../lib",
);
@DEFAULT_EXCLUDE_CFLAGS = (
"-I/mingw/include",
"-I/mingw/lib/pkgconfig/../../include",
);
}
# See caveats above for Strawberry and PAR::Packer
require Config;
if(not $ENV{PAR_0} and $Config::Config{myuname} =~ /strawberry-perl/)
{
my($vol, $dir, $file) = File::Spec->splitpath($^X);
my @dirs = File::Spec->splitdir($dir);
splice @dirs, -3;
my $path = (File::Spec->catdir($vol, @dirs, qw( c )));
$path =~ s{\\}{/}g;
push @DEFAULT_EXCLUDE_LFLAGS, (
"-L$path/lib",
"-L$path/lib/pkgconfig/../../lib",
"-R$path/lib",
"-R$path/lib/pkgconfig/../../lib",
);
push @DEFAULT_EXCLUDE_CFLAGS, (
"-I$path/include",
"-I$path/lib/pkgconfig/../../include",
);
}
} elsif($^O eq 'darwin') {
if(-x '/usr/local/Homebrew/bin/brew') {
# Mac OS X with homebrew installed
push @DEFAULT_SEARCH_PATH,
bsd_glob '/usr/local/opt/*/lib/pkgconfig'
;
}
}
my @ENV_SEARCH_PATH = split($Config{path_sep}, $ENV{PKG_CONFIG_PATH} || "");
unshift @DEFAULT_SEARCH_PATH, @ENV_SEARCH_PATH;
if($^O eq 'MSWin32') {
@DEFAULT_SEARCH_PATH = map { s{\\}{/}g; $_ } map { /\s/ ? Win32::GetShortPathName($_) : $_ } @DEFAULT_SEARCH_PATH;
}
if($ENV{PKG_CONFIG_ALLOW_SYSTEM_CFLAGS}) {
@DEFAULT_EXCLUDE_CFLAGS = ();
}
if($ENV{PKG_CONFIG_ALLOW_SYSTEM_LIBS}) {
@DEFAULT_EXCLUDE_LFLAGS = ();
}
my $LD_OUTPUT_RE = qr/
SEARCH_DIR\("
([^"]+)
"\)
/x;
sub GuessPaths {
my $pkg = shift;
local $ENV{LD_LIBRARY_PATH} = "";
local $ENV{C_INCLUDE_PATH} = "";
local $ENV{LD_RUN_PATH} = "";
my $ld = $ENV{LD} || 'ld';
my $ld_output = qx(ld -verbose);
my @defl_search_dirs = ($ld_output =~ m/$LD_OUTPUT_RE/g);
@DEFAULT_EXCLUDE_LFLAGS = ();
foreach my $path (@defl_search_dirs) {
push @DEFAULT_EXCLUDE_LFLAGS, (map { "$_".$path }
(qw(-R -L -rpath= -rpath-link= -rpath -rpath-link)));
}
log_debug("Determined exclude LDFLAGS", @DEFAULT_EXCLUDE_LFLAGS);
#now get the include paths:
my @cpp_output = qx(cpp --verbose 2>&1 < /dev/null);
@cpp_output = map { chomp $_; $_ } @cpp_output;
#log_info(join("!", @cpp_output));
while (my $cpp_line = shift @cpp_output) {
chomp($cpp_line);
if($cpp_line =~ /\s*#include\s*<.+search starts here/) {
last;
}
}
#log_info(@cpp_output);
my @include_paths;
while (my $path = shift @cpp_output) {
if($path =~ /\s*End of search list/) {
last;
}
push @include_paths, $path;
}
@DEFAULT_EXCLUDE_CFLAGS = map { "-I$_" } @include_paths;
log_debug("Determine exclude CFLAGS", @DEFAULT_EXCLUDE_CFLAGS);
}
################################################################################
### Define our fields ###
################################################################################
struct(
__PACKAGE__,
[
# .pc search paths, defaults to PKG_CONFIG_PATH in environment
'search_path' => '@',
# whether to also spit out static dependencies
'static' => '$',
# whether we replace references to -L and friends with -Wl,-rpath, etc.
'rpath' => '$',
# build rpath-search,
# no recursion. set if we just want a version, or to see if the
# package exists.
'no_recurse' => '$',
#list of cflags and ldflags to exclude
'exclude_ldflags' => '@',
'exclude_cflags' => '@',
# what level of recursion we're at
'recursion' => '$',
# hash of libraries, keyed by recursion levels. Lower recursion numbers
# will be listed first
'libs_deplist' => '*%',
# cumulative cflags and ldflags
'ldflags' => '*@',
'cflags' => '*@',
# whether we print the c/ldflags
'print_cflags' => '$',
'print_ldflags' => '$',
# information about our top-level package
'pkg' => '$',
'pkg_exists' => '$',
'pkg_version' => '$',
'pkg_url', => '$',
'pkg_description' => '$',
'errmsg' => '$',
# classes used for storing persistent data
'varclass' => '$',
'udefclass' => '$',
'filevars' => '*%',
'uservars' => '*%',
# options for printing variables
'print_variables' => '$',
'print_variable' => '$',
'print_values' => '$',
'defined_variables' => '*%',
# for creating PkgConfig objects with identical
# settings
'original' => '$',
]
);
################################################################################
################################################################################
### Variable Storage ###
################################################################################
################################################################################
sub _get_pc_varname {
my ($self,$vname_base) = @_;
$self->varclass . "::" . $vname_base;
}
sub _get_pc_udefname {
my ($self,$vname_base) = @_;
$self->udefclass . "::" . $vname_base;
}
sub _pc_var {
my ($self,$vname) = @_;
$vname =~ s,\.,DOT,g;
no strict 'refs';
$vname = $self->_get_pc_varname($vname);
no warnings qw(once);
my $glob = *{$vname};
$glob ? $$glob : ();
}
sub _quote_cvt($) {
join ' ', map { s/(\s|"|')/\\$1/g; $_ } shellwords(shift)
}
sub assign_var {
my ($self,$field,$value) = @_;
no strict 'refs';
# if the user has provided a definition, use that.
if(exists ${$self->udefclass."::"}{$field}) {
log_debug("Prefix already defined by user");
return;
}
my $evalstr = sprintf('$%s = PkgConfig::_quote_cvt(%s)',
$self->_get_pc_varname($field), $value);
log_debug("EVAL", $evalstr);
do {
no warnings 'uninitialized';
eval $evalstr;
};
if($@) {
log_err($@);
}
}
sub prepare_vars {
my $self = shift;
my $varclass = $self->varclass;
no strict 'refs';
%{$varclass . "::"} = ();
while (my ($name,$glob) = each %{$self->udefclass."::"}) {
my $ref = *$glob{SCALAR};
next unless defined $ref;
${"$varclass\::$name"} = $$ref;
}
}
################################################################################
################################################################################
### Initializer ###
################################################################################
################################################################################
sub find {
my ($cls,$library,%options) = @_;
my @uspecs = (
['search_path', \@DEFAULT_SEARCH_PATH],
['exclude_ldflags', \@DEFAULT_EXCLUDE_LFLAGS],
['exclude_cflags', \@DEFAULT_EXCLUDE_CFLAGS]
);
my %original = %options;
foreach (@uspecs) {
my ($basekey,$default) = @$_;
my $list = [ @{$options{$basekey} ||= [] } ];
if($options{$basekey . "_override"}) {
@$list = @{ delete $options{$basekey."_override"} };
} else {
push @$list, @$default;
}
$options{$basekey} = $list;
#print "$basekey: " . Dumper($list);
}
$VarClassSerial++;
$options{varclass} = sprintf("PkgConfig::Vars::SERIAL_%d", $VarClassSerial);
$options{udefclass} = sprintf("PkgConfig::UDefs::SERIAL_%d", $VarClassSerial);
$options{original} = \%original;
my $udefs = delete $options{VARS} || {};
while (my ($k,$v) = each %$udefs) {
no strict 'refs';
my $vname = join('::', $options{udefclass}, $k);
${$vname} = $v;
}
my $o = $cls->new(%options);
my @libraries;
if(ref $library eq 'ARRAY') {
@libraries = @$library;
} else {
@libraries = ($library);
}
if($options{file_path}) {
if(-r $options{file_path}) {
$o->recursion(1);
$o->parse_pcfile($options{file_path});
$o->recursion(0);
} else {
$o->errmsg("No such file $options{file_path}\n");
}
} else {
foreach my $lib (@libraries) {
$o->recursion(0);
my($op,$ver);
($lib,$op,$ver) = ($1,$2,PkgConfig::Version->new($3))
if $lib =~ /^(.*)\s+(!=|=|>=|<=|>|<)\s+(.*)$/;
$o->find_pcfile($lib);
if(!$o->errmsg && defined $op) {
$op = '==' if $op eq '=';
unless(eval qq{ PkgConfig::Version->new(\$o->pkg_version) $op \$ver })
{
$o->errmsg("Requested '$lib $op $ver' but version of $lib is " .
($o->pkg_version ? $o->pkg_version : '') . "\n");
}
}
}
}
$o;
}
################################################################################
################################################################################
### Modify our flags stack ###
################################################################################
################################################################################
sub append_ldflags {
my ($self,@flags) = @_;
my @ld_flags = _split_flags(@flags);
foreach my $ldflag (@ld_flags) {
next unless $ldflag =~ /^-Wl/;
my (@wlflags) = split(/,/, $ldflag);
shift @wlflags; #first is -Wl,
filter_omit(\@wlflags, $self->exclude_ldflags);
if(!@wlflags) {
$ldflag = "";
next;
}
$ldflag = join(",", '-Wl', @wlflags);
}
@ld_flags = grep $_, @ld_flags;
return unless @ld_flags;
push @{($self->libs_deplist->{$self->recursion} ||=[])},
@ld_flags;
}
# notify us about extra compiler flags
sub append_cflags {
my ($self,@flags) = @_;
push @{$self->cflags}, _split_flags(@flags);
}
################################################################################
################################################################################
### All sorts of parsing is here ###
################################################################################
################################################################################
sub get_requires {
my ($self,$requires) = @_;
return () unless $requires;
my @reqlist = split(/[\s,]+/, $requires);
my @ret;
while (defined (my $req = shift @reqlist) ) {
my $reqlet = [ $req ];
push @ret, $reqlet;
last unless @reqlist;
#check if we need some version scanning:
my $cmp_op;
my $want;
GT_PARSE_REQ:
{
#all in one word:
($cmp_op) = ($req =~ /([<>=]+)/);
if($cmp_op) {
if($req =~ /[<>=]+$/) {
log_debug("comparison operator spaced ($cmp_op)");
($want) = ($req =~ /([^<>=]+$)/);
$want ||= shift @reqlist;
} else {
$want = shift @reqlist;
}
push @$reqlet, ($cmp_op, $want);
} elsif ($reqlist[0] =~ /[<>=]+/) {
$req = shift @reqlist;
goto GT_PARSE_REQ;
}
}
}
#log_debug(@ret);
@ret;
}
sub parse_line {
my ($self,$line,$evals) = @_;
no strict 'vars';
$line =~ s/#[^#]+$//g; # strip comments
return unless $line;
my ($tok) = ($line =~ /([=:])/);
my ($field,$value) = split(/[=:]/, $line, 2);
return unless defined $value;
if($tok eq '=') {
$self->defined_variables->{$field} = $value;
}
#strip trailing/leading whitespace:
$field =~ s/(^\s+)|(\s+)$//msg;
#remove trailing/leading whitespace from value
$value =~ s/(^\s+)|(\s+$)//msg;
log_debugf("Field %s, Value %s", $field, $value);
$field = lc($field);
#perl variables can't have '.' in them:
$field =~ s/\./DOT/g;
#remove quotes from field names
$field =~ s/['"]//g;
# pkg-config escapes a '$' with a '$$'. This won't go in perl:
$value =~ s/[^\\]\$\$/\\\$/g;
$value =~ s/([@%&])/\$1/g;
# append our pseudo-package for persistence.
my $varclass = $self->varclass;
$value =~ s/(\$\{[^}]+\})/lc($1)/ge;
$value =~ s/\$\{/\$\{$varclass\::/g;
# preserve quoted space
$value = join ' ', map { s/(["'])/\\$1/g; "'$_'" } shellwords $value
if $value =~ /[\\"']/;
#quote the value string, unless quoted already
$value = "\"$value\"";
#get existent variables from our hash:
#$value =~ s/'/"/g; #allow for interpolation
$self->assign_var($field, $value);
}
sub parse_pcfile {
my ($self,$pcfile,$wantversion) = @_;
#log_warn("Requesting $pcfile");
open my $fh, "<", $pcfile or die "$pcfile: $!";
$self->prepare_vars();
my @lines = (<$fh>);
close($fh);
my $text = join("", @lines);
$text =~ s,\\[\r\n],,g;
@lines = split(/[\r\n]/, $text);
my @eval_strings;
#Fold lines:
my $pcfiledir = dirname $pcfile;
$pcfiledir =~ s{\\}{/}g;
foreach my $line ("pcfiledir=$pcfiledir", @lines) {
$self->parse_line($line, \@eval_strings);
}
#now that we have eval strings, evaluate them all within the same
#lexical scope:
$self->append_cflags( $self->_pc_var('cflags') );
if($self->static) {
$self->append_cflags( $self->_pc_var('cflags.private') );
}
$self->append_ldflags( $self->_pc_var('libs') );
if($self->static) {
$self->append_ldflags( $self->_pc_var('libs.private') );
}
my @deps;
my @deps_dynamic = $self->get_requires( $self->_pc_var('requires'));
my @deps_static = $self->get_requires( $self->_pc_var('requires.private') );
@deps = @deps_dynamic;
if($self->static) {
push @deps, @deps_static;
}
if($self->recursion == 1 && (!$self->pkg_exists())) {
$self->pkg_version( $self->_pc_var('version') );
$self->pkg_url( $self->_pc_var('url') );
$self->pkg_description( $self->_pc_var('description') );
$self->pkg_exists(1);
}
unless ($self->no_recurse) {
foreach (@deps) {
my ($dep,$cmp_op,$version) = @$_;
$dep = "$dep $cmp_op $version" if defined $cmp_op;
my $other = PkgConfig->find($dep, %{ $self->original });
if($other->errmsg) {
$self->errmsg($other->errmsg);
last;
}
$self->append_cflags( $other->get_cflags );
$self->append_ldflags( $other->get_ldflags );
}
}
}
################################################################################
################################################################################
### Locate and process a .pc file ###
################################################################################
################################################################################
sub find_pcfile {
my ($self,$libname,$version) = @_;
$self->recursion($self->recursion + 1);
my $pcfile = "$libname.pc";
my $found = 0;
my @found_paths = (grep {
-e File::Spec->catfile($_, $pcfile)
} @{$self->search_path});
if(!@found_paths) {
my @search_paths = @{$self->search_path};
$self->errmsg(
join("\n",
"Can't find $pcfile in any of @search_paths",
"use the PKG_CONFIG_PATH environment variable, or",
"specify extra search paths via 'search_paths'",
""
)
) unless $self->errmsg();
return;
}
$pcfile = File::Spec->catfile($found_paths[0], $pcfile);
$self->parse_pcfile($pcfile);
$self->recursion($self->recursion - 1);
}
################################################################################
################################################################################
### Public Getters ###
################################################################################
################################################################################
sub _return_context (@) {
wantarray ? (@_) : join(' ', map { s/(\s|['"])/\\$1/g; $_ } @_)
}
sub get_cflags {
my $self = shift;
my @cflags = @{$self->cflags};
filter_omit(\@cflags, $self->exclude_cflags);
filter_dups(\@cflags);
_return_context @cflags;
}
sub get_ldflags {
my $self = shift;
my @ordered_libs;
my @lib_levels = sort keys %{$self->libs_deplist};
my @ret;
@ordered_libs = @{$self->libs_deplist}{@lib_levels};
foreach my $liblist (@ordered_libs) {
my $lcopy = [ @$liblist ];
filter_dups($lcopy);
filter_omit($lcopy, $self->exclude_ldflags);
push @ret, @$lcopy;
}
@ret = reverse @ret;
filter_dups(\@ret);
@ret = reverse(@ret);
_return_context @ret;
}
sub get_var {
my($self, $name) = @_;
$self->_pc_var($name);
}
sub get_list {
my $self = shift;
my @search_paths = @{$self->search_path};
my @rv = ();
$self->recursion(0);
for my $d (@search_paths) {
next unless -d $d;
for my $pc (bsd_glob("$d/*.pc")) {
if ($pc =~ m|/([^\\\/]+)\.pc$|) {
$self->parse_pcfile($pc);
push @rv, [$1, $self->_pc_var('name') . ' - ' . $self->_pc_var('description')];
}
}
}
@rv;
}
################################################################################
################################################################################
### Utility functions ###
################################################################################
################################################################################
#split a list of tokens by spaces
sub _split_flags {
my @flags = @_;
if(!@flags) {
return @flags;
}
if(@flags == 1) {
my $str = shift @flags;
return () if !$str;
#@flags = map { s/\\(\s)/$1/g; $_ } split(/(?<!\\)\s+/, $str);
@flags = shellwords $str;
}
@flags = grep $_, @flags;
@flags;
}
sub filter_dups {
my $array = shift;
my @ret;
my %seen_hash;
#@$array = reverse @$array;
foreach my $elem (@$array) {
if(exists $seen_hash{$elem}) {
next;
}
$seen_hash{$elem} = 1;
push @ret, $elem;
}
#print Dumper(\%seen_hash);
@$array = @ret;
}
sub filter_omit {
my ($have,$exclude) = @_;
my @ret;
#print Dumper($have);
foreach my $elem (@$have) {
#log_warn("Checking '$elem'");
if(grep { $_ eq $elem } @$exclude) {
#log_warn("Found illegal flag '$elem'");
next;
}
push @ret, $elem;
}
@$have = @ret;
}
sub version_2_array {
my $string = shift;
my @chunks = split(/\./, $string);
my @ret;
my $chunk;
while( ($chunk = pop @chunks)
&& $chunk =~ /^\d+$/) {
push @ret, $chunk;
}
@ret;
}
sub version_check {
my ($want,$have) = @_;
my @a_want = version_2_array($want);
my @a_have = version_2_array($have);
my $max_elem = scalar @a_want > scalar @a_have
? scalar @a_have
: scalar @a_want;
for(my $i = 0; $i < $max_elem; $i++) {
if($a_want[$i] > $a_have[$i]) {
return 0;
}
}
1;
}
if(caller) {
return 1;
}
package
PkgConfig::Version;
use overload
'<=>' => sub { $_[0]->cmp($_[1]) },
'""' => sub { $_[0]->as_string },
fallback => 1;
sub new {
my($class, $value) = @_;
bless [split /\./, defined $value ? $value : ''], $class;
}
sub clone {
__PACKAGE__->new(shift->as_string);
}
sub as_string {
my($self) = @_;
join '.', @{ $self };
}
sub cmp {
my($self, $other) = @_;
no warnings 'uninitialized';
defined($self->[0]) || defined($other->[0]) ? ($self->[0] <=> $other->[0]) || &cmp([@{$self}[1..$#$self]], [@{$other}[1..$#$other]]) : 0;
}
################################################################################
################################################################################
################################################################################
################################################################################
### Script-Only stuff ###
################################################################################
################################################################################
################################################################################
################################################################################
package PkgConfig::Script;
use strict;
use warnings;
use Getopt::Long qw(:config no_ignore_case);
use Pod::Usage;
my $quiet_errors = 1;
my @ARGV_PRESERVE = @ARGV;
my @POD_USAGE_SECTIONS = (
"NAME",
'DESCRIPTION/SCRIPT OPTIONS/USAGE',
"DESCRIPTION/SCRIPT OPTIONS/ARGUMENTS|ENVIRONMENT",
"AUTHOR & COPYRIGHT"
);
my @POD_USAGE_OPTIONS = (
-verbose => 99,
-sections => \@POD_USAGE_SECTIONS
);
GetOptions(
'libs' => \my $PrintLibs,
'libs-only-L' => \my $PrintLibsOnlyL,
'libs-only-l' => \my $PrintLibsOnlyl,
'libs-only-other' => \my $PrintLibsOnlyOther,
'list-all' => \my $ListAll,
'static' => \my $UseStatic,
'cflags' => \my $PrintCflags,
'cflags-only-I' => \my $PrintCflagsOnlyI,
'cflags-only-other' => \my $PrintCflagsOnlyOther,
'exists' => \my $PrintExists,
'atleast-version=s' => \my $AtLeastVersion,
'atleast-pkgconfig-version=s' => \my $AtLeastPkgConfigVersion,
'exact-version=s' => \my $ExactVersion,
'max-version=s' => \my $MaxVersion,
'silence-errors' => \my $SilenceErrors,
'print-errors' => \my $PrintErrors,
'errors-to-stdout' => \my $ErrToStdOut,
'short-errors' => \my $ShortErrors,
'define-variable=s', => \my %UserVariables,
'print-variables' => \my $PrintVariables,
'print-values' => \my $PrintValues,
'variable=s', => \my $OutputVariableValue,
'modversion' => \my $PrintVersion,
'version', => \my $PrintAPIversion,
'real-version' => \my $PrintRealVersion,
'debug' => \my $Debug,
'with-path=s', => \my @ExtraPaths,
'env-only', => \my $EnvOnly,
'guess-paths', => \my $GuessPaths,
'h|help|?' => \my $WantHelp
) or pod2usage(@POD_USAGE_OPTIONS);
if($^O eq 'msys' && !$ENV{PKG_CONFIG_NO_OS_CUSTOMIZATION}) {
$UseStatic = 1;
}
if($WantHelp) {
pod2usage(@POD_USAGE_OPTIONS, -exitval => 0);
}
if($Debug) {
eval {
Log::Fu::set_log_level('PkgConfig', 'DEBUG');
};
$PkgConfig::UseDebugging = 1;
}
if($GuessPaths) {
PkgConfig->GuessPaths();
}
if($PrintAPIversion) {
print '0.', $compat_version, "\n";
exit(0);
}
if($AtLeastPkgConfigVersion) {
my($major,$minor,$patch) = split /\./, $AtLeastPkgConfigVersion;
exit 1 if $major > 0;
exit 1 if $minor > $compat_version;
exit 1 if $minor == $compat_version && $patch > 0;
exit 0;
}
if($PrintRealVersion) {
printf STDOUT ("ppkg-config - cruftless pkg-config\n" .
"Version: %s\n", $PkgConfig::VERSION);
exit(0);
}
if($PrintErrors) {
$quiet_errors = 0;
}
if($SilenceErrors) {
$quiet_errors = 1;
}
# This option takes precedence over all other options
# be it:
# --silence-errors
# or
# --print-errors
if ($ErrToStdOut) {
$quiet_errors = 2;
}
my $WantFlags = ($PrintCflags || $PrintLibs || $PrintLibsOnlyL || $PrintCflagsOnlyI || $PrintCflagsOnlyOther || $PrintLibsOnlyOther || $PrintLibsOnlyl || $PrintVersion);
if($WantFlags) {
$quiet_errors = 0 unless $SilenceErrors;
}
my %pc_options;
if($PrintExists || $AtLeastVersion || $ExactVersion || $MaxVersion || $PrintVersion) {
$pc_options{no_recurse} = 1;
}
$pc_options{static} = $UseStatic;
$pc_options{search_path} = \@ExtraPaths;
if($EnvOnly) {
delete $pc_options{search_path};
$pc_options{search_path_override} = [ @ExtraPaths, @ENV_SEARCH_PATH];
}
$pc_options{print_variables} = $PrintVariables;
$pc_options{print_values} = $PrintValues;
$pc_options{VARS} = \%UserVariables;
if($ListAll) {
my $o = PkgConfig->find([], %pc_options);
my @list = $o->get_list();
# can't use List::Util::max as it wasn't core until Perl 5.8
my $max_length = 0;
foreach my $length (map { length $_->[0] } @list) {
$max_length = $length if $length > $max_length;
}
printf "%-${max_length}s %s\n", $_->[0], $_->[1] for @list;
exit(0);
}
my @FINDLIBS = @ARGV or die "Must specify at least one library";
if($AtLeastVersion) {
@FINDLIBS = map { "$_ >= $AtLeastVersion" } @FINDLIBS;
} elsif($MaxVersion) {
@FINDLIBS = map { "$_ <= $MaxVersion" } @FINDLIBS;
} elsif($ExactVersion) {
@FINDLIBS = map { "$_ = $ExactVersion" } @FINDLIBS;
}
my $o = PkgConfig->find(\@FINDLIBS, %pc_options);
if($o->errmsg) {
# --errors-to-stdout
if ($quiet_errors eq 2) {
print STDOUT $o->errmsg;
# --print-errors
} elsif ($quiet_errors eq 1) {
print STDERR $o->errmsg;
}
# --silence-errors
exit(1);
}
if($o->print_variables) {
while (my ($k,$v) = each %{$o->defined_variables}) {
print $k;
if($o->print_values) {
print "=$v";
} else {
print "\n";
}
}
}
if($OutputVariableValue) {
my $val = ($o->_pc_var($OutputVariableValue) or "");
print $val . "\n";
}
if(!$WantFlags) {
exit(0);
}
if($PrintVersion) {
print $o->pkg_version . "\n";
exit(0);
}
my @print_flags;
if($PrintCflags) {
@print_flags = $o->get_cflags;
}
if($PrintCflagsOnlyI) {
@print_flags = grep /^-I/, $o->get_cflags;
}
if($PrintCflagsOnlyOther) {
@print_flags = grep /^-[^I]/, $o->get_cflags;
}
if($PrintLibs) {
@print_flags = $o->get_ldflags;
}
if ($PrintLibsOnlyOther) {
@print_flags = grep /^-[^LRl]/, $o->get_ldflags;
}
# handle --libs-only-L and --libs-only-l but watch the case when
# we got 'ppkg-config --libs-only-L --libs-only-l foo' which must behave just like
# 'ppkg-config --libs-only-l foo'
if($PrintLibsOnlyl or ($PrintLibsOnlyl and $PrintLibsOnlyL)) {
@print_flags = grep /^-l/, $o->get_ldflags;
} elsif ($PrintLibsOnlyL) {
@print_flags = grep /^-[LR]/, $o->get_ldflags;
}
print scalar PkgConfig::_return_context(@print_flags);
print "\n";
exit(0);
__END__
=head1 NAME
PkgConfig - Pure-Perl Core-Only replacement for pkg-config
=head1 SYNOPSIS
=head2 As a replacement for C<pkg-config>
$ ppkg-config --libs --cflags --static gio-2.0
#outputs (lines artificially broken up for readability):
# -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
# -pthread -lgio-2.0 -lz -lresolv -lgobject-2.0
# -lgmodule-2.0 -ldl -lgthread-2.0 -pthread -lrt -lglib-2.0
C<pkg-config.pl> can be used as an alias for C<ppkg-config> on platforms that
support it. It can also be installed as C<pkg-config> though this is not
recommended if your system has a native C<pkg-config>.
Compare to:
$ pkg-config --libs --cflags --static gio-2.0
#outputs ( "" ):
# -pthread -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include
# -pthread -lgio-2.0 -lz -lresolv -lgobject-2.0 -lgmodule-2.0
# -ldl -lgthread-2.0 -lrt -lglib-2.0
=head2 From another Perl module
use PkgConfig;
my $o = PkgConfig->find('gio');
if($o->errmsg) {
#handle error
} else {
my $prefix = $o->get_var('prefix');
my @cflags = $o->get_cflags;
my @ldflags = $o->get_ldflags;
}
=head1 DESCRIPTION
C<PkgConfig> provides a pure-perl, core-only replacement for the C<pkg-config>
utility.
This is not a description of the uses of C<pkg-config> but rather a description
of the differences between the C version and the Perl one.
While C<pkg-config> is a compiled binary linked with glib, the pure-perl version
has no such requirement, and will run wherever Perl ( >= 5.6 ) does.
The main supported options are the common C<--libs>, C<--cflags>,
C<--static>, C<--exists> and C<--modversion>.
=head2 SCRIPT OPTIONS
=head3 USAGE
<packagename1 pkgname2..> [ --options ]
=head3 ARGUMENTS
By default, a library name must be supplied unless one of L<--version>,
or L<--real-version> is specified.
The output should normally be suitable for passing to your favorite compiler.
=head4 --libs
(Also) print linker flags. Dependencies are traverse in order. Top-level dependencies
will appear earlier in the command line than bottom-level dependencies.
=head4 --libs-only-L
Prints -L/-R part of "--libs". It defines library search path but without libraries to link with.
=head4 --libs-only-l
Prints the -l part of "--libs".
=head4 --libs-only-other
Prints the part of "--libs" not covered by "--libs-only-L"
and "--libs-only-l", such as "--pthread".
=head4 --list-all
List all know packages.
=head4 --cflags
(Also) print compiler and C preprocessor flags.
=head4 --cflags-only-I
Prints the -I part of "--cflags"
=head4 --cflags-only-other
Prints the parts of "--cflags" not covered by "--cflags-only-I".
=head4 --modversion
Print the version of a given package.
=head4 --static
Use extra dependencies and libraries if linking against a static version of the
requested library
=head4 --exists
Return success (0) if the package exists in the search path.
=head4 --with-path=PATH
Prepend C<PATH> to the list of search paths containing C<.pc> files.
This option can be specified multiple times with different paths, and they will
all be added.
=head4 --env-only
Using this option, B<only> paths specified in C<PKG_CONFIG_PATH> are recognized
and any hard-coded defaults are ignored.
=head4 --guess-paths
Invoke C<gcc> and C<ld> to determine default linker and include paths. Default
paths will be excluded from explicit -L and -I flags.
=head4 --define-variable=VARIABLE=VALUE
Define a variable, overriding any such variable definition in the .pc file, and
allowing your value to interpolate with subsequent uses.
=head4 --variable=VARIABLE
This returns the value of a variable defined in a package's .pc file.
=head4 --print-variables
Print all defined variables found in the .pc files.
=head4 --version
The target version of C<pkg-config> emulated by this script
=head4 --real-version
The actual version of this script
=head4 --debug
Print debugging information
=head4 --silence-errors
Turn off errors. This is the default for non-libs/cflag/modversion
arguments
=head4 --print-errors
Print errors to STDERR and takes precedence over
C<--silence-errors>
=head4 --short-errors
Ignored, but recognized for compatibility.
=head4 --errors-to-stdout
Print errors to STDOUT and takes precedence over
C<--print-errors>
=head3 ENVIRONMENT
the C<PKG_CONFIG_PATH> and C<PKG_CONFIG_LIBDIR> variables are honored and used
as a colon-delimited (semicolon-delimited on Windows) list of directories with
contain C<.pc> files.
Other environment variables recongized by both C<pkg-config> and L<PkgConfig>
include:
=over 4
=item PKG_CONFIG_ALLOW_SYSTEM_CFLAGS
=item PKG_CONFIG_ALLOW_SYSTEM_LIBS
=back
If L<Win32API::Registry> is installed, on Windows (but not Cygwin) L<PkgConfig>
will also consult these registry keys. The names are ignored, but the values
are paths containing C<.pc> files.
=over 4
=item HKEY_CURRENT_USER\Software\pkgconfig\PKG_CONFIG_PATH
=item HKEY_LOCAL_MACHINE\Software\pkgconfig\PKG_CONFIG_PATH
=back
Registry support should be considered somewhat experimental, subject to change
in the future, though not without good reason. The rationale for this caveat
is that this feature is documented in several places, but I have yet to find
a working version that implements this feature.
=head2 MODULE OPTIONS
=head4 I<< PkgConfig->find >>
my $result = PkgConfig->find($library, %options);
Find a library and return a result object.
C<$library> can be either a single name of a library, or a reference to an
array of library names
The options are in the form of hash keys and values, and the following are
recognized:
=over
=item C<search_path>
=item C<search_path_override>
Prepend search paths in addition to the paths specified in C<$ENV{PKG_CONFIG_PATH}>
The value is an array reference.
the C<_override> variant ignores defaults (like C<PKG_CONFIG_PATH>).
=item C<file_path>
Specifies the full path of the of the .pc file that you wish to load. It does
not need to be in the search path (although any dependencies will need to be).
Useful if you know the full path of the exact .pc file that you want.
=item C<exclude_cflags>
=item C<exclude_ldflags>
=item C<exclude_cflags_override>
=item C<exclude_ldflags_override>
Some C<.pc> files specify default compiler and linker search paths, e.g.
C<-I/usr/include -L/usr/lib>. Specifying them on the command line can be
problematic as it drastically changes the search order.
The above options will either append or replace the options which are excluded
and filtered.
The default excluded linker and compiler options can be obtained via
C<@PkgConfig::DEFAULT_EXCLUDE_LFLAGS> and C<@PkgConfig::DEFAULT_EXCLUDE_CFLAGS>,
respectively.
=item C<static>
Also specify static libraries.
=item C<no_recurse>
Do not recurse dependencies. This is useful for just doing version checks.
=item C<VARS>
Define a hashref of variables to override any variable definitions within
the .pc files. This is equivalent to the C<--define-variable> command-line
option.
=back
A C<PkgConfig> object is returned and may be queried about the results:
=head4 I<< $o->errmsg >>
An error message, if any. This is a string and indicates an error.
=head4 I<< $o->pkg_exists >>
Boolean value, true if the package exists.
=head4 I<< $o->pkg_version >>
The version of the package
=head4 I<< $o->get_cflags >>
=head4 I<< $o->get_ldflags >>
Returns compiler and linker flags, respectively.
In list context, these methods return a list with each argument split on
unescaped spaces.
In list context returns a list of compiler and linker flags, respectively.
In scalar context returns a string of compiler and linker flags with spaces
and quotes escaped correctly.
=head4 I<< $o->get_var($name) >>
Get the variable with the given name.
=head4 I<< PkgConfig->Guess >>
This is a class method, and will replace the hard-coded default linker and include
paths with those discovered by invoking L<ld(1)> and L<cpp(1)>.
Currently this only works with GCC-supplied C<ld> and GNU C<ld>.
=head2 INSTALL
The C<Makefile.PL> that comes with C<PkgConfig> can take one or more C<--script>
options to change of the name of the script or scripts that are installed.
=over 4
=item --script ppkg-config
This is the default and works on all platforms
=item --script pkg-config.pl
This is installed by default on all platforms except for Windows, where the .pl
may confuse the shell and cause the script to be opened in a text editor.
=item --script pkg-config
This is the default name of the real C<pkg-config> and so you have to specifically
enable it if you want it.
=item --script none
Don't install any scripts.
=back
Example, install all script names:
% perl Makefile.PL --script ppkg-config --script pkg-config.pl --script pkg-config
Example, don't install any scripts:
% perl Makefile.PL --script none
You can also set the environment variable PERL_PKG_CONFIG_SCRIPTS to the
desired --script value (separating each script name with a comma C<,>) to
ensure that upgrades of PkgConfig do the same.
=head2 CAVEATS
On Strawberry Perl C<ppkg-config> acts like Strawberry is the system.
This means that
=over 4
=item
The .pc files that are bundled with Strawberry are searched by default.
=item
The Strawberry include and lib directories are used to compute the
exclusion lists.
=back
As of Strawberry 5.20.0.1 PkgConfig is bundled with Strawberry and
C<pkg-config> is installed by default (in addition to C<ppkg-config>,
though the C<ppkg-config> alias is NOT bundled with Strawberry itself).
For details on how to patch the .pc files bundled with older Strawberries,
see the C<README.win32> that comes with this Distribution.
=head2 BUGS
The order of the flags is not exactly matching to that of C<pkg-config>. From my
own observation, it seems this module does a better job, but I might be wrong.
Unlike C<pkg-config>, the scripts C<--exists> function will return nonzero if
a package B<or> any of its dependencies are missing. This differs from the
behavior of C<pkg-config> which will just check for the definition of the
package itself (without dependencies).
=head1 SEE ALSO
=over 4
=item L<pkg-config|http://www.freedesktop.org/wiki/Software/pkg-config>
The original C implementation
=item L<pkgconf|https://github.com/pkgconf/pkgconf>
An alternative C implementation
=item L<PkgConfig::LibPkgConf>
Perl bindings for C<libpkgconf>, the same library that C<pkgconf> is built on.
=item L<ExtUtils::PkgConfig>
A wrapper around the C<pkg-config> binary that can be used in your C<Makefile.PL>
or C<Build.PL>.
=item L<http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/pkg-config/>
Another perl implementation of pkg-config
=item L<pkg-config|https://github.com/ruby-gnome2/pkg-config>
Pure Ruby implementation.
=item L<pykg-config|https://pypi.python.org/pypi/pykg-config/1.2.0>
A Python implementation.
=back
=head1 AUTHOR
=over 4
=item Original Author: M. Nunberg
=item Current maintainer: Graham Ollis E<lt>plicease@cpan.orgE<gt>
=back
Other contributors include:
=over 4
=item Vladimir Timofeev (vovkasm)
=item kmx
=item Sanel Zukan
=item Breno G. de Oliveira (garu)
=item Gregor Herrmann
=item Ilya Pavlov (ILUX, Ilya33)
=item Shawn Laffan (SLAFFAN, shawnlaffan)
=item Ari Jolma (AJOLMA)
=back
=head1 COPYRIGHT AND LICENSE
Copyright (C) 2012 M. Nunberg
This is free software; you can redistribute it and/or modify it under the same
terms as the Perl 5 programming language system itself.
=cut