Annotation of src/usr.bin/pkg-config/pkg-config, Revision 1.13
1.1 ckuethe 1: #!/usr/bin/perl
1.13 ! espie 2: # $OpenBSD: pkg-config,v 1.12 2006/12/02 18:58:46 espie Exp $
1.1 ckuethe 3:
4: #$CSK: pkgconfig.pl,v 1.39 2006/11/27 16:26:20 ckuethe Exp $
5: # Copyright (c) 2006 Chris Kuethe <ckuethe@openbsd.org>
6: #
7: # Permission to use, copy, modify, and distribute this software for any
8: # purpose with or without fee is hereby granted, provided that the above
9: # copyright notice and this permission notice appear in all copies.
10: #
11: # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12: # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13: # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14: # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18:
19: use strict;
20: use warnings;
21: use Getopt::Long;
1.4 espie 22: use File::Basename;
1.11 espie 23: use OpenBSD::PkgConfig;
1.1 ckuethe 24:
25: my @PKGPATH = qw(/usr/local/lib/pkgconfig /usr/X11R6/lib/pkgconfig );
26:
1.4 espie 27: if (defined($ENV{'PKG_CONFIG_PATH'}) && $ENV{'PKG_CONFIG_PATH'}) {
28: push(@PKGPATH, split /:/, $ENV{'PKG_CONFIG_PATH'});
1.1 ckuethe 29: }
30:
31: my $logfile = '';
1.4 espie 32: if (defined($ENV{'PKG_CONFIG_LOGFILE'}) && $ENV{'PKG_CONFIG_LOGFILE'}) {
1.1 ckuethe 33: $logfile = $ENV{'PKG_CONFIG_LOGFILE'};
34: }
35:
1.10 espie 36: my $version = 0.19; # pretend to be this version of pkgconfig
37:
38: my %configs = ();
39: my %mode = ();
1.11 espie 40: my $variables = {};
1.10 espie 41: my $D = 0; # debug flag
1.1 ckuethe 42:
1.7 ckuethe 43: # defaults
1.11 espie 44: $mode{printerr} = 1;
1.7 ckuethe 45:
1.4 espie 46: if ($logfile) {
47: open my $L, ">>" . $logfile;
48: print $L '[' . join('] [', $0, @ARGV) . "]\n";
49: close $L;
1.1 ckuethe 50: }
51:
52: # combo arg-parsing and dependency resolution loop. Hopefully when the loop
53: # terminates, we have a full list of packages upon which we depend, and the
54: # right set of compiler and linker flags to use them.
55: #
56: # as each .pc file is loaded, it is stored in %configs, indexed by package
57: # name. this makes it possible to then pull out flags or do substitutions
58: # without having to go back and reload the files from disk
59:
60: Getopt::Long::Configure('no_ignore_case');
61: GetOptions( 'debug' => \$D,
62: 'help' => \&help, #does not return
63: 'usage' => \&help, #does not return
64: 'list-all' => \&do_list, #does not return
65: 'version' => sub { print "$version\n" ; exit(0);} ,
1.11 espie 66: 'errors-to-stdout' => sub { $mode{estdout} = 1},
67: 'print-errors' => sub { $mode{printerr} = 1},
68: 'silence-errors' => sub { $mode{printerr} = 0},
69: 'atleast-pkgconfig-version=s' => \$mode{minvers},
70:
71: 'cflags' => sub { $mode{cflags} = 3},
72: 'cflags-only-I' => sub { $mode{cflags} |= 1},
73: 'cflags-only-other' => sub { $mode{cflags} |= 2},
74: 'libs' => sub { $mode{libs} = 7},
75: 'libs-only-l' => sub { $mode{libs} |= 1},
76: 'libs-only-L' => sub { $mode{libs} |= 2},
77: 'libs-only-other' => sub { $mode{libs} |= 4},
78: 'exists' => sub { $mode{exists} = 1} ,
79: 'static' => sub { $mode{static} = 1},
80: 'uninstalled' => sub { $mode{uninstalled} = 1},
1.1 ckuethe 81: 'atleast-version=s' => \$mode{'atleast-version'},
1.13 ! espie 82: 'modversion' => \$mode{modversion},
1.11 espie 83: 'variable=s' => \$mode{variable},
84: 'define-variable=s' => $variables,
1.1 ckuethe 85: );
86:
1.4 espie 87: print STDERR "\n[" . join('] [', $0, @ARGV) . "]\n" if $D;
1.11 espie 88: self_version($mode{minvers}) if $mode{minvers}; #does not return
1.13 ! espie 89:
! 90: my $rc = 0;
1.1 ckuethe 91:
1.10 espie 92: {
93: my $p = join(' ', @ARGV);
1.1 ckuethe 94: $p =~ s/\s+/ /g;
95: $p =~ s/^\s//g;
1.4 espie 96: @ARGV = split /\s+/, $p;
1.10 espie 97: }
1.1 ckuethe 98:
1.11 espie 99: if (defined $mode{exists}) {
1.4 espie 100: while (@ARGV) {
1.13 ! espie 101: my $p = shift @ARGV;
! 102: my $cfg = cache_find_config($p);
! 103: exit 1 if !defined $cfg;
! 104: if (@ARGV >= 2 && $ARGV[0] =~ /[<=>]+/ &&
! 105: $ARGV[1] =~ /[0-9\.]+/) {
! 106: $rc = 1 unless versionmatch($cfg, @ARGV);
! 107: shift @ARGV; shift @ARGV;
1.1 ckuethe 108: }
109: }
1.13 ! espie 110: exit $rc;
1.1 ckuethe 111: }
112:
1.13 ! espie 113: my $cfg_full_list = [];
! 114: my @vlist = ();
1.1 ckuethe 115:
116: while (@ARGV){
1.13 ! espie 117: my $p = shift @ARGV;
! 118: my $op = undef;
! 119: my $v = undef;
! 120: if (@ARGV >= 2 && $ARGV[0] =~ /[<=>]+/ &&
! 121: $ARGV[1] =~ /[0-9\.]+/) {
! 122: $op = shift @ARGV;
! 123: $v = shift @ARGV;
1.1 ckuethe 124: }
125: $p =~ s/,//g;
1.13 ! espie 126: handle_config($p, $op, $v, $cfg_full_list);
! 127: do_modversion($p) if defined $mode{modversion};
! 128: do_variable($p, $mode{variable}) if $mode{variable};
1.11 espie 129: }
1.1 ckuethe 130:
1.13 ! espie 131: my $cfg_list = simplify_and_reverse($cfg_full_list);
! 132:
! 133: if ($mode{cflags} || $mode{libs}|| $mode{variable}) {
! 134: push @vlist, do_cflags() if $mode{cflags};
! 135: push @vlist, do_libs() if $mode{libs};
! 136: print join(' ', @vlist), "\n";
1.1 ckuethe 137: }
138:
1.13 ! espie 139: exit $rc;
1.1 ckuethe 140:
141: ###########################################################################
142:
1.11 espie 143: sub handle_config
144: {
1.13 ! espie 145: my ($p, $op, $v, $list) = @_;
! 146:
1.11 espie 147:
1.13 ! espie 148: my $cfg = cache_find_config($p);
1.11 espie 149:
1.13 ! espie 150: unshift @$list, $p if defined $cfg;
1.11 espie 151:
1.13 ! espie 152: if (defined $op) {
! 153: if (!versionmatch($cfg, $op, $v)) {
! 154: mismatch($p, $cfg, $op, $v) if $mode{printerr};
! 155: $rc = 1;
! 156: return undef;
! 157: }
1.11 espie 158: }
159:
1.13 ! espie 160: return undef if !defined $cfg;
1.11 espie 161:
162: my $deps = $cfg->get_property('Requires', $variables);
163: if (defined $deps) {
1.13 ! espie 164: for my $dep (@$deps) {
! 165: if ($dep =~ m/^(.*?)\s*([<=>]+)\s*([\d\.]+)$/) {
! 166: handle_config($1, $2, $3, $list);
! 167: } else {
! 168: handle_config($dep, undef, undef, $list);
! 169: }
! 170: }
1.11 espie 171: print STDERR "package $p requires ",
172: join(',', @$deps), "\n" if $D;
173: }
174:
175: $deps = $cfg->get_property('Requires.private', $variables);
176: if (defined $deps) {
1.13 ! espie 177: for my $dep (@$deps) {
! 178: if ($dep =~ m/^(.*?)\s*([<=>]+)\s*([\d\.]+)$/) {
! 179: handle_config($1, $2, $3, $list);
! 180: } else {
! 181: handle_config($dep, undef, undef, $list);
! 182: }
! 183: }
1.11 espie 184: print STDERR "package $p requires (private)",
185: join(',', @$deps), "\n" if $D;
186: }
187: }
188:
1.1 ckuethe 189: # look for the .pc file in each of the PKGPATH elements. Return the path or
190: # undef if it's not there
1.4 espie 191: sub pathresolve
192: {
193: my ($p) = @_;
194:
195: foreach my $d (@PKGPATH) {
1.10 espie 196: my $f = "$d/$p.pc";
1.4 espie 197: print STDERR "pathresolve($p) looking in $f\n" if $D;
1.10 espie 198: return $f if -f $f;
1.1 ckuethe 199: }
1.10 espie 200: return undef;
1.1 ckuethe 201: }
202:
1.11 espie 203: sub get_config
204: {
205: my ($f) = @_;
206:
207: my $cfg;
208: eval {
209: $cfg = OpenBSD::PkgConfig->read_file($f);
210: };
211: if (!$@) {
212: return $cfg;
213: } else {
1.12 espie 214: print STDERR $@, "\n" if $D;
1.11 espie 215: }
216: return undef;
217: }
218:
1.13 ! espie 219: sub cache_find_config
! 220: {
! 221: my $name = shift;
! 222:
! 223: print STDERR "processing $name\n" if $D;
! 224:
! 225: if (exists $configs{$name}) {
! 226: return $configs{$name};
! 227: } else {
! 228: return $configs{$name} = find_config($name);
! 229: }
! 230: }
! 231:
1.11 espie 232: sub find_config
233: {
234: my ($p) = @_;
235: my $f = pathresolve($p);
236: if (defined $f) {
237: return get_config($f);
238: }
1.13 ! espie 239: if ($mode{printerr}) {
! 240: print STDERR
! 241: "Package $p was not found in the pkg-config search path\n";
! 242: }
1.11 espie 243: return undef;
244: }
1.1 ckuethe 245:
1.11 espie 246: sub stringize
1.4 espie 247: {
1.11 espie 248: my $list = shift;
1.4 espie 249:
1.11 espie 250: if (defined $list) {
251: return join(',', @$list)
252: } else {
253: return '';
1.1 ckuethe 254: }
255: }
256:
257: #if the variable option is set, pull out the named variable
1.4 espie 258: sub do_variable
259: {
1.11 espie 260: my ($p, $v) = @_;
1.1 ckuethe 261:
1.13 ! espie 262: my $cfg = cache_find_config($p);
! 263:
! 264: if (defined $cfg) {
1.11 espie 265: my $value = $cfg->get_variable($v, $variables);
266: if (defined $value) {
1.13 ! espie 267: push(@vlist, $value);
! 268: return undef;
1.11 espie 269: }
270: }
1.13 ! espie 271: $rc = 1;
1.1 ckuethe 272: }
273:
274: #if the modversion option is set, pull out the compiler flags
1.4 espie 275: sub do_modversion
276: {
1.11 espie 277: my ($p) = @_;
1.1 ckuethe 278:
1.13 ! espie 279: my $cfg = cache_find_config($p);
! 280:
! 281: if (defined $cfg) {
1.11 espie 282: my $value = $cfg->get_property('Version', $variables);
283: if (defined $value) {
284: print stringize($value), "\n";
1.13 ! espie 285: return undef;
1.11 espie 286: }
287: }
1.13 ! espie 288: $rc = 1;
1.1 ckuethe 289: }
290:
291: #if the cflags option is set, pull out the compiler flags
1.4 espie 292: sub do_cflags
293: {
1.11 espie 294: my $cflags = [];
1.1 ckuethe 295:
1.11 espie 296: foreach my $pkg (@$cfg_list) {
297: my $l = $configs{$pkg}->get_property('Cflags', $variables);
298: push(@$cflags, @$l) if defined $l;
299: }
300: return OpenBSD::PkgConfig->compress($cflags,
301: sub {
302: local $_ = shift;
303: if (($mode{cflags} & 1) && /^-I/ ||
304: ($mode{cflags} & 2) && !/^-I/) {
305: return 1;
306: } else {
307: return 0;
1.4 espie 308: }
1.11 espie 309: });
1.1 ckuethe 310: }
311:
312: #if the lib option is set, pull out the linker flags
1.4 espie 313: sub do_libs
314: {
1.11 espie 315: my $libs = [];
1.1 ckuethe 316:
1.11 espie 317: foreach my $pkg (@$cfg_list) {
318: my $l = $configs{$pkg}->get_property('Libs', $variables);
319: push(@$libs, @$l) if defined $l;
320: }
1.13 ! espie 321: my $a = OpenBSD::PkgConfig->compress($libs,
1.11 espie 322: sub {
323: local $_ = shift;
1.13 ! espie 324: if (($mode{libs} & 2) && /^-L/ ||
1.11 espie 325: ($mode{libs} & 4) && !/^-[lL]/) {
326: return 1;
327: } else {
328: return 0;
1.4 espie 329: }
1.11 espie 330: });
1.13 ! espie 331: if ($mode{libs} & 1) {
! 332: my $b = OpenBSD::PkgConfig->rcompress($libs,
! 333: sub { shift =~ m/^-l/; });
! 334: return ($a, $b);
! 335: } else {
! 336: return $a;
! 337: }
1.1 ckuethe 338: }
339:
340: #list all packages
1.4 espie 341: sub do_list
342: {
1.1 ckuethe 343: my ($p, $x, $y, @files, $fname, $name);
1.4 espie 344: foreach my $p (@PKGPATH) {
345: push(@files, <$p/*.pc>);
346: }
1.1 ckuethe 347:
348: # Scan the lengths of the package names so I can make a format
349: # string to line the list up just like the real pkgconfig does.
350: $x = 0;
1.4 espie 351: foreach my $f (@files) {
352: $fname = basename($f, '.pc');
353: $y = length $fname;
1.1 ckuethe 354: $x = (($y > $x) ? $y : $x);
355: }
356: $x *= -1;
357:
1.4 espie 358: foreach my $f (@files) {
1.11 espie 359: my $cfg = get_config($f);
1.4 espie 360: $fname = basename($f, '.pc');
1.11 espie 361: printf("%${x}s %s - %s\n", $fname,
362: stringize($cfg->get_property('Name', $variables)),
363: stringize($cfg->get_property('Description', $variables)));
1.1 ckuethe 364: }
1.4 espie 365: exit 0;
1.1 ckuethe 366: }
367:
1.4 espie 368: sub help
369: {
1.1 ckuethe 370: print <<EOF
371: Usage: $0 [options]
372: --debug - turn on debugging output
373: --help - this message
374: --usage - this message
375: --list-all - show all packages that $0 can find
1.8 ckuethe 376: --version - print version of pkgconfig
377: --errors-to-stdout - direct error messages to stdout rather than stderr
378: --print-errors - print error messages in case of error
379: --silence-errors - don't print error messages in case of error
1.1 ckuethe 380: --atleast-pkgconfig-version [version] - require a certain version of pkgconfig
381: --cflags package [versionspec] [package [versionspec]]
382: --cflags-only-I - only output -Iincludepath flags
383: --cflags-only-other - only output flags that are not -I
1.11 espie 384: --define-variable=NAME=VALUE - define variables
1.1 ckuethe 385: --libs package [versionspec] [package [versionspec]]
386: --libs-only-l - only output -llib flags
387: --libs-only-L - only output -Llibpath flags
388: --libs-only-other - only output flags that are not -l or -L
389: --exists package [versionspec] [package [versionspec]]
390: --uninstalled - allow for uninstalled versions to be used
1.8 ckuethe 391: --static - adjust output for static linking
392: --atleast-version [version] - require a certain version of a package
393: --modversion [package] - query the version of a package
394: --variable var package - return the definition of <var> in <package>
1.1 ckuethe 395: EOF
396: ;
1.4 espie 397: exit 1;
1.1 ckuethe 398: }
399:
400: # do we meet/beat the version the caller requested?
1.4 espie 401: sub self_version
402: {
403: my ($v) = @_;
404: my (@a, @b);
405:
406: @a = split /\./, $v;
407: @b = split /\./, $version;
1.1 ckuethe 408:
1.4 espie 409: if (($b[0] >= $a[0]) && ($b[1] >= $a[1])) {
410: exit 0;
1.1 ckuethe 411: } else {
1.4 espie 412: exit 1;
1.1 ckuethe 413: }
414: }
415:
416: # got a package meeting the requested specific version?
1.4 espie 417: sub versionmatch
418: {
1.13 ! espie 419: my ($cfg, $op, $ver) = @_;
1.1 ckuethe 420:
1.13 ! espie 421: # XXX assumes op is >= for now.
! 422:
1.1 ckuethe 423: # can't possibly match if we can't find the file
1.11 espie 424: return 0 if !defined $cfg;
425:
426: my $v = stringize($cfg->get_property('Version', $variables));
427:
1.1 ckuethe 428: # can't possibly match if we can't find the version string
1.11 espie 429: return 0 if $v eq '';
1.1 ckuethe 430:
1.11 espie 431: print "comparing $ver (wanted) to $v (installed)\n" if $D;
432: my @inst = split /\./, $v;
433: my @want = split /\./, $ver;
1.1 ckuethe 434:
1.4 espie 435: while (@inst && @want) { #so long as both lists have something
1.1 ckuethe 436: # bail if the requested version element beats existing
1.4 espie 437: return 1 if $inst[0] > $want[0];
438: return 0 if $inst[0] < $want[0];
439: shift @inst; shift @want;
1.1 ckuethe 440: }
441: # the version at least equals the requested. if the requested
442: # version has some micropatchlevel beyond the existing version,
443: # return failure
1.4 espie 444: return 0 if @want;
1.1 ckuethe 445: # and after all that, the version is good enough
446: return 1;
1.13 ! espie 447: }
! 448:
! 449: sub mismatch
! 450: {
! 451: my ($p, $cfg, $op, $v) = @_;
! 452: print STDERR "Requested '$p $op $v' but version of ",
! 453: stringize($cfg->get_property('Name')), " is ",
! 454: stringize($cfg->get_property('Version')), "\n";
! 455: }
! 456:
! 457: sub simplify_and_reverse
! 458: {
! 459: my $reqlist = shift;
! 460: my $dejavu = {};
! 461: my $result = [];
! 462:
! 463: for my $item (@$reqlist) {
! 464: if (!$dejavu->{$item}) {
! 465: unshift @$result, $item;
! 466: $dejavu->{$item} = 1;
! 467: }
! 468: }
! 469: return $result;
1.1 ckuethe 470: }