Annotation of src/usr.bin/pkg-config/pkg-config, Revision 1.30
1.1 ckuethe 1: #!/usr/bin/perl
1.30 ! jasper 2: # $OpenBSD: pkg-config,v 1.29 2011/03/10 19:06:30 jasper 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:
1.27 jasper 25: my @PKGPATH = qw(/usr/lib/pkgconfig /usr/local/lib/pkgconfig /usr/X11R6/lib/pkgconfig);
1.1 ckuethe 26:
1.16 espie 27: if (defined($ENV{PKG_CONFIG_LIBDIR}) && $ENV{PKG_CONFIG_LIBDIR}) {
28: @PKGPATH = split /:/, $ENV{PKG_CONFIG_LIBDIR};
29: } elsif (defined($ENV{PKG_CONFIG_PATH}) && $ENV{PKG_CONFIG_PATH}) {
1.24 ckuethe 30: unshift(@PKGPATH, split /:/, $ENV{PKG_CONFIG_PATH});
1.1 ckuethe 31: }
32:
33: my $logfile = '';
1.16 espie 34: if (defined($ENV{PKG_CONFIG_LOGFILE}) && $ENV{PKG_CONFIG_LOGFILE}) {
35: $logfile = $ENV{PKG_CONFIG_LOGFILE};
1.1 ckuethe 36: }
37:
1.14 espie 38: my $allow_uninstalled =
1.16 espie 39: defined $ENV{PKG_CONFIG_DISABLE_UNINSTALLED} ? 0 : 1;
1.14 espie 40: my $found_uninstalled = 0;
41:
1.25 sthen 42: my $version = 0.22; # pretend to be this version of pkgconfig
1.10 espie 43:
44: my %configs = ();
45: my %mode = ();
1.11 espie 46: my $variables = {};
1.10 espie 47: my $D = 0; # debug flag
1.1 ckuethe 48:
1.14 espie 49: {
1.16 espie 50: my $d = $ENV{PKG_CONFIG_TOP_BUILD_DIR};
1.14 espie 51: if (defined $d) {
52: $variables->{pc_top_builddir} = $d;
53: } else {
54: $variables->{pc_top_builddir} = '$(top_builddir)';
55: }
56: }
1.29 jasper 57:
58: $D = 1 if defined($ENV{PKG_CONFIG_DEBUG_SPEW});
1.7 ckuethe 59:
1.4 espie 60: if ($logfile) {
61: open my $L, ">>" . $logfile;
62: print $L '[' . join('] [', $0, @ARGV) . "]\n";
63: close $L;
1.1 ckuethe 64: }
65:
66: # combo arg-parsing and dependency resolution loop. Hopefully when the loop
67: # terminates, we have a full list of packages upon which we depend, and the
68: # right set of compiler and linker flags to use them.
69: #
70: # as each .pc file is loaded, it is stored in %configs, indexed by package
71: # name. this makes it possible to then pull out flags or do substitutions
72: # without having to go back and reload the files from disk
73:
74: Getopt::Long::Configure('no_ignore_case');
75: GetOptions( 'debug' => \$D,
76: 'help' => \&help, #does not return
77: 'usage' => \&help, #does not return
1.14 espie 78: 'list-all' => \$mode{list},
1.1 ckuethe 79: 'version' => sub { print "$version\n" ; exit(0);} ,
1.11 espie 80: 'errors-to-stdout' => sub { $mode{estdout} = 1},
81: 'print-errors' => sub { $mode{printerr} = 1},
82: 'silence-errors' => sub { $mode{printerr} = 0},
1.23 jasper 83: 'short-errors' => sub { $mode{printerr} = 0},
1.14 espie 84: 'atleast-pkgconfig-version=s' => \$mode{myminvers},
1.30 ! jasper 85: 'print-provides' => \$mode{printprovides},
! 86: 'print-requires' => \$mode{printrequires},
! 87: 'print-requires-private' => \$mode{printrequiresprivate},
1.11 espie 88:
89: 'cflags' => sub { $mode{cflags} = 3},
90: 'cflags-only-I' => sub { $mode{cflags} |= 1},
91: 'cflags-only-other' => sub { $mode{cflags} |= 2},
92: 'libs' => sub { $mode{libs} = 7},
93: 'libs-only-l' => sub { $mode{libs} |= 1},
94: 'libs-only-L' => sub { $mode{libs} |= 2},
95: 'libs-only-other' => sub { $mode{libs} |= 4},
96: 'exists' => sub { $mode{exists} = 1} ,
97: 'static' => sub { $mode{static} = 1},
98: 'uninstalled' => sub { $mode{uninstalled} = 1},
1.14 espie 99: 'atleast-version=s' => \$mode{minversion},
100: 'exact-version=s' => \$mode{exactversion},
101: 'max-version=s' => \$mode{maxversion},
1.13 espie 102: 'modversion' => \$mode{modversion},
1.11 espie 103: 'variable=s' => \$mode{variable},
104: 'define-variable=s' => $variables,
1.1 ckuethe 105: );
106:
1.14 espie 107: # Initial value of printerr depends on the options...
108: if (!defined $mode{printerr}) {
109: if (defined $mode{libs} || defined $mode{cflags}
110: || defined $mode{version} || defined $mode{list}) {
111: $mode{printerr} = 1;
112: } else {
113: $mode{printerr} = 0;
114: }
115: }
116:
1.4 espie 117: print STDERR "\n[" . join('] [', $0, @ARGV) . "]\n" if $D;
1.13 espie 118:
119: my $rc = 0;
1.1 ckuethe 120:
1.14 espie 121: # XXX pkg-config is a bit weird
1.10 espie 122: {
123: my $p = join(' ', @ARGV);
1.14 espie 124: $p =~ s/^\s+//;
1.4 espie 125: @ARGV = split /\s+/, $p;
1.10 espie 126: }
1.1 ckuethe 127:
1.14 espie 128: if ($mode{myminvers}) {
129: exit self_version($mode{myminvers});
130: }
131:
132: if ($mode{list}) {
133: exit do_list();
1.1 ckuethe 134: }
135:
1.13 espie 136: my $cfg_full_list = [];
1.14 espie 137: my $top_config = [];
1.1 ckuethe 138:
139: while (@ARGV){
1.13 espie 140: my $p = shift @ARGV;
141: my $op = undef;
142: my $v = undef;
143: if (@ARGV >= 2 && $ARGV[0] =~ /[<=>]+/ &&
144: $ARGV[1] =~ /[0-9\.]+/) {
145: $op = shift @ARGV;
146: $v = shift @ARGV;
1.1 ckuethe 147: }
148: $p =~ s/,//g;
1.13 espie 149: handle_config($p, $op, $v, $cfg_full_list);
1.14 espie 150: push(@$top_config, $p);
151: }
152:
153: if ($mode{exists}) {
154: exit $rc;
155: }
156:
157: if ($mode{uninstalled}) {
158: $rc = 1 unless $found_uninstalled;
159: exit $rc;
1.11 espie 160: }
1.1 ckuethe 161:
1.30 ! jasper 162: if ($mode{modversion} || $mode{printprovides}) {
1.14 espie 163: for my $pkg (@$top_config) {
164: do_modversion($pkg);
165: }
166: }
1.13 espie 167:
1.30 ! jasper 168: if ($mode{printrequires} || $mode{printrequiresprivate}) {
! 169: for my $pkg (@$top_config) {
! 170: print_requires($pkg);
! 171: }
! 172: }
! 173:
1.14 espie 174: if ($mode{minversion}) {
175: my $v = $mode{minversion};
176: for my $pkg (@$top_config) {
177: $rc = 1 unless versionmatch($configs{$pkg}, '>=', $v);
178: }
179: exit $rc;
180: }
181:
182: if ($mode{exactversion}) {
183: my $v = $mode{exactversion};
184: for my $pkg (@$top_config) {
185: $rc = 1 unless versionmatch($configs{$pkg}, '=', $v);
186: }
187: exit $rc;
188: }
189:
190: if ($mode{minversion}) {
191: my $v = $mode{maxversion};
192: for my $pkg (@$top_config) {
193: $rc = 1 unless versionmatch($configs{$pkg}, '<=', $v);
194: }
195: exit $rc;
196: }
197:
198: my @vlist = ();
199:
200: if ($mode{variable}) {
201: for my $pkg (@$top_config) {
202: do_variable($pkg, $mode{variable});
203: }
204: }
205:
206: my $dep_cfg_list = simplify_and_reverse($cfg_full_list);
207:
208: if ($mode{cflags} || $mode{libs} || $mode{variable}) {
209: push @vlist, do_cflags($dep_cfg_list) if $mode{cflags};
210: push @vlist, do_libs($dep_cfg_list) if $mode{libs};
1.17 espie 211: print join(' ', @vlist), "\n" if $rc == 0;
1.1 ckuethe 212: }
213:
1.13 espie 214: exit $rc;
1.1 ckuethe 215:
216: ###########################################################################
217:
1.11 espie 218: sub handle_config
219: {
1.13 espie 220: my ($p, $op, $v, $list) = @_;
1.26 jasper 221: my $cfg;
1.13 espie 222:
1.26 jasper 223: # pkg-config won't install a pkg-config.pc file itself, but it may be
224: # listed as a dependency in other files.
225: # If we encounter a dependency on pkg-config, check if our version
226: # is sufficient and error out if not.
227: if ($p eq "pkg-config"){
228: if ($v > $version) {
229: print STDERR "pkg-config version $version too old, $v required.\n" if $D;
230: $rc = 1;
231: return undef;
232: }
233: } else {
234: $cfg = cache_find_config($p);
1.11 espie 235:
1.26 jasper 236: unshift @$list, $p if defined $cfg;
1.15 espie 237:
1.26 jasper 238: if (!defined $cfg) {
1.13 espie 239: $rc = 1;
240: return undef;
241: }
1.11 espie 242:
1.26 jasper 243: if (defined $op) {
244: if (!versionmatch($cfg, $op, $v)) {
245: mismatch($p, $cfg, $op, $v) if $mode{printerr};
246: $rc = 1;
247: return undef;
248: }
249: }
250:
251: my $deps = $cfg->get_property('Requires', $variables);
252: if (defined $deps) {
253: for my $dep (@$deps) {
254: if ($dep =~ m/^(.*?)\s*([<=>]+)\s*([\d\.]+)$/) {
255: handle_config($1, $2, $3, $list);
256: } else {
257: handle_config($dep, undef, undef, $list);
258: }
1.13 espie 259: }
1.26 jasper 260: print STDERR "package $p requires ",
261: join(',', @$deps), "\n" if $D;
1.13 espie 262: }
1.11 espie 263:
1.26 jasper 264: $deps = $cfg->get_property('Requires.private', $variables);
265: if (defined $deps) {
266: for my $dep (@$deps) {
267: if ($dep =~ m/^(.*?)\s*([<=>]+)\s*([\d\.]+)$/) {
268: handle_config($1, $2, $3, $list);
269: } else {
270: handle_config($dep, undef, undef, $list);
271: }
1.13 espie 272: }
1.26 jasper 273: print STDERR "package $p requires (private)",
274: join(',', @$deps), "\n" if $D;
1.13 espie 275: }
1.11 espie 276: }
277: }
278:
1.1 ckuethe 279: # look for the .pc file in each of the PKGPATH elements. Return the path or
280: # undef if it's not there
1.4 espie 281: sub pathresolve
282: {
283: my ($p) = @_;
284:
1.14 espie 285: if ($allow_uninstalled && $p !~ m/\-uninstalled$/) {
286: foreach my $d (@PKGPATH) {
287: my $f = "$d/$p-uninstalled.pc";
288: print STDERR "pathresolve($p) looking in $f\n" if $D;
289: if (-f $f) {
290: $found_uninstalled = 1;
291: return $f;
292: }
293: }
294: }
295:
1.4 espie 296: foreach my $d (@PKGPATH) {
1.10 espie 297: my $f = "$d/$p.pc";
1.4 espie 298: print STDERR "pathresolve($p) looking in $f\n" if $D;
1.10 espie 299: return $f if -f $f;
1.1 ckuethe 300: }
1.10 espie 301: return undef;
1.1 ckuethe 302: }
303:
1.11 espie 304: sub get_config
305: {
306: my ($f) = @_;
307:
308: my $cfg;
309: eval {
310: $cfg = OpenBSD::PkgConfig->read_file($f);
311: };
312: if (!$@) {
313: return $cfg;
314: } else {
1.12 espie 315: print STDERR $@, "\n" if $D;
1.11 espie 316: }
317: return undef;
318: }
319:
1.13 espie 320: sub cache_find_config
321: {
322: my $name = shift;
323:
324: print STDERR "processing $name\n" if $D;
325:
326: if (exists $configs{$name}) {
327: return $configs{$name};
328: } else {
329: return $configs{$name} = find_config($name);
330: }
331: }
332:
1.11 espie 333: sub find_config
334: {
335: my ($p) = @_;
336: my $f = pathresolve($p);
337: if (defined $f) {
338: return get_config($f);
339: }
1.13 espie 340: if ($mode{printerr}) {
341: print STDERR
342: "Package $p was not found in the pkg-config search path\n";
343: }
1.11 espie 344: return undef;
345: }
1.1 ckuethe 346:
1.11 espie 347: sub stringize
1.4 espie 348: {
1.11 espie 349: my $list = shift;
1.21 simon 350: my $sep = shift || ',';
1.4 espie 351:
1.11 espie 352: if (defined $list) {
1.21 simon 353: return join($sep, @$list)
1.11 espie 354: } else {
355: return '';
1.1 ckuethe 356: }
357: }
358:
359: #if the variable option is set, pull out the named variable
1.4 espie 360: sub do_variable
361: {
1.11 espie 362: my ($p, $v) = @_;
1.1 ckuethe 363:
1.13 espie 364: my $cfg = cache_find_config($p);
365:
366: if (defined $cfg) {
1.11 espie 367: my $value = $cfg->get_variable($v, $variables);
368: if (defined $value) {
1.13 espie 369: push(@vlist, $value);
1.11 espie 370: }
1.19 espie 371: return undef;
1.11 espie 372: }
1.19 espie 373: $rc = 1;
1.1 ckuethe 374: }
375:
1.30 ! jasper 376: #if the modversion or print-provides options are set,
! 377: #pull out the compiler flags
1.4 espie 378: sub do_modversion
379: {
1.11 espie 380: my ($p) = @_;
1.1 ckuethe 381:
1.13 espie 382: my $cfg = cache_find_config($p);
383:
384: if (defined $cfg) {
1.11 espie 385: my $value = $cfg->get_property('Version', $variables);
386: if (defined $value) {
1.30 ! jasper 387: if (!defined($mode{printprovides})){
! 388: print stringize($value), "\n";
! 389: return undef;
! 390: } else {
! 391: print "$p = " . stringize($value) . "\n";
! 392: return undef;
! 393: }
1.11 espie 394: }
395: }
1.13 espie 396: $rc = 1;
1.1 ckuethe 397: }
398:
399: #if the cflags option is set, pull out the compiler flags
1.4 espie 400: sub do_cflags
401: {
1.14 espie 402: my $list = shift;
403:
1.11 espie 404: my $cflags = [];
1.1 ckuethe 405:
1.14 espie 406: foreach my $pkg (@$list) {
1.11 espie 407: my $l = $configs{$pkg}->get_property('Cflags', $variables);
408: push(@$cflags, @$l) if defined $l;
409: }
410: return OpenBSD::PkgConfig->compress($cflags,
411: sub {
412: local $_ = shift;
413: if (($mode{cflags} & 1) && /^-I/ ||
414: ($mode{cflags} & 2) && !/^-I/) {
415: return 1;
416: } else {
417: return 0;
1.4 espie 418: }
1.11 espie 419: });
1.14 espie 420: return undef;
1.1 ckuethe 421: }
422:
423: #if the lib option is set, pull out the linker flags
1.4 espie 424: sub do_libs
425: {
1.14 espie 426: my $list = shift;
427:
1.11 espie 428: my $libs = [];
1.1 ckuethe 429:
1.14 espie 430: foreach my $pkg (@$list) {
1.11 espie 431: my $l = $configs{$pkg}->get_property('Libs', $variables);
432: push(@$libs, @$l) if defined $l;
433: }
1.13 espie 434: my $a = OpenBSD::PkgConfig->compress($libs,
1.11 espie 435: sub {
436: local $_ = shift;
1.13 espie 437: if (($mode{libs} & 2) && /^-L/ ||
1.11 espie 438: ($mode{libs} & 4) && !/^-[lL]/) {
439: return 1;
440: } else {
441: return 0;
1.4 espie 442: }
1.11 espie 443: });
1.13 espie 444: if ($mode{libs} & 1) {
445: my $b = OpenBSD::PkgConfig->rcompress($libs,
446: sub { shift =~ m/^-l/; });
447: return ($a, $b);
448: } else {
449: return $a;
450: }
1.1 ckuethe 451: }
452:
453: #list all packages
1.4 espie 454: sub do_list
455: {
1.1 ckuethe 456: my ($p, $x, $y, @files, $fname, $name);
1.20 espie 457: my $error = 0;
458:
1.4 espie 459: foreach my $p (@PKGPATH) {
460: push(@files, <$p/*.pc>);
461: }
1.1 ckuethe 462:
463: # Scan the lengths of the package names so I can make a format
464: # string to line the list up just like the real pkgconfig does.
465: $x = 0;
1.4 espie 466: foreach my $f (@files) {
467: $fname = basename($f, '.pc');
468: $y = length $fname;
1.1 ckuethe 469: $x = (($y > $x) ? $y : $x);
470: }
471: $x *= -1;
472:
1.4 espie 473: foreach my $f (@files) {
1.11 espie 474: my $cfg = get_config($f);
1.20 espie 475: if (!defined $cfg) {
476: print STDERR "Problem reading file $f\n";
477: $error = 1;
478: next;
479: }
1.4 espie 480: $fname = basename($f, '.pc');
1.11 espie 481: printf("%${x}s %s - %s\n", $fname,
482: stringize($cfg->get_property('Name', $variables)),
1.21 simon 483: stringize($cfg->get_property('Description', $variables),
484: ' '));
1.1 ckuethe 485: }
1.20 espie 486: return $error;
1.1 ckuethe 487: }
488:
1.4 espie 489: sub help
490: {
1.1 ckuethe 491: print <<EOF
492: Usage: $0 [options]
493: --debug - turn on debugging output
494: --help - this message
495: --usage - this message
496: --list-all - show all packages that $0 can find
1.8 ckuethe 497: --version - print version of pkgconfig
498: --errors-to-stdout - direct error messages to stdout rather than stderr
499: --print-errors - print error messages in case of error
500: --silence-errors - don't print error messages in case of error
1.1 ckuethe 501: --atleast-pkgconfig-version [version] - require a certain version of pkgconfig
502: --cflags package [versionspec] [package [versionspec]]
503: --cflags-only-I - only output -Iincludepath flags
504: --cflags-only-other - only output flags that are not -I
1.11 espie 505: --define-variable=NAME=VALUE - define variables
1.1 ckuethe 506: --libs package [versionspec] [package [versionspec]]
507: --libs-only-l - only output -llib flags
508: --libs-only-L - only output -Llibpath flags
509: --libs-only-other - only output flags that are not -l or -L
510: --exists package [versionspec] [package [versionspec]]
511: --uninstalled - allow for uninstalled versions to be used
1.8 ckuethe 512: --static - adjust output for static linking
513: --atleast-version [version] - require a certain version of a package
514: --modversion [package] - query the version of a package
515: --variable var package - return the definition of <var> in <package>
1.1 ckuethe 516: EOF
517: ;
1.22 simon 518: exit 0;
1.1 ckuethe 519: }
520:
521: # do we meet/beat the version the caller requested?
1.4 espie 522: sub self_version
523: {
524: my ($v) = @_;
525: my (@a, @b);
526:
527: @a = split /\./, $v;
528: @b = split /\./, $version;
1.1 ckuethe 529:
1.4 espie 530: if (($b[0] >= $a[0]) && ($b[1] >= $a[1])) {
1.14 espie 531: return 0;
1.1 ckuethe 532: } else {
1.14 espie 533: return 1;
534: }
535: }
536:
537: sub compare
538: {
539: my ($a, $b) = @_;
540:
1.28 jasper 541: return 0 if ($a eq $b);
1.14 espie 542:
543: my @a = split /\./, $a;
544: my @b = split /\./, $b;
545:
546: while (@a && @b) { #so long as both lists have something
547: return 1 if $a[0] > $b[0];
548: return -1 if $a[0] < $b[0];
549: shift @a; shift @b;
550: }
551: return 1 if @a;
552: return -1 if @b;
553: return 0;
1.1 ckuethe 554: }
555:
556: # got a package meeting the requested specific version?
1.4 espie 557: sub versionmatch
558: {
1.14 espie 559: my ($cfg, $op, $want) = @_;
1.1 ckuethe 560:
561: # can't possibly match if we can't find the file
1.11 espie 562: return 0 if !defined $cfg;
563:
1.14 espie 564: my $inst = stringize($cfg->get_property('Version', $variables));
1.11 espie 565:
1.1 ckuethe 566: # can't possibly match if we can't find the version string
1.14 espie 567: return 0 if $inst eq '';
1.1 ckuethe 568:
1.14 espie 569: print "comparing $want (wanted) to $inst (installed)\n" if $D;
570: my $value = compare($inst, $want);
571: if ($op eq '>=') {
572: return $value >= 0;
1.28 jasper 573: } elsif ($op eq '=') {
1.14 espie 574: return $value == 0;
575: } elsif ($op eq '!=') {
576: return $value != 0;
577: } elsif ($op eq '<') {
578: return $value < 0;
579: } elsif ($op eq '>') {
580: return $value > 0;
581: } elsif ($op eq '<=') {
582: return $value <= 0;
583: }
1.13 espie 584: }
585:
586: sub mismatch
587: {
588: my ($p, $cfg, $op, $v) = @_;
589: print STDERR "Requested '$p $op $v' but version of ",
590: stringize($cfg->get_property('Name')), " is ",
591: stringize($cfg->get_property('Version')), "\n";
592: }
593:
594: sub simplify_and_reverse
595: {
596: my $reqlist = shift;
597: my $dejavu = {};
598: my $result = [];
599:
600: for my $item (@$reqlist) {
601: if (!$dejavu->{$item}) {
602: unshift @$result, $item;
603: $dejavu->{$item} = 1;
604: }
605: }
606: return $result;
1.30 ! jasper 607: }
! 608:
! 609: # retrieve and print Requires(.private)
! 610: sub print_requires
! 611: {
! 612: my ($p) = @_;
! 613:
! 614: my $cfg = cache_find_config($p);
! 615:
! 616: if (defined($cfg)) {
! 617: my $value;
! 618:
! 619: if (defined($mode{printrequires})) {
! 620: $value = $cfg->get_property('Requires', $variables);
! 621: } elsif (defined($mode{printrequiresprivate})) {
! 622: $value = $cfg->get_property('Requires.private', $variables);
! 623: } else {
! 624: print STDERR "Unknown mode for print_requires.\n" if $D;
! 625: return 1;
! 626: }
! 627:
! 628: if (defined($value)) {
! 629: print "$_\n" foreach (@$value);
! 630: return undef;
! 631: }
! 632: }
! 633:
! 634: $rc = 1;
1.1 ckuethe 635: }