1#!/usr/bin/perl 2# $OpenBSD: pkg-config,v 1.97 2023/09/22 07:28:31 espie Exp $ 3 4# Copyright (c) 2006 Chris Kuethe <ckuethe@openbsd.org> 5# Copyright (c) 2011-2020 Jasper Lievisse Adriaanse <jasper@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 19use v5.36; 20use Config; 21use Getopt::Long; 22use File::Basename; 23use File::stat; 24use OpenBSD::PkgConfig; 25 26use constant { 27 ONLY_I => 1, 28 ONLY_l => 2, 29 ONLY_L => 4, 30 ONLY_OTHER => 8 31}; 32 33my @PKGPATH = qw(/usr/lib/pkgconfig 34 /usr/local/lib/pkgconfig 35 /usr/local/share/pkgconfig 36 /usr/X11R6/lib/pkgconfig 37 /usr/X11R6/share/pkgconfig); 38 39if (defined($ENV{PKG_CONFIG_LIBDIR}) && $ENV{PKG_CONFIG_LIBDIR}) { 40 @PKGPATH = split(/:/, $ENV{PKG_CONFIG_LIBDIR}); 41} elsif (defined($ENV{PKG_CONFIG_PATH}) && $ENV{PKG_CONFIG_PATH}) { 42 unshift(@PKGPATH, split(/:/, $ENV{PKG_CONFIG_PATH})); 43} 44 45my $logfile = ''; 46if (defined($ENV{PKG_CONFIG_LOG}) && $ENV{PKG_CONFIG_LOG}) { 47 $logfile = $ENV{PKG_CONFIG_LOG}; 48} 49 50my $allow_uninstalled = 51 defined $ENV{PKG_CONFIG_DISABLE_UNINSTALLED} ? 0 : 1; 52my $found_uninstalled = 0; 53 54my $version = '0.29.2'; # pretend to be this version of pkgconfig 55 56my %configs = (); 57setup_self(); 58 59my %mode = (); 60my $variables = {}; 61 62$variables->{pc_top_builddir} = $ENV{PKG_CONFIG_TOP_BUILD_DIR} // 63 '$(top_builddir)'; 64 65$variables->{pc_sysrootdir} //= $ENV{PKG_CONFIG_SYSROOT_DIR}; 66# The default '/' is implied. 67 68my @sys_includes = ('/usr/include'); 69foreach my $path ($ENV{PKG_CONFIG_SYSTEM_INCLUDE_PATH}, $ENV{C_PATH}, $ENV{C_INCLUDE_PATH}, 70 $ENV{CPLUS_INCLUDE_PATH}) { 71 next if !defined($path); 72 unshift(@sys_includes, split(/:/, $path)); 73} 74 75defined $ENV{PKG_CONFIG_DEBUG_SPEW} ? $mode{debug} = 1 : $mode{debug} = 0; 76 77if ($logfile) { 78 open my $L, ">>" , $logfile or die; 79 say $L beautify_list($0, @ARGV); 80 close $L; 81} 82 83# combo arg-parsing and dependency resolution loop. Hopefully when the loop 84# terminates, we have a full list of packages upon which we depend, and the 85# right set of compiler and linker flags to use them. 86# 87# as each .pc file is loaded, it is stored in %configs, indexed by package 88# name. this makes it possible to then pull out flags or do substitutions 89# without having to go back and reload the files from disk. 90 91Getopt::Long::Configure('no_ignore_case'); 92GetOptions( 'debug' => \$mode{debug}, 93 'help' => \&help, #does not return 94 'usage' => \&help, #does not return 95 'list-all' => \$mode{list}, 96 'version' => sub { say $version ; exit(0);} , 97 'errors-to-stdout' => sub { $mode{estdout} = 1}, 98 'print-errors' => sub { $mode{printerr} = 1}, 99 'silence-errors' => sub { $mode{printerr} = 0}, 100 'short-errors' => sub { $mode{printerr} = 0}, 101 'atleast-pkgconfig-version=s' => \$mode{myminvers}, 102 'print-provides' => \$mode{printprovides}, 103 'print-requires' => \$mode{printrequires}, 104 'print-requires-private' => \$mode{printrequiresprivate}, 105 106 'cflags' => sub { $mode{cflags} = ONLY_I|ONLY_OTHER}, 107 'cflags-only-I' => sub { $mode{cflags} |= ONLY_I}, 108 'cflags-only-other' => sub { $mode{cflags} |= ONLY_OTHER}, 109 'libs' => sub { $mode{libs} = ONLY_L|ONLY_l|ONLY_OTHER}, 110 'libs-only-l' => sub { $mode{libs} |= ONLY_l}, 111 'libs-only-L' => sub { $mode{libs} |= ONLY_L}, 112 'libs-only-other' => sub { $mode{libs} |= ONLY_OTHER}, 113 'exists' => sub { $mode{exists} = 1} , 114 'validate' => sub { $mode{validate} = 1}, 115 'static' => sub { $mode{static} = 1}, 116 'uninstalled' => sub { $mode{uninstalled} = 1}, 117 'atleast-version=s' => \$mode{minversion}, 118 'exact-version=s' => \$mode{exactversion}, 119 'max-version=s' => \$mode{maxversion}, 120 'modversion' => \$mode{modversion}, 121 'variable=s' => \$mode{variable}, 122 'define-variable=s' => $variables, 123 ); 124 125# Unconditionally switch to static mode on static arches as --static 126# may not have been passed explicitly, but we don't want to re-order 127# and simplify the libs like we do for shared architectures. 128{ 129 my @static_archs = qw(); 130 my $machine_arch = $Config{'ARCH'}; 131 if (grep { $_ eq $machine_arch } @static_archs){ 132 $mode{static} = 1; 133 } 134} 135 136# Initial value of printerr depends on the options... 137if (!defined $mode{printerr}) { 138 if (defined $mode{libs} 139 or defined $mode{cflags} 140 or defined $mode{version} 141 or defined $mode{list} 142 or defined $mode{validate}) { 143 $mode{printerr} = 1; 144 } else { 145 $mode{printerr} = 0; 146 } 147} 148 149say_debug("\n" . beautify_list($0, @ARGV)); 150 151my $rc = 0; 152 153# XXX pkg-config is a bit weird 154{ 155my $p = join(' ', @ARGV); 156$p =~ s/^\s+//; 157@ARGV = split(/\,?\s+/, $p); 158} 159 160if ($mode{myminvers}) { 161 exit self_version($mode{myminvers}); 162} 163 164if ($mode{list}) { 165 exit do_list(); 166} 167 168my $cfg_full_list = []; 169my $top_config = []; 170 171# When we got here we're supposed to have had at least one 172# package as argument. 173if (!@ARGV) { 174 say_error("No package name(s) specified."); 175 exit 1; 176} 177 178# Return the next module from @ARGV, if it turns out to be a comma separated 179# module list, take the first one and put the rest back to the front. 180sub get_next_module() 181{ 182 my $module = shift @ARGV; 183 my $m; 184 if ($module =~ m/,/) { 185 my @ms = split(/,/, $module); 186 $m = shift @ms; 187 unshift(@ARGV, @ms) if @ms != 0; 188 } else { 189 return $module; 190 } 191 192 return $m; 193} 194 195while (@ARGV) { 196 my $p = get_next_module(); 197 my $op = undef; 198 my $v = undef; 199 if (@ARGV >= 2 && $ARGV[0] =~ /^[<=>!]+$/ && 200 $ARGV[1] =~ /^[\d\.]+[\w\.]*$/) { 201 $op = shift @ARGV; 202 $v = shift @ARGV; 203 } 204 # For these modes we just need some meta-information and 205 # parsing the requirements is not needed. 206 if (!($mode{modversion} || $mode{printprovides})) { 207 handle_config($p, $op, $v, $cfg_full_list); 208 } 209 push(@$top_config, $p); 210} 211 212if ($mode{exists} || $mode{validate}) { 213 exit $rc; 214} 215 216if ($mode{uninstalled}) { 217 $rc = 1 unless $found_uninstalled; 218 exit $rc; 219} 220 221if ($mode{modversion} || $mode{printprovides}) { 222 for my $pkg (@$top_config) { 223 do_modversion($pkg); 224 } 225} 226 227if ($mode{printrequires} || $mode{printrequiresprivate}) { 228 for my $pkg (@$top_config) { 229 print_requires($pkg); 230 } 231} 232 233if ($mode{minversion}) { 234 my $v = $mode{minversion}; 235 for my $pkg (@$top_config) { 236 $rc = 1 unless versionmatch($configs{$pkg}, '>=', $v); 237 } 238 exit $rc; 239} 240 241if ($mode{exactversion}) { 242 my $v = $mode{exactversion}; 243 for my $pkg (@$top_config) { 244 $rc = 1 unless versionmatch($configs{$pkg}, '=', $v); 245 } 246 exit $rc; 247} 248 249if ($mode{maxversion}) { 250 my $v = $mode{maxversion}; 251 for my $pkg (@$top_config) { 252 $rc = 1 unless versionmatch($configs{$pkg}, '<=', $v); 253 } 254 exit $rc; 255} 256 257my @vlist = (); 258 259if ($mode{variable}) { 260 for my $pkg (@$top_config) { 261 do_variable($pkg, $mode{variable}); 262 } 263} 264 265my $dep_cfg_list = $cfg_full_list; 266 267if ($mode{static}){ 268 $dep_cfg_list = [reverse(@$cfg_full_list)]; 269} else { 270 $dep_cfg_list = simplify_and_reverse($cfg_full_list); 271} 272 273if ($mode{cflags} || $mode{libs} || $mode{variable}) { 274 push @vlist, do_cflags($dep_cfg_list) if $mode{cflags}; 275 push @vlist, do_libs($dep_cfg_list) if $mode{libs}; 276 say join(' ', @vlist) if $rc == 0; 277} 278 279exit $rc; 280 281########################################################################### 282sub set_variables_from_env($file) 283{ 284 state (%done, @l); 285 286 if (!defined $done{$file}) { 287 my $pkg = $file; 288 289 $pkg =~ s/(^.*\/)?(.*?)\.pc$/$2/g; 290 $pkg = uc($pkg); 291 if (!@l) { 292 @l = grep {/PKG_CONFIG_/} keys %ENV; 293 } 294 for my $k (@l) { 295 next unless $k =~ m/PKG_CONFIG_${pkg}_(\w+)/; 296 $variables->{lc($1)} = $ENV{$k}; 297 } 298 $done{$file} = 1; 299 } 300 301} 302 303sub handle_config($p, $op, $v, $list) 304{ 305 my $cfg = cache_find_config($p); 306 307 unshift @$list, $p if defined $cfg; 308 309 if (!defined $cfg) { 310 $rc = 1; 311 return undef; 312 } 313 314 if (defined $op) { 315 if (!versionmatch($cfg, $op, $v)) { 316 mismatch($p, $cfg, $op, $v) if $mode{printerr}; 317 $rc = 1; 318 return undef; 319 } 320 } 321 322 my $get_props = sub($property) { 323 set_variables_from_env($p); 324 325 my $deps = $cfg->get_property($property, $variables); 326 return unless defined $deps; 327 for my $dep (@$deps) { 328 if ($dep =~ m/^(.*?)\s*([<=>]+)\s*([\d\.]+|[\d\.]+\w*\d+)$/) { 329 handle_config($1, $2, $3, $list); 330 } else { 331 handle_config($dep, undef, undef, $list); 332 } 333 } 334 say_debug("package $p " . lc($property) . " " . join(',', @$deps)); 335 }; 336 337 if (defined $mode{cflags} 338 or ($mode{static} && $mode{libs}) 339 or $mode{printrequiresprivate} 340 or $mode{exists}) { 341 &$get_props("Requires.private"); 342 } 343 344 unless (defined $mode{validate}) { 345 &$get_props("Requires"); 346 } 347} 348 349# look for the .pc file in each of the PKGPATH elements. Return the path or 350# undef if it's not there 351sub pathresolve($p) 352{ 353 if ($allow_uninstalled && $p !~ m/\-uninstalled$/) { 354 for my $d (@PKGPATH) { 355 my $f = "$d/$p-uninstalled.pc"; 356 say_debug("pathresolve($p) looking in $f"); 357 if (-f $f) { 358 $found_uninstalled = 1; 359 return $f; 360 } 361 } 362 } 363 364 for my $d (@PKGPATH) { 365 my $f = "$d/$p.pc"; 366 say_debug("pathresolve($p) looking in $f"); 367 return $f if -f $f; 368 } 369 return undef; 370} 371 372sub get_config($f) 373{ 374 my $cfg; 375 eval { 376 $cfg = OpenBSD::PkgConfig->read_file($f); 377 }; 378 if (!$@) { 379 return validate_config($f, $cfg); 380 } else { 381 say_debug($@); 382 } 383 return undef; 384} 385 386sub cache_find_config($name) 387{ 388 say_debug("processing $name"); 389 390 if (exists $configs{$name}) { 391 return $configs{$name}; 392 } else { 393 return $configs{$name} = find_config($name); 394 } 395} 396 397# Required elements for a valid .pc file: Name, Description, Version 398sub validate_config($f, $cfg) 399{ 400 my @required_elems = ('Name', 'Description', 'Version'); 401 402 # Check if we're dealing with an empty file, but don't error out just 403 # yet, we'll do that when we realize there's no Name field. 404 if (stat($f)->size == 0) { 405 say_error("Package file '$f' appears to be empty"); 406 } 407 408 for my $p (@required_elems) { 409 my $e = $cfg->get_property($p, $variables); 410 if (!defined $e) { 411 $f =~ s/(^.*\/)?(.*?)\.pc$/$2/g; 412 say_error("Package '$f' has no $p: field"); 413 return undef; 414 } 415 } 416 417 return $cfg; 418} 419 420# pkg-config won't install a pkg-config.pc file itself, but it may be 421# listed as a dependency in other files. so prime the cache with self. 422sub setup_self() 423{ 424 my $pkg_pc = OpenBSD::PkgConfig->new; 425 $pkg_pc->add_property('Version', $version); 426 $pkg_pc->add_variable('pc_path', join(":", @PKGPATH)); 427 $pkg_pc->add_property('URL', "http://man.openbsd.org/pkg-config"); 428 $pkg_pc->add_property('Description', "fetch metadata about installed software packages"); 429 $configs{'pkg-config'} = $pkg_pc; 430} 431 432sub find_config($p) 433{ 434 # Differentiate between getting a full path and just the module name. 435 my $f = ($p =~ m/\.pc$/ ? $p : pathresolve($p)); 436 437 return get_config($f) if defined($f); 438 439 say_error("Package $p was not found in the pkg-config search path"); 440 441 return undef; 442} 443 444sub stringize($list, $sep = ',') 445{ 446 if (defined $list) { 447 return join($sep, @$list) 448 } else { 449 return ''; 450 } 451} 452 453#if the variable option is set, pull out the named variable 454sub do_variable($p, $v) 455{ 456 my $cfg = cache_find_config($p); 457 458 if (defined $cfg) { 459 my $value = $cfg->get_variable($v, $variables); 460 if (defined $value) { 461 push(@vlist, $value); 462 } 463 return undef; 464 } 465 $rc = 1; 466} 467 468#if the modversion or print-provides options are set, 469#pull out the compiler flags 470sub do_modversion($p) 471{ 472 my $cfg = cache_find_config($p); 473 474 if (defined $cfg) { 475 my $value = $cfg->get_property('Version', $variables); 476 if (defined $value) { 477 if (defined($mode{printprovides})){ 478 say "$p = " , stringize($value); 479 return undef; 480 } else { 481 say stringize($value); 482 return undef; 483 } 484 } 485 } 486 $rc = 1; 487} 488 489#if the cflags option is set, pull out the compiler flags 490sub do_cflags($list) 491{ 492 my $cflags = []; 493 494 for my $pkg (@$list) { 495 my $l = $configs{$pkg}->get_property('Cflags', $variables); 496 PATH: for my $path (@$l) { 497 for my $sys_path (@sys_includes) { 498 next PATH if $path =~ /\Q${sys_path}\E\/*$/; 499 } 500 push(@$cflags, $path); 501 } 502 } 503 my $a = OpenBSD::PkgConfig->compress($cflags, 504 sub($r) { 505 if (($mode{cflags} & ONLY_I) && $r =~ /^-I/ || 506 ($mode{cflags} & ONLY_OTHER) && $r !~ /^-I/) { 507 return 1; 508 } else { 509 return 0; 510 } 511 }); 512 if (defined($variables->{pc_sysrootdir})){ 513 $a =~ s/[\w]?-I/$&$variables->{pc_sysrootdir}/g; 514 } 515 516 return $a; 517} 518 519#if the lib option is set, pull out the linker flags 520sub do_libs($list) 521{ 522 my $libs = []; 523 524 # In static mode, we have to make sure we discover the libs in dependency 525 # order, not in search order. Ordering matters for static linking: 526 # Start with Libs (first our own, then dependencies), and append 527 # Libs.private (same order as for Libs). 528 for my $pkg (@$list) { 529 my $l = $configs{$pkg}->get_property('Libs', $variables); 530 for my $path (@$l) { 531 unless ($path =~ /-L\/usr\/lib\/*$/) { 532 push(@$libs, $path); 533 } 534 } 535 if ($mode{static}) { 536 my $lp = $configs{$pkg}->get_property('Libs.private', $variables); 537 for my $path (@$lp) { 538 unless ($path =~ /-L\/usr\/lib\/*/) { 539 push(@$libs, $path); 540 } 541 } 542 } 543 } 544 545 # Get the linker path directives (-L) and store it in $a. 546 # $b will be the actual libraries. 547 my $r = OpenBSD::PkgConfig->compress_list($libs, 548 sub($r) { 549 if (($mode{libs} & ONLY_L) && $r =~ /^-L/ || 550 ($mode{libs} & ONLY_OTHER) && $r !~ /^-[lL]/) { 551 return 1; 552 } else { 553 return 0; 554 } 555 }); 556 557 if (defined($variables->{pc_sysrootdir})){ 558 for my $i (@$r) { 559 $i =~ s/[\w]?-[lL]/$&$variables->{pc_sysrootdir}/; 560 } 561 } 562 563 if ($mode{libs} & ONLY_l) { 564 push(@$r, OpenBSD::PkgConfig->rcompress($libs, 565 sub($l) { $l =~ m/^-l/; })); 566 } 567 return @$r; 568} 569 570#list all packages 571sub do_list() 572{ 573 my ($p, $x, $y, @files, $fname, $name); 574 575 my $error = 0; 576 577 for my $p (@PKGPATH) { 578 push(@files, <$p/*.pc>); 579 } 580 581 # Scan the lengths of the package names so I can make a format 582 # string to line the list up just like the real pkgconfig does. 583 $x = 0; 584 for my $f (@files) { 585 $fname = basename($f, '.pc'); 586 $y = length $fname; 587 $x = (($y > $x) ? $y : $x); 588 } 589 $x *= -1; 590 591 for my $f (@files) { 592 my $cfg = get_config($f); 593 if (!defined $cfg) { 594 say_warning("Problem reading file $f"); 595 $error = 1; 596 next; 597 } 598 $fname = basename($f, '.pc'); 599 printf("%${x}s %s - %s\n", $fname, 600 stringize($cfg->get_property('Name', $variables), ' '), 601 stringize($cfg->get_property('Description', $variables), 602 ' ')); 603 } 604 return $error; 605} 606 607sub help(@) 608{ 609 print <<EOF 610Usage: $0 [options] 611--debug - turn on debugging output 612--help - this message 613--usage - this message 614--list-all - show all packages that $0 can find 615--version - print version of pkgconfig 616--errors-to-stdout - direct error messages to stdout rather than stderr 617--print-errors - print error messages in case of error 618--print-provides - print all the modules the given package provides 619--print-requires - print all the modules the given package requires 620--print-requires-private - print all the private modules the given package requires 621--silence-errors - don\'t print error messages in case of error 622--atleast-pkgconfig-version [version] - require a certain version of pkgconfig 623--cflags package [versionspec] [package [versionspec]] 624--cflags-only-I - only output -Iincludepath flags 625--cflags-only-other - only output flags that are not -I 626--define-variable=NAME=VALUE - define variables 627--libs package [versionspec] [package [versionspec]] 628--libs-only-l - only output -llib flags 629--libs-only-L - only output -Llibpath flags 630--libs-only-other - only output flags that are not -l or -L 631--exists package [versionspec] [package [versionspec]] 632--validate package 633--uninstalled - allow for uninstalled versions to be used 634--static - adjust output for static linking 635--atleast-version [version] - require a certain version of a package 636--exact-version [version] - require exactly the specified version of a package 637--max-version [version] - require at most a certain version of a package 638--modversion [package] - query the version of a package 639--variable var package - return the definition of <var> in <package> 640EOF 641; 642 exit 0; 643} 644 645# do we meet/beat the version the caller requested? 646sub self_version($v) 647{ 648 my (@a, @b); 649 650 @a = split(/\./, $v); 651 @b = split(/\./, $version); 652 653 if (($b[0] >= $a[0]) && ($b[1] >= $a[1])) { 654 return 0; 655 } else { 656 return 1; 657 } 658} 659 660sub parse_suffix($s) 661{ 662 my @l = (); 663 my $full = $s; 664 # is there a valid non-numeric suffix to deal with later? 665 # accepted are (in order): a(lpha) < b(eta) < rc < ' '. 666 # suffix[0] is the 'alpha' part, suffix[1] is the '1' part in 'alpha1'. 667 if ($s =~ s/(rc|beta|b|alpha|a)(\d+)$//) { 668 @l = ($1, $2); 669 } 670 # also deal with -stable extension 671 elsif ($s =~ s/(\-stable)$//) { 672 @l = ($1); 673 } 674 # The above are standard suffixes; deal with single alphabetical 675 # suffixes too, e.g. 1.0.1h 676 elsif ($s =~ s/([a-zA-Z]){1}$//) { 677 @l = ($1); 678 } 679 680 if (@l) { 681 say_debug("valid suffix @l found in $full."); 682 } 683 684 return ($s, @l); 685} 686 687sub compare($full_a, $full_b) 688{ 689 return 0 if $full_a eq $full_b; 690 691 my ($a, @suffix_a) = parse_suffix($full_a); 692 my ($b, @suffix_b) = parse_suffix($full_b); 693 694 my @a = split(/\./, $a); 695 my @b = split(/\./, $b); 696 697 while (@a && @b) { #so long as both lists have something 698 if (!(@suffix_a || @suffix_b)) { 699 # simple comparison when no suffixes are in the game. 700 my $rc = compare_numeric($a[0], $b[0], 0); 701 return $rc if defined($rc); 702 } else { 703 # extended comparison. 704 if (((@a == 1) || (@b == 1)) && 705 ($a[0] == $b[0])){ 706 # one of the arrays has reached the last element, 707 # compare the suffix. 708 709 # directly compare suffixes, provided both suffixes 710 # are present. 711 if (@suffix_a && @suffix_b) { 712 my $first_char = sub($s) { 713 return substr($s, 0, 1); 714 }; 715 716 # suffixes are equal, compare on numeric 717 if (&$first_char($suffix_a[0]) eq 718 &$first_char($suffix_b[0])) { 719 return compare_numeric($suffix_a[1], $suffix_b[1], 1); 720 } 721 722 # rc beats beta beats alpha 723 if (&$first_char($suffix_a[0]) lt &$first_char($suffix_b[0])) { 724 say_debug("$full_a (installed) < $full_b (wanted)"); 725 return -1; 726 } else { 727 say_debug("$full_a (installed) > $full_b (wanted)"); 728 return 1; 729 } 730 731 } else { 732 # one of either is lacking a suffix, 733 # thereby beating the other. 734 # e.g.: 1.02 > 1.02b1 735 if (@suffix_a) { # a is older 736 say_debug("$full_a (installed) < $full_b (wanted)"); 737 return 1; 738 } 739 740 if (@suffix_b) { # b is older 741 say_debug("$full_a (installed) > $full_b (wanted)"); 742 return -1; 743 } 744 } 745 } else { 746 my $rc = compare_numeric($a[0], $b[0], 0); 747 return $rc if defined($rc); 748 } 749 } 750 shift @a; shift @b; 751 } 752 return 1 if @a; 753 return -1 if @b; 754 return 0; 755} 756 757# simple numeric comparison, with optional equality test. 758sub compare_numeric($x, $y, $eq) 759{ 760 return 1 if $x > $y; 761 return -1 if $x < $y; 762 return 0 if (($x == $y) and ($eq == 1)); 763 return undef; 764} 765 766# got a package meeting the requested specific version? 767sub versionmatch($cfg, $op, $want) 768{ 769 # can't possibly match if we can't find the file 770 return 0 if !defined $cfg; 771 772 my $inst = stringize($cfg->get_property('Version', $variables)); 773 774 # can't possibly match if we can't find the version string 775 return 0 if $inst eq ''; 776 777 say_debug("comparing $want (wanted) to $inst (installed)"); 778 my $value = compare($inst, $want); 779 if ($op eq '>=') { return $value >= 0; } 780 elsif ($op eq '=') { return $value == 0; } 781 elsif ($op eq '!=') { return $value != 0; } 782 elsif ($op eq '<') { return $value < 0; } 783 elsif ($op eq '>') { return $value > 0; } 784 elsif ($op eq '<=') { return $value <= 0; } 785} 786 787sub mismatch($p, $cfg, $op, $v) 788{ 789 my $name = stringize($cfg->get_property('Name'), ' '); 790 my $version = stringize($cfg->get_property('Version')); 791 my $url = stringize($cfg->get_property('URL')); 792 793 say_warning("Requested '$p $op $v' but version of $name is $version"); 794 say_warning("You may find new versions of $name at $url") if $url; 795} 796 797sub simplify_and_reverse($reqlist) 798{ 799 my $dejavu = {}; 800 my $result = []; 801 802 for my $item (@$reqlist) { 803 if (!$dejavu->{$item}) { 804 unshift @$result, $item; 805 $dejavu->{$item} = 1; 806 } 807 } 808 return $result; 809} 810 811# retrieve and print Requires(.private) 812sub print_requires($p) 813{ 814 my $cfg = cache_find_config($p); 815 816 if (defined($cfg)) { 817 my $value; 818 819 if (defined($mode{printrequires})) { 820 $value = $cfg->get_property('Requires', $variables); 821 } elsif (defined($mode{printrequiresprivate})) { 822 $value = $cfg->get_property('Requires.private', $variables); 823 } else { 824 say_debug("Unknown mode for print_requires."); 825 return 1; 826 } 827 828 if (defined($value)) { 829 say $_ for @$value; 830 return undef; 831 } 832 } 833 834 $rc = 1; 835} 836 837sub beautify_list(@p) 838{ 839 return join(' ', map {"[$_]"} @p); 840} 841 842sub say_debug($msg) 843{ 844 say_msg($msg) if $mode{debug}; 845} 846 847sub say_error($msg) 848{ 849 say_msg($msg) if $mode{printerr} 850} 851 852sub say_warning($msg) 853{ 854 say_msg($msg); 855} 856 857sub say_msg($str) 858{ 859 # If --errors-to-stdout was given, close STDERR (to be safe), 860 # then dup the output to STDOUT and delete the key from %mode so we 861 # won't keep checking it. STDERR stays dup'ed. 862 if ($mode{estdout}) { 863 close(STDERR); 864 open(STDERR, ">&STDOUT") or die "Can't dup STDOUT: $!"; 865 delete($mode{estdout}); 866 } 867 868 say STDERR $str; 869} 870