Annotation of src/usr.bin/pkg-config/pkg-config, Revision 1.28
1.1 ckuethe 1: #!/usr/bin/perl
1.28 ! jasper 2: # $OpenBSD: pkg-config,v 1.27 2010/12/28 14:31:38 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.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) = @_;
1.26 jasper 210: my $cfg;
1.13 espie 211:
1.26 jasper 212: # pkg-config won't install a pkg-config.pc file itself, but it may be
213: # listed as a dependency in other files.
214: # If we encounter a dependency on pkg-config, check if our version
215: # is sufficient and error out if not.
216: if ($p eq "pkg-config"){
217: if ($v > $version) {
218: print STDERR "pkg-config version $version too old, $v required.\n" if $D;
219: $rc = 1;
220: return undef;
221: }
222: } else {
223: $cfg = cache_find_config($p);
1.11 espie 224:
1.26 jasper 225: unshift @$list, $p if defined $cfg;
1.15 espie 226:
1.26 jasper 227: if (!defined $cfg) {
1.13 espie 228: $rc = 1;
229: return undef;
230: }
1.11 espie 231:
1.26 jasper 232: if (defined $op) {
233: if (!versionmatch($cfg, $op, $v)) {
234: mismatch($p, $cfg, $op, $v) if $mode{printerr};
235: $rc = 1;
236: return undef;
237: }
238: }
239:
240: my $deps = $cfg->get_property('Requires', $variables);
241: if (defined $deps) {
242: for my $dep (@$deps) {
243: if ($dep =~ m/^(.*?)\s*([<=>]+)\s*([\d\.]+)$/) {
244: handle_config($1, $2, $3, $list);
245: } else {
246: handle_config($dep, undef, undef, $list);
247: }
1.13 espie 248: }
1.26 jasper 249: print STDERR "package $p requires ",
250: join(',', @$deps), "\n" if $D;
1.13 espie 251: }
1.11 espie 252:
1.26 jasper 253: $deps = $cfg->get_property('Requires.private', $variables);
254: if (defined $deps) {
255: for my $dep (@$deps) {
256: if ($dep =~ m/^(.*?)\s*([<=>]+)\s*([\d\.]+)$/) {
257: handle_config($1, $2, $3, $list);
258: } else {
259: handle_config($dep, undef, undef, $list);
260: }
1.13 espie 261: }
1.26 jasper 262: print STDERR "package $p requires (private)",
263: join(',', @$deps), "\n" if $D;
1.13 espie 264: }
1.11 espie 265: }
266: }
267:
1.1 ckuethe 268: # look for the .pc file in each of the PKGPATH elements. Return the path or
269: # undef if it's not there
1.4 espie 270: sub pathresolve
271: {
272: my ($p) = @_;
273:
1.14 espie 274: if ($allow_uninstalled && $p !~ m/\-uninstalled$/) {
275: foreach my $d (@PKGPATH) {
276: my $f = "$d/$p-uninstalled.pc";
277: print STDERR "pathresolve($p) looking in $f\n" if $D;
278: if (-f $f) {
279: $found_uninstalled = 1;
280: return $f;
281: }
282: }
283: }
284:
1.4 espie 285: foreach my $d (@PKGPATH) {
1.10 espie 286: my $f = "$d/$p.pc";
1.4 espie 287: print STDERR "pathresolve($p) looking in $f\n" if $D;
1.10 espie 288: return $f if -f $f;
1.1 ckuethe 289: }
1.10 espie 290: return undef;
1.1 ckuethe 291: }
292:
1.11 espie 293: sub get_config
294: {
295: my ($f) = @_;
296:
297: my $cfg;
298: eval {
299: $cfg = OpenBSD::PkgConfig->read_file($f);
300: };
301: if (!$@) {
302: return $cfg;
303: } else {
1.12 espie 304: print STDERR $@, "\n" if $D;
1.11 espie 305: }
306: return undef;
307: }
308:
1.13 espie 309: sub cache_find_config
310: {
311: my $name = shift;
312:
313: print STDERR "processing $name\n" if $D;
314:
315: if (exists $configs{$name}) {
316: return $configs{$name};
317: } else {
318: return $configs{$name} = find_config($name);
319: }
320: }
321:
1.11 espie 322: sub find_config
323: {
324: my ($p) = @_;
325: my $f = pathresolve($p);
326: if (defined $f) {
327: return get_config($f);
328: }
1.13 espie 329: if ($mode{printerr}) {
330: print STDERR
331: "Package $p was not found in the pkg-config search path\n";
332: }
1.11 espie 333: return undef;
334: }
1.1 ckuethe 335:
1.11 espie 336: sub stringize
1.4 espie 337: {
1.11 espie 338: my $list = shift;
1.21 simon 339: my $sep = shift || ',';
1.4 espie 340:
1.11 espie 341: if (defined $list) {
1.21 simon 342: return join($sep, @$list)
1.11 espie 343: } else {
344: return '';
1.1 ckuethe 345: }
346: }
347:
348: #if the variable option is set, pull out the named variable
1.4 espie 349: sub do_variable
350: {
1.11 espie 351: my ($p, $v) = @_;
1.1 ckuethe 352:
1.13 espie 353: my $cfg = cache_find_config($p);
354:
355: if (defined $cfg) {
1.11 espie 356: my $value = $cfg->get_variable($v, $variables);
357: if (defined $value) {
1.13 espie 358: push(@vlist, $value);
1.11 espie 359: }
1.19 espie 360: return undef;
1.11 espie 361: }
1.19 espie 362: $rc = 1;
1.1 ckuethe 363: }
364:
365: #if the modversion option is set, pull out the compiler flags
1.4 espie 366: sub do_modversion
367: {
1.11 espie 368: my ($p) = @_;
1.1 ckuethe 369:
1.13 espie 370: my $cfg = cache_find_config($p);
371:
372: if (defined $cfg) {
1.11 espie 373: my $value = $cfg->get_property('Version', $variables);
374: if (defined $value) {
375: print stringize($value), "\n";
1.13 espie 376: return undef;
1.11 espie 377: }
378: }
1.13 espie 379: $rc = 1;
1.1 ckuethe 380: }
381:
382: #if the cflags option is set, pull out the compiler flags
1.4 espie 383: sub do_cflags
384: {
1.14 espie 385: my $list = shift;
386:
1.11 espie 387: my $cflags = [];
1.1 ckuethe 388:
1.14 espie 389: foreach my $pkg (@$list) {
1.11 espie 390: my $l = $configs{$pkg}->get_property('Cflags', $variables);
391: push(@$cflags, @$l) if defined $l;
392: }
393: return OpenBSD::PkgConfig->compress($cflags,
394: sub {
395: local $_ = shift;
396: if (($mode{cflags} & 1) && /^-I/ ||
397: ($mode{cflags} & 2) && !/^-I/) {
398: return 1;
399: } else {
400: return 0;
1.4 espie 401: }
1.11 espie 402: });
1.14 espie 403: return undef;
1.1 ckuethe 404: }
405:
406: #if the lib option is set, pull out the linker flags
1.4 espie 407: sub do_libs
408: {
1.14 espie 409: my $list = shift;
410:
1.11 espie 411: my $libs = [];
1.1 ckuethe 412:
1.14 espie 413: foreach my $pkg (@$list) {
1.11 espie 414: my $l = $configs{$pkg}->get_property('Libs', $variables);
415: push(@$libs, @$l) if defined $l;
416: }
1.13 espie 417: my $a = OpenBSD::PkgConfig->compress($libs,
1.11 espie 418: sub {
419: local $_ = shift;
1.13 espie 420: if (($mode{libs} & 2) && /^-L/ ||
1.11 espie 421: ($mode{libs} & 4) && !/^-[lL]/) {
422: return 1;
423: } else {
424: return 0;
1.4 espie 425: }
1.11 espie 426: });
1.13 espie 427: if ($mode{libs} & 1) {
428: my $b = OpenBSD::PkgConfig->rcompress($libs,
429: sub { shift =~ m/^-l/; });
430: return ($a, $b);
431: } else {
432: return $a;
433: }
1.1 ckuethe 434: }
435:
436: #list all packages
1.4 espie 437: sub do_list
438: {
1.1 ckuethe 439: my ($p, $x, $y, @files, $fname, $name);
1.20 espie 440: my $error = 0;
441:
1.4 espie 442: foreach my $p (@PKGPATH) {
443: push(@files, <$p/*.pc>);
444: }
1.1 ckuethe 445:
446: # Scan the lengths of the package names so I can make a format
447: # string to line the list up just like the real pkgconfig does.
448: $x = 0;
1.4 espie 449: foreach my $f (@files) {
450: $fname = basename($f, '.pc');
451: $y = length $fname;
1.1 ckuethe 452: $x = (($y > $x) ? $y : $x);
453: }
454: $x *= -1;
455:
1.4 espie 456: foreach my $f (@files) {
1.11 espie 457: my $cfg = get_config($f);
1.20 espie 458: if (!defined $cfg) {
459: print STDERR "Problem reading file $f\n";
460: $error = 1;
461: next;
462: }
1.4 espie 463: $fname = basename($f, '.pc');
1.11 espie 464: printf("%${x}s %s - %s\n", $fname,
465: stringize($cfg->get_property('Name', $variables)),
1.21 simon 466: stringize($cfg->get_property('Description', $variables),
467: ' '));
1.1 ckuethe 468: }
1.20 espie 469: return $error;
1.1 ckuethe 470: }
471:
1.4 espie 472: sub help
473: {
1.1 ckuethe 474: print <<EOF
475: Usage: $0 [options]
476: --debug - turn on debugging output
477: --help - this message
478: --usage - this message
479: --list-all - show all packages that $0 can find
1.8 ckuethe 480: --version - print version of pkgconfig
481: --errors-to-stdout - direct error messages to stdout rather than stderr
482: --print-errors - print error messages in case of error
483: --silence-errors - don't print error messages in case of error
1.1 ckuethe 484: --atleast-pkgconfig-version [version] - require a certain version of pkgconfig
485: --cflags package [versionspec] [package [versionspec]]
486: --cflags-only-I - only output -Iincludepath flags
487: --cflags-only-other - only output flags that are not -I
1.11 espie 488: --define-variable=NAME=VALUE - define variables
1.1 ckuethe 489: --libs package [versionspec] [package [versionspec]]
490: --libs-only-l - only output -llib flags
491: --libs-only-L - only output -Llibpath flags
492: --libs-only-other - only output flags that are not -l or -L
493: --exists package [versionspec] [package [versionspec]]
494: --uninstalled - allow for uninstalled versions to be used
1.8 ckuethe 495: --static - adjust output for static linking
496: --atleast-version [version] - require a certain version of a package
497: --modversion [package] - query the version of a package
498: --variable var package - return the definition of <var> in <package>
1.1 ckuethe 499: EOF
500: ;
1.22 simon 501: exit 0;
1.1 ckuethe 502: }
503:
504: # do we meet/beat the version the caller requested?
1.4 espie 505: sub self_version
506: {
507: my ($v) = @_;
508: my (@a, @b);
509:
510: @a = split /\./, $v;
511: @b = split /\./, $version;
1.1 ckuethe 512:
1.4 espie 513: if (($b[0] >= $a[0]) && ($b[1] >= $a[1])) {
1.14 espie 514: return 0;
1.1 ckuethe 515: } else {
1.14 espie 516: return 1;
517: }
518: }
519:
520: sub compare
521: {
522: my ($a, $b) = @_;
523:
1.28 ! jasper 524: return 0 if ($a eq $b);
1.14 espie 525:
526: my @a = split /\./, $a;
527: my @b = split /\./, $b;
528:
529: while (@a && @b) { #so long as both lists have something
530: return 1 if $a[0] > $b[0];
531: return -1 if $a[0] < $b[0];
532: shift @a; shift @b;
533: }
534: return 1 if @a;
535: return -1 if @b;
536: return 0;
1.1 ckuethe 537: }
538:
539: # got a package meeting the requested specific version?
1.4 espie 540: sub versionmatch
541: {
1.14 espie 542: my ($cfg, $op, $want) = @_;
1.1 ckuethe 543:
544: # can't possibly match if we can't find the file
1.11 espie 545: return 0 if !defined $cfg;
546:
1.14 espie 547: my $inst = stringize($cfg->get_property('Version', $variables));
1.11 espie 548:
1.1 ckuethe 549: # can't possibly match if we can't find the version string
1.14 espie 550: return 0 if $inst eq '';
1.1 ckuethe 551:
1.14 espie 552: print "comparing $want (wanted) to $inst (installed)\n" if $D;
553: my $value = compare($inst, $want);
554: if ($op eq '>=') {
555: return $value >= 0;
1.28 ! jasper 556: } elsif ($op eq '=') {
1.14 espie 557: return $value == 0;
558: } elsif ($op eq '!=') {
559: return $value != 0;
560: } elsif ($op eq '<') {
561: return $value < 0;
562: } elsif ($op eq '>') {
563: return $value > 0;
564: } elsif ($op eq '<=') {
565: return $value <= 0;
566: }
1.13 espie 567: }
568:
569: sub mismatch
570: {
571: my ($p, $cfg, $op, $v) = @_;
572: print STDERR "Requested '$p $op $v' but version of ",
573: stringize($cfg->get_property('Name')), " is ",
574: stringize($cfg->get_property('Version')), "\n";
575: }
576:
577: sub simplify_and_reverse
578: {
579: my $reqlist = shift;
580: my $dejavu = {};
581: my $result = [];
582:
583: for my $item (@$reqlist) {
584: if (!$dejavu->{$item}) {
585: unshift @$result, $item;
586: $dejavu->{$item} = 1;
587: }
588: }
589: return $result;
1.1 ckuethe 590: }