From 820cb3fcc50f9c770b552e145df102ae0fcdc98e Mon Sep 17 00:00:00 2001 From: Chris Kuethe Date: Mon, 27 Nov 2006 16:49:14 +0000 Subject: Yet Another pkg-config program, this time as a perl script. Not all flags and options are implemented yet, but it works well enough to build a bunch of ports and xenocara (which requires pkg-config). It is not yet activated in the build; I'm committing to allow for in-tree development (including a man page). ok espie, matthieu --- usr.bin/pkg-config/Makefile | 13 ++ usr.bin/pkg-config/pkg-config | 360 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 373 insertions(+) create mode 100644 usr.bin/pkg-config/Makefile create mode 100644 usr.bin/pkg-config/pkg-config (limited to 'usr.bin') diff --git a/usr.bin/pkg-config/Makefile b/usr.bin/pkg-config/Makefile new file mode 100644 index 00000000000..28ece1330f3 --- /dev/null +++ b/usr.bin/pkg-config/Makefile @@ -0,0 +1,13 @@ +# $OpenBSD: Makefile,v 1.1 2006/11/27 16:49:13 ckuethe Exp $ + +SCRIPT=pkg-config + +# Nothing to build or clean +all: +clean: + +install: + ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${SCRIPT} ${DESTDIR}${BINDIR}/ + +.include diff --git a/usr.bin/pkg-config/pkg-config b/usr.bin/pkg-config/pkg-config new file mode 100644 index 00000000000..76ddff5ddf4 --- /dev/null +++ b/usr.bin/pkg-config/pkg-config @@ -0,0 +1,360 @@ +#!/usr/bin/perl + +#$CSK: pkgconfig.pl,v 1.39 2006/11/27 16:26:20 ckuethe Exp $ +# Copyright (c) 2006 Chris Kuethe +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use strict; +use warnings; +use Getopt::Long; + +my @PKGPATH = qw(/usr/local/lib/pkgconfig /usr/X11R6/lib/pkgconfig ); +push(@PKGPATH, '/usr/local/libdata/pkgconfig', '/usr/X11R6/libdata/pkgconfig'); + +if (defined($ENV{'PKG_CONFIG_PATH'}) && $ENV{'PKG_CONFIG_PATH'}){ + push(@PKGPATH, split(/:/,$ENV{'PKG_CONFIG_PATH'})); +} + +my $logfile = ''; +if (defined($ENV{'PKG_CONFIG_LOGFILE'}) && $ENV{'PKG_CONFIG_LOGFILE'}){ + $logfile = $ENV{'PKG_CONFIG_LOGFILE'}; +} + +our $version = 0.19; # pretend to be this version of pkgconfig +my $parse_args = 1; +my ($deps, $privdeps, $var, $val, $p, $f); + +our %configs = (); +our %mode = (); +our $D = 0; # debug flag + +$/ = undef; +if ($logfile){ + open (L, ">>" . $logfile); + print L '[' . join('] [', $0, @ARGV) . "]\n"; + close(L); +} + +# combo arg-parsing and dependency resolution loop. Hopefully when the loop +# terminates, we have a full list of packages upon which we depend, and the +# right set of compiler and linker flags to use them. +# +# as each .pc file is loaded, it is stored in %configs, indexed by package +# name. this makes it possible to then pull out flags or do substitutions +# without having to go back and reload the files from disk + +Getopt::Long::Configure('no_ignore_case'); +GetOptions( 'debug' => \$D, + 'help' => \&help, #does not return + 'usage' => \&help, #does not return + 'list-all' => \&do_list, #does not return + 'version' => sub { print "$version\n" ; exit(0);} , + 'errors-to-stdout' => sub { $mode{'estdout'} = 1}, + 'print-errors' => sub { $mode{'printerr'} = 1}, + 'atleast-pkgconfig-version=s' => \$mode{'minvers'}, + + 'cflags' => sub { $mode{'cflags'} = 3}, + 'cflags-only-I' => sub { $mode{'cflags'} |= 1}, + 'cflags-only-other' => sub { $mode{'cflags'} |= 2}, + 'libs' => sub { $mode{'libs'} = 7}, + 'libs-only-l' => sub { $mode{'libs'} |= 1}, + 'libs-only-L' => sub { $mode{'libs'} |= 2}, + 'libs-only-other' => sub { $mode{'libs'} |= 4}, + 'exists' => sub { $mode{'exists'} = 1} , + 'static' => sub { $mode{'static'} = 1}, + 'uninstalled' => sub { $mode{'uninstalled'} = 1}, + 'atleast-version=s' => \$mode{'atleast-version'}, + 'modversion=s' => \$mode{'modversion'}, + 'variable=s' => \$mode{'variable'} + ); + +print STDERR "\n[" . join('] [', $0, @ARGV) . "]\n" if ($D); +self_version($mode{'minvers'}) if ($mode{'minvers'}); #does not return +do_modversion($mode{'modversion'}) if ($mode{'modversion'}); #does not return + +$p = join(' ', @ARGV); +$p =~ s/\s+/ /g; +$p =~ s/^\s//g; +@ARGV = split(/\s+/, $p); + +if (defined($mode{'exists'})){ + while (@ARGV){ + if ((@ARGV >= 2) && ($ARGV[1] =~ /[<=>]+/) && + ($ARGV[2] =~ /[0-9\.]+/)){ + exit 1 unless (versionmatch(@ARGV)); + shift(@ARGV); shift(@ARGV); shift(@ARGV); + } else { + exit 1 unless (pathresolve($ARGV[0])); + shift(@ARGV); + } + } + exit(0); +} + +do_variable($ARGV[0],$mode{'variable'}) if ($mode{'variable'}); + +while (@ARGV){ + $p = $ARGV[0]; + if ((@ARGV >= 2) && ($ARGV[1] =~ /[<=>]+/) && + ($ARGV[2] =~ /[0-9\.]+/)){ + shift(@ARGV); + shift(@ARGV); + } + shift(@ARGV); + $p =~ s/,//g; + unless ($configs{$p}){ # don't reprocess things we've seen + print STDERR "processing $p\n" if ($D); + if ($f = pathresolve($p)){ # locate the .pc file + exit(0) if (defined($mode{'exists'})); + + $configs{$p} = slurp($f); # load the config + $deps = ''; + if ($configs{$p} =~ /\bRequires: +(\w.+?)\n/){ + $deps = $1; + # XXX how should i handle versions? + $deps =~ s/[<>=]+\s*[0-9\.]+\s*//; + $deps =~ tr/,/ /; + } + print STDERR "package $p requires '$deps'\n" + if ($D && $deps); + push(@ARGV, split(/\s+/,$deps)) if ($deps); + + $privdeps = ''; + if ($configs{$p} =~ /\bRequires\.private: +(\w.+?)\n/){ + $privdeps = $1; + # XXX how should i handle versions? + $privdeps =~ s/[<>=]+\s*[0-9\.]+\s*//; + } + print STDERR "package $p requires (private) '" . + $privdeps . "'\n" if ($D && $privdeps); + push(@ARGV, split(/\s+/,$privdeps)) if ($privdeps); + + } else { + exit(1) if (defined($mode{'exists'})); + warn("can't find $p\n"); + } + } +} + +do_cflags() if ($mode{'cflags'}); +do_libs() if ($mode{'libs'}); + +exit(0); + +########################################################################### + +# look for the .pc file in each of the PKGPATH elements. Return the path or +# undef if it's not there +sub pathresolve{ + my ($p); + + $p = shift; + foreach (@PKGPATH){ + $f = $_ . '/' . ${p} . '.pc'; + print STDERR "pathresolve($p) looking in $f\n" if ($D); + last if (-f $f); + $f = undef; + } + return $f; +} + + +# Given a filename, return its contents. Also do variable substitutions. +sub slurp{ + my ($f); + + $f = shift; + open(F, $f) or return undef; + print STDERR "slurp($f) OK\n" if ($D); + $f = ; + close(F); + $f = varsub($f); + return $f; +} + +# Do variable substitutions, so if "target=x11" is present (for example), +# any lines referring to $target are filled in properly. +sub varsub{ + my ($var, $val); + + my $buf = shift; + while ($buf =~ /\${(\w+)}/gsm){ + $var = $1; + if ($buf =~ /${var}=(.+?)\n/s){ + $val = $1; + $buf =~ s/\${$var}/$val/g; + } + } + return $buf; +} + +#if the variable option is set, pull out the named variable +sub do_variable{ + my ($p, $v, undef) = @_; + my ($f); + + exit(1) unless ($f = pathresolve($p)); + exit(1) unless ($f = slurp($f)); + + exit(1) unless ($f =~ /\b${v}=(.+?)\n/); + print "$1\n"; + exit(0); +} + +#if the modversion option is set, pull out the compiler flags +sub do_modversion{ + my ($p, undef) = @_; + my ($f); + + exit(1) unless ($f = pathresolve($p)); + exit(1) unless ($f = slurp($f)); + + exit(1) unless ($f =~ /\bVersion:\s+(.+?)\n/); + print "$1\n"; + exit(0); +} + +#if the cflags option is set, pull out the compiler flags +sub do_cflags{ + my %words; # store them as a hash to get de-duplicating + my @out; + + foreach my $p (keys %configs){ + if ($configs{$p} =~ /\bCflags:\s+(.+?)\n/){ + foreach (split(/\s+/, $1)){ $words{$_}=1; } + } + } + foreach (sort keys %words){ + push(@out, $_) if (/^-I/ && ($mode{'cflags'} & 1)); + push(@out, $_) if (/^-[^I]/ && ($mode{'cflags'} & 2)); + } + printf("%s\n", join(' ', @out)); +} + +#if the lib option is set, pull out the linker flags +sub do_libs{ + my %words; # store them as a hash to get de-duplicating + my @out; + + foreach my $p (keys %configs){ + if ($configs{$p} =~ /\bLibs:\s+(.+?)\n/){ + foreach (split(/\s+/, $1)){ $words{$_}=1; } + } + } + foreach (sort keys %words){ + push(@out, $_) if (/^-l/ && ($mode{'libs'} & 1)); + push(@out, $_) if (/^-L/ && ($mode{'libs'} & 2)); + push(@out, $_) if (/^-[^lL]/ && ($mode{'libs'} & 4)); + } + printf("%s\n", join(' ', @out)); +} + +#list all packages +sub do_list{ + my ($p, $x, $y, @files, $fname, $name); + foreach (@PKGPATH){ push(@files, <$_/*.pc>); } + + # Scan the lengths of the package names so I can make a format + # string to line the list up just like the real pkgconfig does. + $x = 0; + foreach (@files){ + $fname = (split(/\//, $_))[-1]; + $fname =~ s/\.pc$//; + $y = length($fname); + $x = (($y > $x) ? $y : $x); + } + $x *= -1; + + foreach (@files){ + $p = slurp($_); + $fname = (split(/\//, $_))[-1]; + $fname =~ s/\.pc$//; + if ($p =~ /Name: (\w[^\n]+)\n/gm){ + $name = $1; + if ($p =~ /Description:\s+(\w[^\n]+)\n/gm){ + printf("%${x}s %s - %s\n", $fname, $name, $1); + } + } + } + exit(0); +} + +sub help{ + my ($unused); + print < in +--cflags package [versionspec] [package [versionspec]] +--cflags-only-I - only output -Iincludepath flags +--cflags-only-other - only output flags that are not -I +--libs package [versionspec] [package [versionspec]] +--libs-only-l - only output -llib flags +--libs-only-L - only output -Llibpath flags +--libs-only-other - only output flags that are not -l or -L +--exists package [versionspec] [package [versionspec]] +--uninstalled - allow for uninstalled versions to be used +EOF +; + exit(1); +} + +# do we meet/beat the version the caller requested? +sub self_version{ + my (@a, @b, $v); + $v = shift; + @a = split(/\./, $v); + @b = split(/\./, $version); + + if (($b[0] >= $a[0]) && ($b[1] >= $a[1])){ + exit(0); + } else { + exit(1); + } +} + +# got a package meeting the requested specific version? +sub versionmatch{ + my ($pname, $op, $ver, undef) = @_; + my (@want, @inst, $m, $f); + + print STDERR "pname = '$pname'\n" if ($D); + # can't possibly match if we can't find the file + return 0 unless ($f = pathresolve($pname)); + # load the file + $configs{$pname} = slurp($f); + # can't possibly match if we can't find the version string + return 0 unless ($configs{$pname} =~ /Version: ([0-9\.]+)\n/gm); + + print "comparing $ver (wanted) to $1 (installed)\n" if ($D); + @inst = split(/\./, $1); + @want = split(/\./, $ver); + + while (@inst && @want){ #so long as both lists have something + # bail if the requested version element beats existing + return 1 if ($inst[0] > $want[0]); + return 0 if ($inst[0] < $want[0]); + shift(@inst); shift(@want); + } + # the version at least equals the requested. if the requested + # version has some micropatchlevel beyond the existing version, + # return failure + return 0 if (@want); + # and after all that, the version is good enough + return 1; +} -- cgit v1.2.3