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