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