Annotation of src/usr.bin/pkg-config/pkg-config, Revision 1.4
1.1 ckuethe 1: #!/usr/bin/perl
2:
3: #$CSK: pkgconfig.pl,v 1.39 2006/11/27 16:26:20 ckuethe Exp $
4: # Copyright (c) 2006 Chris Kuethe <ckuethe@openbsd.org>
5: #
6: # Permission to use, copy, modify, and distribute this software for any
7: # purpose with or without fee is hereby granted, provided that the above
8: # copyright notice and this permission notice appear in all copies.
9: #
10: # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11: # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12: # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13: # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14: # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15: # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16: # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17:
18: use strict;
19: use warnings;
20: use Getopt::Long;
1.4 ! espie 21: use File::Basename;
1.1 ckuethe 22:
23: my @PKGPATH = qw(/usr/local/lib/pkgconfig /usr/X11R6/lib/pkgconfig );
24:
1.4 ! espie 25: if (defined($ENV{'PKG_CONFIG_PATH'}) && $ENV{'PKG_CONFIG_PATH'}) {
! 26: push(@PKGPATH, split /:/, $ENV{'PKG_CONFIG_PATH'});
1.1 ckuethe 27: }
28:
29: my $logfile = '';
1.4 ! espie 30: if (defined($ENV{'PKG_CONFIG_LOGFILE'}) && $ENV{'PKG_CONFIG_LOGFILE'}) {
1.1 ckuethe 31: $logfile = $ENV{'PKG_CONFIG_LOGFILE'};
32: }
33:
34: our $version = 0.19; # pretend to be this version of pkgconfig
35: my $parse_args = 1;
36: my ($deps, $privdeps, $var, $val, $p, $f);
37:
38: our %configs = ();
39: our %mode = ();
40: our $D = 0; # debug flag
41:
42: $/ = undef;
1.4 ! espie 43: if ($logfile) {
! 44: open my $L, ">>" . $logfile;
! 45: print $L '[' . join('] [', $0, @ARGV) . "]\n";
! 46: close $L;
1.1 ckuethe 47: }
48:
49: # combo arg-parsing and dependency resolution loop. Hopefully when the loop
50: # terminates, we have a full list of packages upon which we depend, and the
51: # right set of compiler and linker flags to use them.
52: #
53: # as each .pc file is loaded, it is stored in %configs, indexed by package
54: # name. this makes it possible to then pull out flags or do substitutions
55: # without having to go back and reload the files from disk
56:
57: Getopt::Long::Configure('no_ignore_case');
58: GetOptions( 'debug' => \$D,
59: 'help' => \&help, #does not return
60: 'usage' => \&help, #does not return
61: 'list-all' => \&do_list, #does not return
62: 'version' => sub { print "$version\n" ; exit(0);} ,
63: 'errors-to-stdout' => sub { $mode{'estdout'} = 1},
64: 'print-errors' => sub { $mode{'printerr'} = 1},
65: 'atleast-pkgconfig-version=s' => \$mode{'minvers'},
66:
67: 'cflags' => sub { $mode{'cflags'} = 3},
68: 'cflags-only-I' => sub { $mode{'cflags'} |= 1},
69: 'cflags-only-other' => sub { $mode{'cflags'} |= 2},
70: 'libs' => sub { $mode{'libs'} = 7},
71: 'libs-only-l' => sub { $mode{'libs'} |= 1},
72: 'libs-only-L' => sub { $mode{'libs'} |= 2},
73: 'libs-only-other' => sub { $mode{'libs'} |= 4},
74: 'exists' => sub { $mode{'exists'} = 1} ,
75: 'static' => sub { $mode{'static'} = 1},
76: 'uninstalled' => sub { $mode{'uninstalled'} = 1},
77: 'atleast-version=s' => \$mode{'atleast-version'},
78: 'modversion=s' => \$mode{'modversion'},
79: 'variable=s' => \$mode{'variable'}
80: );
81:
1.4 ! espie 82: print STDERR "\n[" . join('] [', $0, @ARGV) . "]\n" if $D;
! 83: self_version($mode{'minvers'}) if $mode{'minvers'}; #does not return
! 84: do_modversion($mode{'modversion'}) if $mode{'modversion'}; #does not return
1.1 ckuethe 85:
86: $p = join(' ', @ARGV);
87: $p =~ s/\s+/ /g;
88: $p =~ s/^\s//g;
1.4 ! espie 89: @ARGV = split /\s+/, $p;
1.1 ckuethe 90:
1.4 ! espie 91: if (defined $mode{'exists'}) {
! 92: while (@ARGV) {
1.1 ckuethe 93: if ((@ARGV >= 2) && ($ARGV[1] =~ /[<=>]+/) &&
1.4 ! espie 94: ($ARGV[2] =~ /[0-9\.]+/)) {
! 95: exit 1 unless versionmatch(@ARGV);
! 96: shift @ARGV; shift @ARGV; shift @ARGV;
1.1 ckuethe 97: } else {
1.4 ! espie 98: exit 1 unless pathresolve($ARGV[0]);
! 99: shift @ARGV;
1.1 ckuethe 100: }
101: }
1.4 ! espie 102: exit 0;
1.1 ckuethe 103: }
104:
1.4 ! espie 105: do_variable($ARGV[0], $mode{'variable'}) if $mode{'variable'};
1.1 ckuethe 106:
107: while (@ARGV){
108: $p = $ARGV[0];
109: if ((@ARGV >= 2) && ($ARGV[1] =~ /[<=>]+/) &&
1.4 ! espie 110: ($ARGV[2] =~ /[0-9\.]+/)) {
! 111: shift @ARGV;
! 112: shift @ARGV;
1.1 ckuethe 113: }
1.4 ! espie 114: shift @ARGV;
1.1 ckuethe 115: $p =~ s/,//g;
1.4 ! espie 116: unless ($configs{$p}) { # don't reprocess things we've seen
! 117: print STDERR "processing $p\n" if $D;
! 118: if ($f = pathresolve($p)) { # locate the .pc file
! 119: exit 0 if defined $mode{'exists'};
1.1 ckuethe 120:
121: $configs{$p} = slurp($f); # load the config
122: $deps = '';
1.4 ! espie 123: if ($configs{$p} =~ /\bRequires: +(\w.+?)\n/) {
1.1 ckuethe 124: $deps = $1;
125: # XXX how should i handle versions?
126: $deps =~ s/[<>=]+\s*[0-9\.]+\s*//;
127: $deps =~ tr/,/ /;
128: }
129: print STDERR "package $p requires '$deps'\n"
1.4 ! espie 130: if $D && $deps;
! 131: push(@ARGV, split /\s+/, $deps) if $deps;
1.1 ckuethe 132:
133: $privdeps = '';
1.4 ! espie 134: if ($configs{$p} =~ /\bRequires\.private: +(\w.+?)\n/) {
1.1 ckuethe 135: $privdeps = $1;
136: # XXX how should i handle versions?
137: $privdeps =~ s/[<>=]+\s*[0-9\.]+\s*//;
138: }
139: print STDERR "package $p requires (private) '" .
1.4 ! espie 140: $privdeps . "'\n" if $D && $privdeps;
! 141: push(@ARGV, split /\s+/, $privdeps) if $privdeps;
1.1 ckuethe 142:
143: } else {
1.4 ! espie 144: warn "can't find $p\n";
! 145: exit 1;
1.1 ckuethe 146: }
147: }
148: }
149:
1.4 ! espie 150: do_cflags() if $mode{'cflags'};
! 151: do_libs() if $mode{'libs'};
1.1 ckuethe 152:
1.4 ! espie 153: exit 0;
1.1 ckuethe 154:
155: ###########################################################################
156:
157: # look for the .pc file in each of the PKGPATH elements. Return the path or
158: # undef if it's not there
1.4 ! espie 159: sub pathresolve
! 160: {
! 161: my ($p) = @_;
! 162:
! 163: foreach my $d (@PKGPATH) {
! 164: $f = "$d/$p.pc";
! 165: print STDERR "pathresolve($p) looking in $f\n" if $D;
! 166: last if -f $f;
1.1 ckuethe 167: $f = undef;
168: }
169: return $f;
170: }
171:
172:
173: # Given a filename, return its contents. Also do variable substitutions.
1.4 ! espie 174: sub slurp
! 175: {
! 176: my ($f) = @_;
! 177:
! 178: open my $F, '<', $f or return undef;
! 179: print STDERR "slurp($f) OK\n" if $D;
! 180: $f = <$F>;
! 181: close $F;
1.1 ckuethe 182: $f = varsub($f);
183: return $f;
184: }
185:
186: # Do variable substitutions, so if "target=x11" is present (for example),
187: # any lines referring to $target are filled in properly.
1.4 ! espie 188: sub varsub
! 189: {
! 190: my ($buf) = @_;
! 191:
1.1 ckuethe 192: my ($var, $val);
193:
1.4 ! espie 194: while ($buf =~ /\${(\w+)}/gsm) {
1.1 ckuethe 195: $var = $1;
1.4 ! espie 196: if ($buf =~ /${var}=(.+?)\n/s) {
1.1 ckuethe 197: $val = $1;
198: $buf =~ s/\${$var}/$val/g;
199: }
200: }
201: return $buf;
202: }
203:
204: #if the variable option is set, pull out the named variable
1.4 ! espie 205: sub do_variable
! 206: {
1.1 ckuethe 207: my ($p, $v, undef) = @_;
208: my ($f);
209:
1.4 ! espie 210: exit 1 unless $f = pathresolve($p);
! 211: exit 1 unless $f = slurp($f);
1.1 ckuethe 212:
1.4 ! espie 213: exit 1 unless $f =~ /\b${v}=(.+?)\n/;
1.1 ckuethe 214: print "$1\n";
1.4 ! espie 215: exit 0;
1.1 ckuethe 216: }
217:
218: #if the modversion option is set, pull out the compiler flags
1.4 ! espie 219: sub do_modversion
! 220: {
1.1 ckuethe 221: my ($p, undef) = @_;
222: my ($f);
223:
1.4 ! espie 224: exit 1 unless $f = pathresolve($p);
! 225: exit 1 unless $f = slurp($f);
1.1 ckuethe 226:
1.4 ! espie 227: exit 1 unless $f =~ /\bVersion:\s+(.+?)\n/;
1.1 ckuethe 228: print "$1\n";
1.4 ! espie 229: exit 0;
1.1 ckuethe 230: }
231:
232: #if the cflags option is set, pull out the compiler flags
1.4 ! espie 233: sub do_cflags
! 234: {
1.1 ckuethe 235: my %words; # store them as a hash to get de-duplicating
236: my @out;
237:
1.4 ! espie 238: foreach my $p (keys %configs) {
! 239: if ($configs{$p} =~ /\bCflags:\s+(.+?)\n/) {
! 240: foreach my $q (split /\s+/, $1) {
! 241: $words{$q}=1;
! 242: }
1.1 ckuethe 243: }
244: }
1.4 ! espie 245: foreach my $k (sort keys %words) {
! 246: push(@out, $k) if $k =~ /^-I/ && ($mode{'cflags'} & 1);
! 247: push(@out, $k) if $k =~ /^-[^I]/ && ($mode{'cflags'} & 2);
1.1 ckuethe 248: }
1.4 ! espie 249: print join(' ', @out), "\n";
! 250: return undef;
1.1 ckuethe 251: }
252:
253: #if the lib option is set, pull out the linker flags
1.4 ! espie 254: sub do_libs
! 255: {
1.1 ckuethe 256: my %words; # store them as a hash to get de-duplicating
257: my @out;
258:
1.4 ! espie 259: foreach my $p (keys %configs) {
! 260: if ($configs{$p} =~ /\bLibs:\s+(.+?)\n/) {
! 261: foreach my $q (split /\s+/, $1) {
! 262: $words{$q}=1;
! 263: }
1.1 ckuethe 264: }
265: }
1.4 ! espie 266: foreach my $k (sort keys %words) {
! 267: push(@out, $k) if $k =~ /^-l/ && ($mode{'libs'} & 1);
! 268: push(@out, $k) if $k =~ /^-L/ && ($mode{'libs'} & 2);
! 269: push(@out, $k) if $k =~ /^-[^lL]/ && ($mode{'libs'} & 4);
1.1 ckuethe 270: }
1.4 ! espie 271: print join(' ', @out), "\n";
! 272: return undef;
1.1 ckuethe 273: }
274:
275: #list all packages
1.4 ! espie 276: sub do_list
! 277: {
1.1 ckuethe 278: my ($p, $x, $y, @files, $fname, $name);
1.4 ! espie 279: foreach my $p (@PKGPATH) {
! 280: push(@files, <$p/*.pc>);
! 281: }
1.1 ckuethe 282:
283: # Scan the lengths of the package names so I can make a format
284: # string to line the list up just like the real pkgconfig does.
285: $x = 0;
1.4 ! espie 286: foreach my $f (@files) {
! 287: $fname = basename($f, '.pc');
! 288: $y = length $fname;
1.1 ckuethe 289: $x = (($y > $x) ? $y : $x);
290: }
291: $x *= -1;
292:
1.4 ! espie 293: foreach my $f (@files) {
! 294: $p = slurp($f);
! 295: $fname = basename($f, '.pc');
! 296: if ($p =~ /Name: (\w[^\n]+)\n/gm) {
1.1 ckuethe 297: $name = $1;
1.4 ! espie 298: if ($p =~ /Description:\s+(\w[^\n]+)\n/gm) {
1.1 ckuethe 299: printf("%${x}s %s - %s\n", $fname, $name, $1);
300: }
301: }
302: }
1.4 ! espie 303: exit 0;
1.1 ckuethe 304: }
305:
1.4 ! espie 306: sub help
! 307: {
1.1 ckuethe 308: print <<EOF
309: Usage: $0 [options]
310: --debug - turn on debugging output
311: --help - this message
312: --usage - this message
313: --list-all - show all packages that $0 can find
314: --atleast-pkgconfig-version [version] - require a certain version of pkgconfig
315: --variable var package - return the definition of <var> in <package>
316: --cflags package [versionspec] [package [versionspec]]
317: --cflags-only-I - only output -Iincludepath flags
318: --cflags-only-other - only output flags that are not -I
319: --libs package [versionspec] [package [versionspec]]
320: --libs-only-l - only output -llib flags
321: --libs-only-L - only output -Llibpath flags
322: --libs-only-other - only output flags that are not -l or -L
323: --exists package [versionspec] [package [versionspec]]
324: --uninstalled - allow for uninstalled versions to be used
325: EOF
326: ;
1.4 ! espie 327: exit 1;
1.1 ckuethe 328: }
329:
330: # do we meet/beat the version the caller requested?
1.4 ! espie 331: sub self_version
! 332: {
! 333: my ($v) = @_;
! 334: my (@a, @b);
! 335:
! 336: @a = split /\./, $v;
! 337: @b = split /\./, $version;
1.1 ckuethe 338:
1.4 ! espie 339: if (($b[0] >= $a[0]) && ($b[1] >= $a[1])) {
! 340: exit 0;
1.1 ckuethe 341: } else {
1.4 ! espie 342: exit 1;
1.1 ckuethe 343: }
344: }
345:
346: # got a package meeting the requested specific version?
1.4 ! espie 347: sub versionmatch
! 348: {
1.1 ckuethe 349: my ($pname, $op, $ver, undef) = @_;
350: my (@want, @inst, $m, $f);
351:
1.4 ! espie 352: print STDERR "pname = '$pname'\n" if $D;
1.1 ckuethe 353: # can't possibly match if we can't find the file
1.4 ! espie 354: return 0 unless $f = pathresolve($pname);
1.1 ckuethe 355: # load the file
356: $configs{$pname} = slurp($f);
357: # can't possibly match if we can't find the version string
1.4 ! espie 358: return 0 unless $configs{$pname} =~ /Version: ([0-9\.]+)\n/gm;
1.1 ckuethe 359:
1.4 ! espie 360: print "comparing $ver (wanted) to $1 (installed)\n" if $D;
! 361: @inst = split /\./, $1;
! 362: @want = split /\./, $ver;
1.1 ckuethe 363:
1.4 ! espie 364: while (@inst && @want) { #so long as both lists have something
1.1 ckuethe 365: # bail if the requested version element beats existing
1.4 ! espie 366: return 1 if $inst[0] > $want[0];
! 367: return 0 if $inst[0] < $want[0];
! 368: shift @inst; shift @want;
1.1 ckuethe 369: }
370: # the version at least equals the requested. if the requested
371: # version has some micropatchlevel beyond the existing version,
372: # return failure
1.4 ! espie 373: return 0 if @want;
1.1 ckuethe 374: # and after all that, the version is good enough
375: return 1;
376: }