Annotation of src/usr.bin/pkg-config/pkg-config, Revision 1.68
1.1 ckuethe 1: #!/usr/bin/perl
1.68 ! jasper 2: # $OpenBSD: pkg-config,v 1.67 2011/06/20 12:10:17 jasper Exp $
1.40 jasper 3: # $CSK: pkgconfig.pl,v 1.39 2006/11/27 16:26:20 ckuethe Exp $
1.1 ckuethe 4:
5: # Copyright (c) 2006 Chris Kuethe <ckuethe@openbsd.org>
1.40 jasper 6: # Copyright (c) 2011 Jasper Lievisse Adriaanse <jasper@openbsd.org>
1.1 ckuethe 7: #
8: # Permission to use, copy, modify, and distribute this software for any
9: # purpose with or without fee is hereby granted, provided that the above
10: # copyright notice and this permission notice appear in all copies.
11: #
12: # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13: # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14: # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15: # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16: # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17: # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18: # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19:
20: use strict;
21: use warnings;
22: use Getopt::Long;
1.4 espie 23: use File::Basename;
1.58 jasper 24: use File::stat;
1.11 espie 25: use OpenBSD::PkgConfig;
1.1 ckuethe 26:
1.27 jasper 27: my @PKGPATH = qw(/usr/lib/pkgconfig /usr/local/lib/pkgconfig /usr/X11R6/lib/pkgconfig);
1.1 ckuethe 28:
1.16 espie 29: if (defined($ENV{PKG_CONFIG_LIBDIR}) && $ENV{PKG_CONFIG_LIBDIR}) {
1.66 jasper 30: @PKGPATH = split(/:/, $ENV{PKG_CONFIG_LIBDIR});
1.16 espie 31: } elsif (defined($ENV{PKG_CONFIG_PATH}) && $ENV{PKG_CONFIG_PATH}) {
1.66 jasper 32: unshift(@PKGPATH, split(/:/, $ENV{PKG_CONFIG_PATH}));
1.1 ckuethe 33: }
34:
35: my $logfile = '';
1.50 jasper 36: if (defined($ENV{PKG_CONFIG_LOG}) && $ENV{PKG_CONFIG_LOG}) {
37: $logfile = $ENV{PKG_CONFIG_LOG};
1.1 ckuethe 38: }
39:
1.33 jasper 40: my $allow_uninstalled =
1.16 espie 41: defined $ENV{PKG_CONFIG_DISABLE_UNINSTALLED} ? 0 : 1;
1.14 espie 42: my $found_uninstalled = 0;
43:
1.49 jasper 44: my $version = 0.25; # pretend to be this version of pkgconfig
1.10 espie 45:
46: my %configs = ();
1.35 espie 47: setup_self();
48:
1.10 espie 49: my %mode = ();
1.11 espie 50: my $variables = {};
1.1 ckuethe 51:
1.56 jasper 52: $variables->{pc_top_builddir} = $ENV{PKG_CONFIG_TOP_BUILD_DIR} //
1.35 espie 53: '$(top_builddir)';
54:
55: $variables->{pc_sysrootdir} //= $ENV{PKG_CONFIG_SYSROOT_DIR};
56: # The default '/' is implied.
1.29 jasper 57:
1.61 jasper 58: defined $ENV{PKG_CONFIG_DEBUG_SPEW} ? $mode{debug} = 1 : $mode{debug} = 0;
1.7 ckuethe 59:
1.4 espie 60: if ($logfile) {
1.35 espie 61: open my $L, ">>" , $logfile or die;
1.51 jasper 62: print $L beautify_list($0, @ARGV), "\n";
1.4 espie 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
1.34 jasper 72: # without having to go back and reload the files from disk.
1.1 ckuethe 73:
74: Getopt::Long::Configure('no_ignore_case');
1.68 ! jasper 75: GetOptions( 'debug' => \$mode{debug},
! 76: 'help' => \&help, #does not return
! 77: 'usage' => \&help, #does not return
! 78: 'list-all' => \$mode{list},
! 79: 'version' => sub { print "$version\n" ; exit(0);} ,
! 80: 'errors-to-stdout' => sub { $mode{estdout} = 1},
! 81: 'print-errors' => sub { $mode{printerr} = 1},
! 82: 'silence-errors' => sub { $mode{printerr} = 0},
! 83: 'short-errors' => sub { $mode{printerr} = 0},
1.14 espie 84: 'atleast-pkgconfig-version=s' => \$mode{myminvers},
1.68 ! jasper 85: 'print-provides' => \$mode{printprovides},
! 86: 'print-requires' => \$mode{printrequires},
1.30 jasper 87: 'print-requires-private' => \$mode{printrequiresprivate},
1.11 espie 88:
1.68 ! jasper 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},
! 99: 'atleast-version=s' => \$mode{minversion},
! 100: 'exact-version=s' => \$mode{exactversion},
! 101: 'max-version=s' => \$mode{maxversion},
! 102: 'modversion' => \$mode{modversion},
! 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}) {
1.61 jasper 109: if (defined $mode{libs}
110: or defined $mode{cflags}
111: or defined $mode{version}
112: or defined $mode{list}) {
1.14 espie 113: $mode{printerr} = 1;
114: } else {
115: $mode{printerr} = 0;
116: }
117: }
118:
1.62 jasper 119: say_debug("\n" . beautify_list($0, @ARGV));
1.13 espie 120:
121: my $rc = 0;
1.1 ckuethe 122:
1.14 espie 123: # XXX pkg-config is a bit weird
1.10 espie 124: {
125: my $p = join(' ', @ARGV);
1.14 espie 126: $p =~ s/^\s+//;
1.66 jasper 127: @ARGV = split(/\,?\s+/, $p);
1.10 espie 128: }
1.1 ckuethe 129:
1.14 espie 130: if ($mode{myminvers}) {
131: exit self_version($mode{myminvers});
132: }
133:
134: if ($mode{list}) {
135: exit do_list();
1.1 ckuethe 136: }
137:
1.13 espie 138: my $cfg_full_list = [];
1.14 espie 139: my $top_config = [];
1.1 ckuethe 140:
141: while (@ARGV){
1.13 espie 142: my $p = shift @ARGV;
143: my $op = undef;
144: my $v = undef;
1.35 espie 145: if (@ARGV >= 2 && $ARGV[0] =~ /^[<=>]+$/ &&
1.59 jasper 146: $ARGV[1] =~ /^[\d\.]+[\w\.]*$/) {
1.13 espie 147: $op = shift @ARGV;
148: $v = shift @ARGV;
1.1 ckuethe 149: }
1.52 jasper 150: # For these modes we just need some meta-information and
151: # parsing the requirements is not needed.
152: if (!($mode{modversion} || $mode{printprovides})) {
153: handle_config($p, $op, $v, $cfg_full_list);
154: }
1.14 espie 155: push(@$top_config, $p);
156: }
157:
158: if ($mode{exists}) {
159: exit $rc;
160: }
161:
162: if ($mode{uninstalled}) {
163: $rc = 1 unless $found_uninstalled;
164: exit $rc;
1.11 espie 165: }
1.1 ckuethe 166:
1.30 jasper 167: if ($mode{modversion} || $mode{printprovides}) {
1.14 espie 168: for my $pkg (@$top_config) {
169: do_modversion($pkg);
170: }
171: }
1.13 espie 172:
1.30 jasper 173: if ($mode{printrequires} || $mode{printrequiresprivate}) {
174: for my $pkg (@$top_config) {
175: print_requires($pkg);
176: }
177: }
178:
1.14 espie 179: if ($mode{minversion}) {
180: my $v = $mode{minversion};
181: for my $pkg (@$top_config) {
182: $rc = 1 unless versionmatch($configs{$pkg}, '>=', $v);
183: }
184: exit $rc;
185: }
186:
187: if ($mode{exactversion}) {
188: my $v = $mode{exactversion};
189: for my $pkg (@$top_config) {
190: $rc = 1 unless versionmatch($configs{$pkg}, '=', $v);
191: }
192: exit $rc;
193: }
194:
195: if ($mode{minversion}) {
196: my $v = $mode{maxversion};
197: for my $pkg (@$top_config) {
198: $rc = 1 unless versionmatch($configs{$pkg}, '<=', $v);
199: }
200: exit $rc;
201: }
202:
203: my @vlist = ();
204:
205: if ($mode{variable}) {
206: for my $pkg (@$top_config) {
207: do_variable($pkg, $mode{variable});
208: }
209: }
210:
211: my $dep_cfg_list = simplify_and_reverse($cfg_full_list);
212:
213: if ($mode{cflags} || $mode{libs} || $mode{variable}) {
1.66 jasper 214: push @vlist, do_cflags($dep_cfg_list) if $mode{cflags};
215: push @vlist, do_libs($dep_cfg_list) if $mode{libs};
216: print join(' ', @vlist), "\n" if $rc == 0;
1.1 ckuethe 217: }
218:
1.13 espie 219: exit $rc;
1.1 ckuethe 220:
221: ###########################################################################
222:
1.11 espie 223: sub handle_config
224: {
1.13 espie 225: my ($p, $op, $v, $list) = @_;
1.35 espie 226: my $cfg = cache_find_config($p);
1.13 espie 227:
1.35 espie 228: unshift @$list, $p if defined $cfg;
1.11 espie 229:
1.35 espie 230: if (!defined $cfg) {
231: $rc = 1;
232: return undef;
233: }
1.15 espie 234:
1.35 espie 235: if (defined $op) {
236: if (!versionmatch($cfg, $op, $v)) {
237: mismatch($p, $cfg, $op, $v) if $mode{printerr};
1.13 espie 238: $rc = 1;
239: return undef;
240: }
1.35 espie 241: }
1.11 espie 242:
1.43 jasper 243: my $get_props = sub {
244: my $property = shift;
245:
246: my $deps = $cfg->get_property($property, $variables);
247: if (defined $deps) {
248: for my $dep (@$deps) {
1.46 jasper 249: if ($dep =~ m/^(.*?)\s*([<=>]+)\s*([\d\.]+|[\d\.]+[\w]*[\d]+)$/) {
1.43 jasper 250: handle_config($1, $2, $3, $list);
251: } else {
252: handle_config($dep, undef, undef, $list);
253: }
1.26 jasper 254: }
1.62 jasper 255: say_debug("package $p " . lc($property) . " " . join(',', @$deps));
1.26 jasper 256: }
1.43 jasper 257: };
258:
1.66 jasper 259: if (defined $mode{cflags}
260: or ($mode{static} && $mode{libs})
261: or $mode{printrequiresprivate}) {
1.64 jasper 262: &$get_props("Requires.private");
263: }
1.43 jasper 264: &$get_props("Requires");
1.26 jasper 265:
1.11 espie 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";
1.62 jasper 277: say_debug("pathresolve($p) looking in $f");
1.14 espie 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.62 jasper 287: say_debug("pathresolve($p) looking in $f");
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;
1.33 jasper 298: eval {
1.11 espie 299: $cfg = OpenBSD::PkgConfig->read_file($f);
300: };
301: if (!$@) {
1.37 jasper 302: return validate_config($f, $cfg);
1.11 espie 303: } else {
1.62 jasper 304: say_debug($@);
1.11 espie 305: }
306: return undef;
307: }
308:
1.13 espie 309: sub cache_find_config
310: {
311: my $name = shift;
312:
1.62 jasper 313: say_debug("processing $name");
1.13 espie 314:
315: if (exists $configs{$name}) {
316: return $configs{$name};
317: } else {
318: return $configs{$name} = find_config($name);
319: }
1.37 jasper 320: }
321:
322: # Required elements for a valid .pc file: Name, Description, Version
323: sub validate_config
324: {
325: my ($f, $cfg) = @_;
326: my @required_elems = ('Name', 'Description', 'Version');
1.58 jasper 327:
328: # Check if we're dealing with an empty file, but don't error out just
329: # yet, we'll do that when we realize there's no Name field.
1.61 jasper 330: if (stat($f)->size == 0) {
1.58 jasper 331: my $p = $f;
332: $p =~ s/(^.*\/)(.*?)$/$2/g;
1.62 jasper 333: say_error("Package file '$p' appears to be empty");
1.58 jasper 334: }
1.37 jasper 335:
336: foreach (@required_elems) {
1.58 jasper 337: my $e = $cfg->get_property($_, $variables);
1.37 jasper 338: if (!defined $e) {
1.39 jasper 339: $f =~ s/(^.*\/)(.*?)\.pc$/$2/g;
1.62 jasper 340: say_error("Package '$f' has no $_: field");
1.37 jasper 341: return undef;
342: }
343: }
344:
345: return $cfg;
1.13 espie 346: }
347:
1.35 espie 348: # pkg-config won't install a pkg-config.pc file itself, but it may be
1.63 jasper 349: # listed as a dependency in other files. so prime the cache with self.
1.35 espie 350: sub setup_self
351: {
352: my $pkg_pc = OpenBSD::PkgConfig->new;
353: $pkg_pc->add_property('Version', $version);
1.38 jasper 354: $pkg_pc->add_variable('pc_path', join(":", @PKGPATH));
1.63 jasper 355: $pkg_pc->add_property('URL', "http://www.openbsd.org/cgi-bin/man.cgi?query=pkg-config");
356: $pkg_pc->add_property('Description', "fetch metadata about installed software packages");
1.35 espie 357: $configs{'pkg-config'} = $pkg_pc;
358: }
359:
1.11 espie 360: sub find_config
361: {
362: my ($p) = @_;
363: my $f = pathresolve($p);
1.64 jasper 364:
365: return get_config($f) if defined($f);
366:
1.62 jasper 367: say_error("Package $p was not found in the pkg-config search path");
1.61 jasper 368:
1.11 espie 369: return undef;
370: }
1.1 ckuethe 371:
1.11 espie 372: sub stringize
1.4 espie 373: {
1.11 espie 374: my $list = shift;
1.21 simon 375: my $sep = shift || ',';
1.4 espie 376:
1.11 espie 377: if (defined $list) {
1.21 simon 378: return join($sep, @$list)
1.11 espie 379: } else {
380: return '';
1.1 ckuethe 381: }
382: }
383:
384: #if the variable option is set, pull out the named variable
1.4 espie 385: sub do_variable
386: {
1.11 espie 387: my ($p, $v) = @_;
1.1 ckuethe 388:
1.13 espie 389: my $cfg = cache_find_config($p);
390:
391: if (defined $cfg) {
1.11 espie 392: my $value = $cfg->get_variable($v, $variables);
393: if (defined $value) {
1.13 espie 394: push(@vlist, $value);
1.11 espie 395: }
1.19 espie 396: return undef;
1.11 espie 397: }
1.19 espie 398: $rc = 1;
1.1 ckuethe 399: }
400:
1.30 jasper 401: #if the modversion or print-provides options are set,
402: #pull out the compiler flags
1.4 espie 403: sub do_modversion
404: {
1.11 espie 405: my ($p) = @_;
1.1 ckuethe 406:
1.13 espie 407: my $cfg = cache_find_config($p);
408:
409: if (defined $cfg) {
1.11 espie 410: my $value = $cfg->get_property('Version', $variables);
411: if (defined $value) {
1.60 jasper 412: if (defined($mode{printprovides})){
413: print "$p = " . stringize($value) . "\n";
1.30 jasper 414: return undef;
415: } else {
1.60 jasper 416: print stringize($value), "\n";
1.30 jasper 417: return undef;
418: }
1.11 espie 419: }
420: }
1.13 espie 421: $rc = 1;
1.1 ckuethe 422: }
423:
424: #if the cflags option is set, pull out the compiler flags
1.4 espie 425: sub do_cflags
426: {
1.14 espie 427: my $list = shift;
428:
1.11 espie 429: my $cflags = [];
1.1 ckuethe 430:
1.14 espie 431: foreach my $pkg (@$list) {
1.11 espie 432: my $l = $configs{$pkg}->get_property('Cflags', $variables);
433: push(@$cflags, @$l) if defined $l;
434: }
1.32 jasper 435: my $a = OpenBSD::PkgConfig->compress($cflags,
1.11 espie 436: sub {
437: local $_ = shift;
438: if (($mode{cflags} & 1) && /^-I/ ||
439: ($mode{cflags} & 2) && !/^-I/) {
440: return 1;
441: } else {
442: return 0;
1.4 espie 443: }
1.11 espie 444: });
1.32 jasper 445: if (defined($a) && defined($variables->{pc_sysrootdir})){
1.36 jasper 446: $a =~ s/[\w]?-I/$&$variables->{pc_sysrootdir}/g;
1.32 jasper 447: }
448:
449: return $a;
1.1 ckuethe 450: }
451:
452: #if the lib option is set, pull out the linker flags
1.4 espie 453: sub do_libs
454: {
1.14 espie 455: my $list = shift;
456:
1.11 espie 457: my $libs = [];
1.1 ckuethe 458:
1.68 ! jasper 459: # In static mode, we have to make sure we discover the libs in dependency
! 460: # order, not in search order. Ordering matters for static linking:
! 461: # Start with Libs (first our own, then dependencies), and append
! 462: # Libs.private (same order as for Libs).
1.14 espie 463: foreach my $pkg (@$list) {
1.11 espie 464: my $l = $configs{$pkg}->get_property('Libs', $variables);
465: push(@$libs, @$l) if defined $l;
1.67 jasper 466: if ($mode{static}) {
467: my $lp = $configs{$pkg}->get_property('Libs.private', $variables);
468: push(@$libs, @$lp) if defined $lp;
469: }
1.11 espie 470: }
1.66 jasper 471:
1.68 ! jasper 472: # Get the linker path directives (-L) and store it in $a.
! 473: # $b will be the actual libraries.
1.13 espie 474: my $a = OpenBSD::PkgConfig->compress($libs,
1.11 espie 475: sub {
476: local $_ = shift;
1.13 espie 477: if (($mode{libs} & 2) && /^-L/ ||
1.11 espie 478: ($mode{libs} & 4) && !/^-[lL]/) {
479: return 1;
480: } else {
481: return 0;
1.4 espie 482: }
1.11 espie 483: });
1.32 jasper 484:
485: if (defined($variables->{pc_sysrootdir})){
1.36 jasper 486: $a =~ s/[\w]?-[lL]/$&$variables->{pc_sysrootdir}/g;
1.32 jasper 487: }
488:
1.13 espie 489: if ($mode{libs} & 1) {
490: my $b = OpenBSD::PkgConfig->rcompress($libs,
1.66 jasper 491: sub { shift =~ m/^-l/; });
1.13 espie 492: return ($a, $b);
493: } else {
494: return $a;
495: }
1.1 ckuethe 496: }
497:
498: #list all packages
1.4 espie 499: sub do_list
500: {
1.1 ckuethe 501: my ($p, $x, $y, @files, $fname, $name);
1.20 espie 502: my $error = 0;
503:
1.33 jasper 504: foreach my $p (@PKGPATH) {
505: push(@files, <$p/*.pc>);
1.4 espie 506: }
1.1 ckuethe 507:
508: # Scan the lengths of the package names so I can make a format
509: # string to line the list up just like the real pkgconfig does.
510: $x = 0;
1.4 espie 511: foreach my $f (@files) {
512: $fname = basename($f, '.pc');
513: $y = length $fname;
1.1 ckuethe 514: $x = (($y > $x) ? $y : $x);
515: }
516: $x *= -1;
517:
1.4 espie 518: foreach my $f (@files) {
1.11 espie 519: my $cfg = get_config($f);
1.20 espie 520: if (!defined $cfg) {
1.62 jasper 521: say_warning("Problem reading file $f");
1.20 espie 522: $error = 1;
523: next;
524: }
1.4 espie 525: $fname = basename($f, '.pc');
1.33 jasper 526: printf("%${x}s %s - %s\n", $fname,
1.53 jasper 527: stringize($cfg->get_property('Name', $variables), ' '),
1.21 simon 528: stringize($cfg->get_property('Description', $variables),
529: ' '));
1.1 ckuethe 530: }
1.20 espie 531: return $error;
1.1 ckuethe 532: }
533:
1.4 espie 534: sub help
535: {
1.1 ckuethe 536: print <<EOF
537: Usage: $0 [options]
538: --debug - turn on debugging output
539: --help - this message
540: --usage - this message
541: --list-all - show all packages that $0 can find
1.8 ckuethe 542: --version - print version of pkgconfig
543: --errors-to-stdout - direct error messages to stdout rather than stderr
544: --print-errors - print error messages in case of error
1.34 jasper 545: --print-provides - print all the modules the given package provides
546: --print-requires - print all the modules the given package requires
547: --print-requires-private - print all the private modules the given package requires
1.66 jasper 548: --silence-errors - don\'t print error messages in case of error
1.1 ckuethe 549: --atleast-pkgconfig-version [version] - require a certain version of pkgconfig
550: --cflags package [versionspec] [package [versionspec]]
551: --cflags-only-I - only output -Iincludepath flags
552: --cflags-only-other - only output flags that are not -I
1.11 espie 553: --define-variable=NAME=VALUE - define variables
1.1 ckuethe 554: --libs package [versionspec] [package [versionspec]]
555: --libs-only-l - only output -llib flags
556: --libs-only-L - only output -Llibpath flags
557: --libs-only-other - only output flags that are not -l or -L
558: --exists package [versionspec] [package [versionspec]]
559: --uninstalled - allow for uninstalled versions to be used
1.8 ckuethe 560: --static - adjust output for static linking
561: --atleast-version [version] - require a certain version of a package
562: --modversion [package] - query the version of a package
563: --variable var package - return the definition of <var> in <package>
1.1 ckuethe 564: EOF
565: ;
1.22 simon 566: exit 0;
1.1 ckuethe 567: }
568:
569: # do we meet/beat the version the caller requested?
1.4 espie 570: sub self_version
571: {
572: my ($v) = @_;
573: my (@a, @b);
574:
1.66 jasper 575: @a = split(/\./, $v);
576: @b = split(/\./, $version);
1.1 ckuethe 577:
1.4 espie 578: if (($b[0] >= $a[0]) && ($b[1] >= $a[1])) {
1.14 espie 579: return 0;
1.1 ckuethe 580: } else {
1.14 espie 581: return 1;
582: }
583: }
584:
585: sub compare
586: {
587: my ($a, $b) = @_;
1.46 jasper 588: my ($full_a, $full_b) = ($a, $b);
589: my (@suffix_a, @suffix_b);
1.14 espie 590:
1.28 jasper 591: return 0 if ($a eq $b);
1.14 espie 592:
1.46 jasper 593: # is there a valid non-numeric suffix to deal with later?
1.58 jasper 594: # accepted are (in order): a(lpha) < b(eta) < rc < ' '.
1.46 jasper 595: # suffix[0] is the 'alpha' part, suffix[1] is the '1' part in 'alpha1'.
1.59 jasper 596: if ($a =~ s/(rc|beta|b|alpha|a)(\d+)$//) {
1.62 jasper 597: say_debug("valid suffix $1$2 found in $a$1$2.");
1.46 jasper 598: $suffix_a[0] = $1;
599: $suffix_a[1] = $2;
600: }
601:
1.59 jasper 602: if ($b =~ s/(rc|beta|b|alpha|a)(\d+)$//) {
1.62 jasper 603: say_debug("valid suffix $1$2 found in $b$1$2.");
1.46 jasper 604: $suffix_b[0] = $1;
605: $suffix_b[1] = $2;
606: }
607:
1.66 jasper 608: my @a = split(/\./, $a);
609: my @b = split(/\./, $b);
1.14 espie 610:
611: while (@a && @b) { #so long as both lists have something
1.46 jasper 612: if (!(@suffix_a || @suffix_b)) {
613: # simple comparison when no suffixes are in the game.
1.48 jasper 614: my $rc = compare_numeric($a[0], $b[0], 0);
615: return $rc if defined($rc);
1.46 jasper 616: } else {
617: # extended comparison.
1.56 jasper 618: if (((@a == 1) || (@b == 1)) &&
1.46 jasper 619: ($a[0] == $b[0])){
620: # one of the arrays has reached the last element,
621: # compare the suffix.
622:
623: # directly compare suffixes, provided both suffixes
624: # are present.
625: if (@suffix_a && @suffix_b) {
626: my $first_char = sub {
627: return substr(shift, 0, 1);
628: };
629:
630: # suffixes are equal, compare on numeric
631: if (&$first_char($suffix_a[0]) eq
632: &$first_char($suffix_b[0])) {
1.48 jasper 633: return compare_numeric($suffix_a[1], $suffix_b[1], 1);
1.46 jasper 634: }
635:
1.47 jasper 636: # rc beats beta beats alpha
1.46 jasper 637: if (&$first_char($suffix_a[0]) lt &$first_char($suffix_b[0])) {
1.62 jasper 638: say_debug("$full_a (installed) < $full_b (wanted)");
1.46 jasper 639: return -1;
640: } else {
1.62 jasper 641: say_debug("$full_a (installed) > $full_b (wanted)");
1.46 jasper 642: return 1;
643: }
644:
645: } else {
646: # one of either is lacking a suffix,
647: # thereby beating the other.
648: # e.g.: 1.02 > 1.02b1
649: if (@suffix_a) { # a is older
1.62 jasper 650: say_debug("$full_a (installed) < $full_b (wanted)");
1.55 jasper 651: return 1;
1.46 jasper 652: }
653:
654: if (@suffix_b) { # b is older
1.62 jasper 655: say_debug("$full_a (installed) > $full_b (wanted)");
1.55 jasper 656: return -1;
1.46 jasper 657: }
658: }
659: } else {
1.48 jasper 660: my $rc = compare_numeric($a[0], $b[0], 0);
661: return $rc if defined($rc);
1.46 jasper 662: }
663: }
1.14 espie 664: shift @a; shift @b;
665: }
666: return 1 if @a;
667: return -1 if @b;
668: return 0;
1.48 jasper 669: }
670:
671: # simple numeric comparison, with optional equality test.
672: sub compare_numeric
673: {
674: my ($x, $y, $eq) = @_;
675:
676: return 1 if $x > $y;
677: return -1 if $x < $y;
678: return 0 if (($x == $y) and ($eq == 1));
679: return undef;
1.1 ckuethe 680: }
681:
682: # got a package meeting the requested specific version?
1.4 espie 683: sub versionmatch
684: {
1.14 espie 685: my ($cfg, $op, $want) = @_;
1.33 jasper 686:
1.1 ckuethe 687: # can't possibly match if we can't find the file
1.11 espie 688: return 0 if !defined $cfg;
689:
1.14 espie 690: my $inst = stringize($cfg->get_property('Version', $variables));
1.11 espie 691:
1.1 ckuethe 692: # can't possibly match if we can't find the version string
1.14 espie 693: return 0 if $inst eq '';
1.1 ckuethe 694:
1.62 jasper 695: say_debug("comparing $want (wanted) to $inst (installed)");
1.14 espie 696: my $value = compare($inst, $want);
1.31 jasper 697: if ($op eq '>=') { return $value >= 0; }
698: elsif ($op eq '=') { return $value == 0; }
699: elsif ($op eq '!=') { return $value != 0; }
700: elsif ($op eq '<') { return $value < 0; }
701: elsif ($op eq '>') { return $value > 0; }
702: elsif ($op eq '<=') { return $value <= 0; }
1.13 espie 703: }
704:
705: sub mismatch
706: {
707: my ($p, $cfg, $op, $v) = @_;
1.53 jasper 708: my $name = stringize($cfg->get_property('Name'), ' ');
1.41 jasper 709: my $version = stringize($cfg->get_property('Version'));
710: my $url = stringize($cfg->get_property('URL'));
711:
1.62 jasper 712: say_warning("Requested '$p $op $v' but version of $name is $version");
713: say_warning("You may find new versions of $name at $url") if $url;
1.13 espie 714: }
715:
716: sub simplify_and_reverse
717: {
718: my $reqlist = shift;
719: my $dejavu = {};
720: my $result = [];
721:
722: for my $item (@$reqlist) {
723: if (!$dejavu->{$item}) {
724: unshift @$result, $item;
725: $dejavu->{$item} = 1;
726: }
727: }
728: return $result;
1.30 jasper 729: }
730:
731: # retrieve and print Requires(.private)
732: sub print_requires
733: {
734: my ($p) = @_;
735:
736: my $cfg = cache_find_config($p);
737:
738: if (defined($cfg)) {
739: my $value;
740:
741: if (defined($mode{printrequires})) {
742: $value = $cfg->get_property('Requires', $variables);
743: } elsif (defined($mode{printrequiresprivate})) {
744: $value = $cfg->get_property('Requires.private', $variables);
745: } else {
1.62 jasper 746: say_debug("Unknown mode for print_requires.");
1.30 jasper 747: return 1;
748: }
749:
750: if (defined($value)) {
751: print "$_\n" foreach (@$value);
752: return undef;
753: }
754: }
755:
756: $rc = 1;
1.35 espie 757: }
758:
759: sub beautify_list
760: {
761: return join(' ', map {"[$_]"} @_);
1.61 jasper 762: }
763:
1.62 jasper 764: sub say_debug
1.61 jasper 765: {
1.62 jasper 766: say_msg(shift) if $mode{debug};
1.61 jasper 767: }
768:
1.62 jasper 769: sub say_error
1.61 jasper 770: {
1.62 jasper 771: say_msg(shift) if $mode{printerr}
772: }
773:
774: sub say_warning
775: {
776: say_msg(shift);
777: }
778:
779: sub say_msg
780: {
1.63 jasper 781: my $str = shift;
1.62 jasper 782:
783: # If --errors-to-stdout was given, close STDERR (to be safe),
784: # then dup the output to STDOUT and delete the key from %mode so we
785: # won't keep checking it. STDERR stays dup'ed.
786: if ($mode{estdout}) {
787: close(STDERR);
788: open(STDERR, ">&STDOUT") or die "Can't dup STDOUT: $!";
789: delete($mode{estdout});
790: }
791:
792: print STDERR $str . "\n";
1.1 ckuethe 793: }