openpkg/build.pl

Thu, 04 Oct 2012 20:30:05 +0200

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 04 Oct 2012 20:30:05 +0200
changeset 715
c10fb90893b9
permissions
-rw-r--r--

Correct out of date build configuration, porting to Solaris 11 network
link infrastructure and new libpcap logic. This additionally allows for
device drivers in subdirectories of /dev. Correct packaged nmap
personalities and signatures to work out of the box. Finally, hack
arpd logic to properly close sockets and quit on TERM by repeating
signaling in the run command script. Sadly, all this fails to correct
the run time behaviour of honeyd which fails to bind to the IP layer.

michael@428 1 ##
michael@428 2 ## build.pl -- OpenPKG Package Building and Installing
michael@428 3 ## Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/>
michael@428 4 ##
michael@428 5 ## This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
michael@428 6 ## All rights reserved. Licenses which grant limited permission to use,
michael@428 7 ## copy, modify and distribute this software are available from the
michael@428 8 ## OpenPKG GmbH.
michael@428 9 ##
michael@428 10 ## THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
michael@428 11 ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
michael@428 12 ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
michael@428 13 ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
michael@428 14 ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@428 15 ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@428 16 ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
michael@428 17 ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
michael@428 18 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
michael@428 19 ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
michael@428 20 ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
michael@428 21 ## SUCH DAMAGE.
michael@428 22 ##
michael@428 23
michael@428 24 #############################################################################
michael@428 25 ##
michael@428 26 ## MAIN PROCEDURE
michael@428 27 ##
michael@428 28 #############################################################################
michael@428 29
michael@428 30 require 5;
michael@428 31 #use strict;
michael@428 32 my (
michael@428 33 $opt_h,
michael@428 34 $opt_R, $opt_r, $opt_f, $opt_u, $opt_U, $opt_a, $opt_A,
michael@428 35 $opt_z, $opt_Z, $opt_P, $opt_N, $opt_E, $opt_H, $opt_i,
michael@428 36 $opt_D, $opt_p, $opt_q, $opt_s, $opt_S, $opt_X, $opt_M,
michael@428 37 $opt_L, $opt_W, $opt_K, $opt_e, $opt_b, $opt_B, $opt_g,
michael@428 38 $opt_k
michael@428 39 );
michael@428 40
michael@428 41 # global context variables
michael@428 42 my $prg = "openpkg build";
michael@428 43 my %env = ('' => {});
michael@428 44
michael@428 45 ##
michael@428 46 ## OPTION PARSING
michael@428 47 ##
michael@428 48
michael@428 49 # parse command line options
michael@428 50 my $getopts = 'hR:r:f:uUaAzZP:N:E:H:iD:p:qsSXMLWKebBgk';
michael@428 51 getopts($getopts);
michael@428 52
michael@428 53 # parse configuration script options
michael@428 54 if (open(FH, "<$ENV{'HOME'}/.openpkg/build")) {
michael@428 55 my ($env) = $env{''};
michael@428 56 my ($go) = $getopts;
michael@428 57 $go =~ s/[^a-zA-Z]//g;
michael@428 58 while (my $line = <FH>) {
michael@428 59 if ($line =~ m/^\s*\[([^\]]*)\]/) {
michael@428 60 $env{$1} = {} unless ($env{$1});
michael@428 61 $env = $env{$1};
michael@428 62 } elsif (my ($opt, $val) = ($line =~ m/^\-([$go])\s*(.*?)\s*$/)) {
michael@428 63 $val = 1 unless (defined($val));
michael@428 64 if (exists($env->{$opt})) {
michael@428 65 $env->{$opt} .= " " . $val;
michael@428 66 } else {
michael@428 67 $env->{$opt} = $val;
michael@428 68 }
michael@428 69 }
michael@428 70 }
michael@428 71 close(FH);
michael@428 72 }
michael@428 73
michael@428 74 # usage sanity check and usage help
michael@428 75 sub usage {
michael@428 76 my ($rc) = @_;
michael@428 77 my $usage =
michael@428 78 "openpkg:build:USAGE: $prg [options] [pattern ...]\n" .
michael@428 79 " -a operate on all installed packages\n" .
michael@428 80 " -A operate on all repository packages\n" .
michael@428 81 " -R <rpm> path to \"openpkg rpm\" command\n" .
michael@428 82 " -r <repository> URL to package repository directory\n" .
michael@428 83 " -f <index.rdf> URL to package repository index file\n" .
michael@428 84 " -u ignore local binary RPMs\n" .
michael@428 85 " -U upgrade all selected packages including dependencies\n" .
michael@428 86 " -z rebuild from zero all selected installed packages\n" .
michael@428 87 " -Z rebuild from zero all selected available packages\n" .
michael@428 88 " -i ignore errors in the generated script\n" .
michael@428 89 " -q ignore all reverse dependencies\n" .
michael@428 90 " -s generate status map instead of shell script\n" .
michael@428 91 " -S generate status map instead of shell script (including new)\n" .
michael@428 92 " -X use external XML/RDF parser instead of internal one\n" .
michael@428 93 " -M generate short dependency map instead of shell script\n" .
michael@428 94 " -L generate list of packages in repository depending on target\n" .
michael@428 95 " -W include dependencies as if all build options are enabled\n" .
michael@428 96 " -K keep temporarily installed packages\n" .
michael@428 97 " -k keep temporarily downloaded packages\n" .
michael@428 98 " -e rebuild exact version of a package from repository\n" .
michael@428 99 " -b build-time check existing binaries for dependencies only\n" .
michael@428 100 " -B build-time check existing binaries for dependencies and target\n" .
michael@428 101 " -g rebuild packages even when most recent version is installed\n" .
michael@428 102 " -P <priv-cmd> command prefix for privileged commands\n" .
michael@428 103 " -N <non-priv-cmd> command prefix for non-privileged commands\n" .
michael@428 104 " -p <platform> match platform against repository index for binary packages\n" .
michael@428 105 " -E <name> exclude package\n" .
michael@428 106 " -H <name> hint about packages to resolve ambiquity\n" .
michael@428 107 " -D <name>[=<val>] set build option for packages\n";
michael@428 108 if ($rc == 0) {
michael@428 109 print STDOUT $usage;
michael@428 110 }
michael@428 111 else {
michael@428 112 print STDERR $usage;
michael@428 113 }
michael@428 114 exit($rc);
michael@428 115 }
michael@428 116 if ($opt_h) {
michael@428 117 usage(0);
michael@428 118 }
michael@428 119 if (not ( ($#ARGV >= 0 && !($opt_a || $opt_A))
michael@428 120 || ($#ARGV == -1 && ($opt_a || $opt_A)))) {
michael@428 121 usage(1);
michael@428 122 };
michael@428 123
michael@428 124 # determine RPM run-time information
michael@428 125 my $config = rpm_runtime_info();
michael@428 126
michael@428 127 # override command line options with configuration script options
michael@428 128 # now that the effectively used OpenPKG RPM command is known
michael@428 129 foreach my $env (sort { $a cmp $b } grep {
michael@428 130 $config->{"rpm"} =~ m/^\Q$_\E/ # compatibility
michael@428 131 or $config->{"prefix"} =~ m/^\Q$_\E/ # regular
michael@428 132 } keys %env) {
michael@428 133 while (my ($opt, $val) = each(%{$env{$env}})) {
michael@428 134 eval "\$opt_$opt = '$val' unless defined \$opt_$opt;";
michael@428 135 }
michael@428 136 }
michael@428 137
michael@428 138 ##
michael@428 139 ## OPTION POST-PROCESSING
michael@428 140 ##
michael@428 141
michael@428 142 my ($url, $repository, $installed, $env, $list, $bonly, $clist);
michael@428 143 my ($pattern, %with, %exclude, %hint);
michael@428 144
michael@428 145 # determine package goal pattern
michael@428 146 if ($opt_a) {
michael@428 147 $pattern = undef;
michael@428 148 } else {
michael@428 149 $pattern = join(' ', @ARGV);
michael@428 150 }
michael@428 151 if ($opt_A) {
michael@428 152 $pattern = '*';
michael@428 153 }
michael@428 154
michael@428 155 # parse build options
michael@428 156 %with = map {
michael@428 157 m/([^\s=]+)(?:\=(\S+))?/
michael@428 158 ? ($1 => (defined($2) ? $2 : 'yes'))
michael@428 159 : ()
michael@428 160 } split(/\s+/, $opt_D);
michael@428 161
michael@428 162 # split accumulated option values
michael@428 163 %exclude = map { $_ => 1 } split(/\s+/, $opt_E);
michael@428 164 %hint = map { $_ => 1 } split(/\s+/, $opt_H);
michael@428 165
michael@428 166 if (defined($opt_p)) {
michael@428 167 $config->{platform} = $opt_p;
michael@428 168 }
michael@428 169
michael@428 170 # determine RPM package repository information
michael@428 171 if (defined $opt_r) {
michael@428 172 $url = $opt_r;
michael@428 173 $url .= '/' unless $url =~ m/\/$/;
michael@428 174 } else {
michael@428 175 $url = rpm_release_url();
michael@428 176 }
michael@428 177 # if we read the index from a file we can no longer deduce
michael@428 178 # repository paths from index paths. For now lets assume
michael@428 179 # that everything is below SRC/ to be compatible with
michael@428 180 # existing file indexes.
michael@428 181 if (defined($opt_f) and not defined($opt_r)) {
michael@428 182 $url .= 'SRC/';
michael@428 183 }
michael@428 184
michael@428 185 # determine information about INSTALLED packages (virtual and regular),
michael@428 186 # including their options, provides and requirements
michael@428 187 my $installed = get_installed();
michael@428 188
michael@428 189 # SPECIAL CASE post-processing for
michael@428 190 # -Z (ignore installed packages)
michael@428 191 # -a (operate for all installed packages)
michael@428 192 if ($opt_a and $opt_Z) {
michael@428 193 # This allows one to correctly upgrade an existing OpenPKG
michael@428 194 # instance to a newer major version by querying all installed
michael@428 195 # packages and their options (-a) but then ignore them (-Z) during
michael@428 196 # the later processing and instead perform more or less a fresh
michael@428 197 # rebuild from scratch. This ensures that during the process the
michael@428 198 # installed packages are effectively picked up as dependencies
michael@428 199 # only after they in turn were already updated.
michael@428 200 foreach my $package (keys(%{$installed})) {
michael@428 201 next if ($package =~ m/::/);
michael@428 202 if (exists($installed->{$package}->{""})) {
michael@428 203 # virtual package
michael@428 204 $hint{$installed->{$package}->{""}->[0]->{"name"}} = 1
michael@428 205 if (exists($installed->{$package}->{""}->[0]->{"name"}));
michael@428 206 }
michael@428 207 else {
michael@428 208 # regular package
michael@428 209 $pattern .= " $package";
michael@428 210 foreach my $version (keys(%{$installed->{$package}})) {
michael@428 211 foreach my $rec (@{$installed->{$package}->{$version}}) {
michael@428 212 if (defined($rec->{"OPTIONS"})) {
michael@428 213 my $options = $rec->{"OPTIONS"};
michael@428 214 foreach my $option (keys(%{$options})) {
michael@428 215 $with{$package."::".$option} = $options->{$option};
michael@428 216 }
michael@428 217 }
michael@428 218 }
michael@428 219 }
michael@428 220 }
michael@428 221 }
michael@428 222 }
michael@428 223 if ($opt_Z) {
michael@428 224 $installed = {};
michael@428 225 }
michael@428 226
michael@428 227 # determine information about AVAILABLE packages
michael@428 228 # by fetching and parsing a package repository XML/RDF index
michael@428 229 $repository = get_index(
michael@428 230 $url . '00INDEX.rdf',
michael@428 231 $opt_f,
michael@428 232 $opt_X,
michael@428 233 $config->{platform},
michael@428 234 $installed
michael@428 235 );
michael@428 236
michael@428 237 # assemble together all determined environment information
michael@428 238 $env = {
michael@428 239 config => $config,
michael@428 240 installed => $installed,
michael@428 241 repository => $repository,
michael@428 242 built => {},
michael@428 243 revdep => undef,
michael@428 244 with => \%with,
michael@428 245 exclude => \%exclude,
michael@428 246 hint => \%hint,
michael@428 247 upgrade => ($opt_a || $opt_U),
michael@428 248 zero => ($opt_z || $opt_Z),
michael@428 249 exact => $opt_e,
michael@428 250 quick => ($opt_q || $opt_z || $opt_Z),
michael@428 251 status => ($opt_s || $opt_S),
michael@428 252 fatal => [],
michael@428 253 goals => $opt_g,
michael@428 254 sourceonly => ($opt_u || $opt_U || $opt_z || $opt_Z)
michael@428 255 };
michael@428 256
michael@428 257 ##
michael@428 258 ## PERFORM REQUESTED OPERATION
michael@428 259 ##
michael@428 260
michael@428 261 if ($opt_L) {
michael@428 262 # case 1: calculate dependencies only and
michael@428 263 # print packages depending on target
michael@428 264 ($list) = build_deps($pattern, $env);
michael@428 265 print_deps($list);
michael@428 266 } else {
michael@428 267 # case 2: calculate build commands and
michael@428 268 # print results in different formats
michael@428 269 ($list, $bonly, $clist) = build_list($pattern, $env);
michael@428 270 die "openpkg:build:FATAL: cannot find package\n" if (not defined($list));
michael@428 271 if ($opt_M) {
michael@428 272 print_map($installed, $repository, $list, $bonly, $clist);
michael@428 273 } elsif ($opt_S) {
michael@428 274 print_status($installed, $repository, $list, $bonly, $clist);
michael@428 275 } elsif ($opt_s) {
michael@428 276 print_status($installed, {}, $list, $bonly, $clist);
michael@428 277 } else {
michael@428 278 if (@{$env->{fatal}}) {
michael@428 279 die "openpkg:build:FATAL: errors occured while building:\n", @{$env->{fatal}}, "\n";
michael@428 280 }
michael@428 281 print_list1($list, $config, $opt_a || $opt_u || $opt_U, $env->{with}, $opt_i, $opt_b, $opt_B);
michael@428 282 print_list2($bonly, $config) if (not $opt_K);
michael@428 283 }
michael@428 284 }
michael@428 285
michael@428 286 # die gracefully
michael@428 287 exit(0);
michael@428 288
michael@428 289 #############################################################################
michael@428 290 ##
michael@428 291 ## FUNCTIONS: PARSING & RUN-TIME INFORMATION
michael@428 292 ##
michael@428 293 #############################################################################
michael@428 294
michael@428 295 # home-brewn getopt(3) style option parser
michael@428 296 sub getopts ($) {
michael@428 297 my ($opts) = @_;
michael@428 298 my (%optf) = map { m/(\w)/; $1 => $_ } $opts =~ m/(\w:|\w)/g;
michael@428 299 my (%opts, @argv, $optarg);
michael@428 300
michael@428 301 foreach (@ARGV) {
michael@428 302 if (@argv) {
michael@428 303 push @argv, $_;
michael@428 304 } elsif (defined $optarg) {
michael@428 305 if (exists $opts{$optarg}) {
michael@428 306 $opts{$optarg} .= " $_";
michael@428 307 } else {
michael@428 308 $opts{$optarg} = $_;
michael@428 309 }
michael@428 310 $optarg = undef;
michael@428 311 } elsif (!/^[-]/) {
michael@428 312 push @argv, $_;
michael@428 313 } else {
michael@428 314 while (/^\-(\w)(.*)/) {
michael@428 315 if (exists $optf{$1}) {
michael@428 316 if (length($optf{$1}) > 1) {
michael@428 317 if ($2 ne '') {
michael@428 318 if (exists $opts{$1}) {
michael@428 319 $opts{$1} .= " $2";
michael@428 320 } else {
michael@428 321 $opts{$1} = $2;
michael@428 322 }
michael@428 323 } else {
michael@428 324 $optarg = $1;
michael@428 325 }
michael@428 326 last;
michael@428 327 } else {
michael@428 328 $opts{$1} = 1;
michael@428 329 }
michael@428 330 } else {
michael@428 331 warn "openpkg:build:WARNING: unknown option $_\n";
michael@428 332 }
michael@428 333 $_ = "-$2";
michael@428 334 }
michael@428 335 }
michael@428 336 }
michael@428 337 if (defined $optarg) {
michael@428 338 warn "openpkg:build:WARNING: option $optarg requires an argument\n";
michael@428 339 }
michael@428 340 foreach (keys %opts) {
michael@428 341 eval '$opt_'.$_.' = "'.quotemeta($opts{$_}).'";';
michael@428 342 }
michael@428 343 @ARGV = @argv;
michael@428 344 }
michael@428 345
michael@428 346 # determine RPM run-time information
michael@428 347 sub rpm_runtime_info () {
michael@428 348 # determine OpenPKG instance prefix via
michael@428 349 # 1. the environment of the "openpkg build" framework
michael@428 350 # 2. the installation path of the script
michael@428 351 # 3. the installation path of the Perl interpreter
michael@428 352 # 4. the path of the "openpkg" command in $PATH
michael@428 353 my $l_prefix = $ENV{'OPENPKG_PREFIX'};
michael@428 354 if (not $l_prefix) {
michael@428 355 ($l_prefix) = ($0 =~ m/^(.+)\/lib(exec)?\/openpkg(-tools)?\/build(\.pl)?$/);
michael@428 356 }
michael@428 357 if (not $l_prefix) {
michael@428 358 ($l_prefix) = ($^X =~ m/^(.+)\/bin\/perl.*$/);
michael@428 359 }
michael@428 360 if (not $l_prefix) {
michael@428 361 $l_prefix = (`(which openpkg) 2>/dev/null` =~ m/^(.+)\/bin\/openpkg$/);
michael@428 362 }
michael@428 363 if (not -x "$l_prefix/bin/openpkg") {
michael@428 364 die "openpkg:build:FATAL: cannot determine OpenPKG instance prefix";
michael@428 365 }
michael@428 366 print "# operating with OpenPKG instance $l_prefix\n";
michael@428 367
michael@428 368 # determine OpenPKG RPM command
michael@428 369 my $rpm = $opt_R || $env{''}->{'R'} ||
michael@428 370 ((-f "$l_prefix/bin/openpkg" && -f "$l_prefix/libexec/openpkg/rpm") ?
michael@428 371 "$l_prefix/bin/openpkg rpm" : "$l_prefix/bin/rpm");
michael@428 372 $rpm = (`(which $rpm) 2>/dev/null` =~ m{^(/.*)})[0] if ($rpm !~ m|^/|);
michael@428 373 die "openpkg:build:FATAL: cannot locate OpenPKG RPM in path" unless ($rpm =~ m{^/});
michael@428 374 print "# operating with OpenPKG RPM $rpm\n";
michael@428 375
michael@428 376 # determine additional tools
michael@428 377 my $mkp = "$l_prefix/bin/openpkg makeproxy";
michael@428 378 my $rel = "$l_prefix/bin/openpkg release";
michael@428 379 my $bzip2 = $rpm;
michael@428 380 $bzip2 =~ s/\/bin\/openpkg rpm$/\/lib\/openpkg\/bzip2/;
michael@428 381 my $curl = $rpm;
michael@428 382 $curl =~ s/\/bin\/openpkg rpm$/\/lib\/openpkg\/curl/;
michael@428 383 $curl = "$l_prefix/bin/openpkg curl" if (system("$l_prefix/bin/openpkg curl file://$l_prefix/etc/openpkg/platform >/dev/null 2>&1") == 0);
michael@428 384
michael@428 385 # expand RPM macros holding information
michael@428 386 my $c = run("$rpm --eval '%{_rpmdir} %{_rpmfilename} %{_target_os} %{_target_cpu} %{_srcrpmdir}'");
michael@428 387
michael@428 388 # parse and post-process information
michael@428 389 chomp($c);
michael@428 390 my (@q) = split(/\s+/, $c);
michael@428 391 $q[1] =~ s/%{OS}/$q[2]/;
michael@428 392 $q[1] =~ s/%{ARCH}/$q[3]/;
michael@428 393
michael@428 394 # expand RPM rc information about tools
michael@428 395 $c = run("$rpm --showrc");
michael@428 396 my @g = ($c =~ m/\%\{l_tool_locate\s+([^\s\}]+)/g);
michael@428 397
michael@428 398 # return accumulated information
michael@428 399 return {
michael@428 400 rpm => $rpm,
michael@428 401 mkp => $mkp,
michael@428 402 rel => $rel,
michael@428 403 bzip2 => $bzip2,
michael@428 404 curl => $curl,
michael@428 405 rpmdir => $q[0],
michael@428 406 srcrpmdir=> $q[4],
michael@428 407 template => $q[1],
michael@428 408 platform => '',
michael@428 409 prefix => $l_prefix,
michael@428 410 optreg => '(?:'. join('|', map { "\Quse_$_\E" } @g) .')'
michael@428 411 };
michael@428 412 }
michael@428 413
michael@428 414 # determine RPM release URL
michael@428 415 sub rpm_release_url ($$) {
michael@428 416 my ($rel, $url);
michael@428 417
michael@428 418 # determine the release URL the newer way
michael@428 419 $url = run("(".$config->{"rel"}." --fmt='%u') 2>/dev/null || true") || "";
michael@428 420 $url =~ s/^\s+//s;
michael@428 421 $url =~ s/\s+$//s;
michael@428 422
michael@428 423 # use a local jumpstart RDF
michael@428 424 if (-f $config->{"prefix"}."/etc/openpkg/build.rdf") {
michael@428 425 $url = "file://".$config->{"prefix"}."/etc/openpkg/build.rdf"
michael@428 426 }
michael@428 427
michael@428 428 return $url;
michael@428 429 }
michael@428 430
michael@428 431 #############################################################################
michael@428 432 ##
michael@428 433 ## FUNCTIONS: VERSION STRING HANDLING
michael@428 434 ##
michael@428 435 #############################################################################
michael@428 436
michael@428 437 # compare two package versions
michael@428 438 # - "openpkg rpm":
michael@428 439 # splits according to !isalnum(3) ([a-zA-Z0-9])
michael@428 440 # and between isdigit(3) ([0-9]) and isalpha(3) ([a-zA-Z])
michael@428 441 # - "openpkg build" (this):
michael@428 442 # splits on "." characters
michael@428 443 sub vcmp_version ($$) {
michael@428 444 my ($a, $b) = @_;
michael@428 445 my (@a, @b, $c);
michael@428 446 my ($ax, $bx);
michael@428 447
michael@428 448 # split according to dots
michael@428 449 @a = split(/\./, $a);
michael@428 450 @b = split(/\./, $b);
michael@428 451
michael@428 452 # compare as long as components exist
michael@428 453 while (@a && @b) {
michael@428 454 if ($a[0] =~ m/^\d+$/ && $b[0] =~ m/^\d+$/) {
michael@428 455 # numerical comparison
michael@428 456 $c = $a[0] <=> $b[0];
michael@428 457 } elsif ((($a, $ax) = $a[0] =~ m/^(\d+)(.*)$/) &&
michael@428 458 (($b, $bx) = $b[0] =~ m/^(\d+)(.*)$/)) {
michael@428 459 # numerical comparison for prefix,
michael@428 460 # string comparison for remainder
michael@428 461 $c = $a <=> $b;
michael@428 462 $c = $ax cmp $bx unless ($c);
michael@428 463 } else {
michael@428 464 # string comparison
michael@428 465 $c = $a[0] cmp $b[0];
michael@428 466 }
michael@428 467
michael@428 468 # stop comparison if components already mismatched
michael@428 469 return $c if ($c != 0);
michael@428 470
michael@428 471 # else reduce by one component level
michael@428 472 shift(@a);
michael@428 473 shift(@b);
michael@428 474 }
michael@428 475
michael@428 476 # finally compare number of remaining components
michael@428 477 # (in case one is more specific)
michael@428 478 $c = (scalar(@a) <=> scalar(@b));
michael@428 479 return $c;
michael@428 480 }
michael@428 481
michael@428 482 # compare two package releases
michael@428 483 # - "openpkg rpm":
michael@428 484 # uses "vcmp_version" semantics 1:1 again
michael@428 485 # - "openpkg build" (this):
michael@428 486 # uses "vcmp_version" semantics 1:1 again (>= 20060719)
michael@428 487 # trivial string comparison (<= 20060719)
michael@428 488 sub vcmp_release ($$) {
michael@428 489 my ($a, $b) = @_;
michael@428 490
michael@428 491 return vcmp_version($a, $b);
michael@428 492 }
michael@428 493
michael@428 494 # compare two package "version" or "version-release" strings
michael@428 495 # - "openpkg rpm":
michael@428 496 # compares "epoch", "version", "release" (in this order)
michael@428 497 # - "openpkg build" (this):
michael@428 498 # compares "release", "version", (in this order)
michael@428 499 sub vcmp ($$) {
michael@428 500 my ($a, $b) = @_;
michael@428 501 my ($c);
michael@428 502
michael@428 503 # short-circuit comparison for simple case
michael@428 504 return 0 if ($a eq $b);
michael@428 505
michael@428 506 # split into "version" and "release"
michael@428 507 my ($av, $ar) = ($a =~ m/^(.*?)(?:\-([\d\.]+))?$/);
michael@428 508 my ($bv, $br) = ($b =~ m/^(.*?)(?:\-([\d\.]+))?$/);
michael@428 509
michael@428 510 # compare "release"
michael@428 511 if (defined($ar) and defined($br)) {
michael@428 512 $c = vcmp_release($ar, $br);
michael@428 513 return $c if ($c); # short-circuit
michael@428 514 }
michael@428 515
michael@428 516 # compare "version"
michael@428 517 if (defined($av) && defined($bv)) {
michael@428 518 $c = vcmp_version($av, $bv);
michael@428 519 return $c if ($c); # short-circuit
michael@428 520 }
michael@428 521
michael@428 522 # equality
michael@428 523 return 0;
michael@428 524 }
michael@428 525
michael@428 526 # create "version" or "version-release" string
michael@428 527 # from a provide object (see parse_provides)
michael@428 528 sub vs ($) {
michael@428 529 my ($t) = @_;
michael@428 530 return (
michael@428 531 defined($t->{release})
michael@428 532 ? "$t->{version}-$t->{release}"
michael@428 533 : $t->{version}
michael@428 534 );
michael@428 535 }
michael@428 536
michael@428 537 # create "name-version" or "name-version-release" string
michael@428 538 # from a provide object (see parse_provides)
michael@428 539 sub vsn ($) {
michael@428 540 my ($t) = @_;
michael@428 541 return "$t->{name}-".vs($t);
michael@428 542 }
michael@428 543
michael@428 544 #############################################################################
michael@428 545 ##
michael@428 546 ## FUNCTIONS: INSTALLATION INFORMATION
michael@428 547 ##
michael@428 548 #############################################################################
michael@428 549
michael@428 550 # parse OpenPKG RPM 'provide' string
michael@428 551 # "<virtual-name>" (virtual package)
michael@428 552 # "<name> = <version>-<release>" (regular package)
michael@428 553 # "<name>::<option> = <value>" (regular package build option)
michael@428 554 sub parse_provides ($) {
michael@428 555 my ($s) = @_;
michael@428 556 my ($nam, $val, $pre, $with, $pxy, $ver, $rel);
michael@428 557
michael@428 558 ($nam, $val) = ($s =~ m/^([^\s\(]+(?:\([^\)]*\))?)\s*(?:=\s*(\S*?))?$/);
michael@428 559 if (($pre, $with) = ($nam =~ m/^(\S+?)::(\S*)$/)) {
michael@428 560 # build option
michael@428 561 $val =~ s/(?:\%([0-9a-fA-F][0-9a-fA-F]))/chr(hex($1))/eg; # hex decode
michael@428 562 ($ver, $rel, $pxy) = ($val, undef, undef);
michael@428 563 } else {
michael@428 564 # virtual or real package
michael@428 565 ($ver, $rel, $pxy) = ($val =~ m/^([^\s\-]+)-([^\s\+]+)(\+PROXY)?$/);
michael@428 566 }
michael@428 567
michael@428 568 # return accumulated information
michael@428 569 return {
michael@428 570 name => $nam, # the full name of the resource
michael@428 571 version => $ver, # the version (or value)
michael@428 572 release => $rel, # and release number (or undef)
michael@428 573 proxy => $pxy, # whether the resource is a PROXY resource (or undef)
michael@428 574 prefix => $pre, # the packagename (if resource is an option)
michael@428 575 with => $with # the buildoption (if resource is an option)
michael@428 576 };
michael@428 577 }
michael@428 578
michael@428 579 # parse option from RPM 'provides' list
michael@428 580 sub parse_provideslist ($) {
michael@428 581 my ($l) = @_;
michael@428 582 my ($p);
michael@428 583 my ($nam, $val, %opts);
michael@428 584
michael@428 585 foreach (@$l) {
michael@428 586 $p = parse_provides($_);
michael@428 587 next if (not (defined $p->{with} && defined $p->{prefix}));
michael@428 588 $opts{$p->{with}} = $p->{version};
michael@428 589 }
michael@428 590 return \%opts;
michael@428 591 }
michael@428 592
michael@428 593 # translate dependency object into provides object
michael@428 594 sub depends2provides ($) {
michael@428 595 my ($dep) = @_;
michael@428 596 my ($ver, $rel, $pxy, $pre, $with);
michael@428 597
michael@428 598 ($ver, $rel, $pxy) = ($dep->{val} =~ m/^([^\s\-]+)-([^\s\+]+)(\+PROXY)?$/);
michael@428 599 ($pre, $with) = ($dep->{name} =~ m/^(\S+?)::(\S*)$/);
michael@428 600
michael@428 601 return {
michael@428 602 name => $dep->{name},
michael@428 603 version => (defined $ver ? $ver : $dep->{val}),
michael@428 604 release => $rel,
michael@428 605 proxy => $pxy,
michael@428 606 prefix => $pre,
michael@428 607 with => $with
michael@428 608 }
michael@428 609 }
michael@428 610
michael@428 611 # parse OpenPKG RPM 'require' string
michael@428 612 # "<virtual-name>" (virtual package)
michael@428 613 # "<name> =|<|<=|>|>= <version>[-<release>]" (regular package)
michael@428 614 # "<name>::<option> =|<|<=|>|>= <value>" (regular package build option)
michael@428 615 sub parse_depends ($) {
michael@428 616 my ($dep) = @_;
michael@428 617 my ($name, $op, $val);
michael@428 618
michael@428 619 if (ref($dep)) {
michael@428 620 # dependency from new index stored as a node
michael@428 621 # - content of the node is the name
michael@428 622 # - certain attributes denote the comparison operator
michael@428 623 # - the value of such an attribute is the comparison operand
michael@428 624 # - the operator (and operand) are optional and there can only be one
michael@428 625 $name = $dep->{content};
michael@428 626 $op = undef;
michael@428 627 $op = 'equ' if (exists($dep->{equ}));
michael@428 628 $op = 'geq' if (exists($dep->{geq}));
michael@428 629 $op = 'leq' if (exists($dep->{leq}));
michael@428 630 $op = 'gt' if (exists($dep->{gt}));
michael@428 631 $op = 'lt' if (exists($dep->{lt}));
michael@428 632 if (defined($op)) {
michael@428 633 $val = $dep->{$op};
michael@428 634 }
michael@428 635 } elsif ($dep =~ m/\S/) {
michael@428 636 # dependency from old index stored as text string
michael@428 637 # "name operator operand" or "name"
michael@428 638 ($name, $op, $val) = ($dep =~ m/(\S+)\s*(?:(\S+)\s*(\S+))?\s*$/);
michael@428 639 if (defined($op)) {
michael@428 640 $op = {
michael@428 641 '==' => 'equ', '=' => 'equ',
michael@428 642 '>=' => 'geq', '=>' => 'geq',
michael@428 643 '<=' => 'leq', '=<' => 'leq',
michael@428 644 '>' => 'gt', '<' => 'lt'
michael@428 645 }->{$op};
michael@428 646 if (not defined($op)) {
michael@428 647 print "# don't know how to handle dependency: $dep (invalid operator)\n";
michael@428 648 return;
michael@428 649 }
michael@428 650 }
michael@428 651 }
michael@428 652 return {
michael@428 653 name => $name,
michael@428 654 op => $op,
michael@428 655 val => $val
michael@428 656 };
michael@428 657 }
michael@428 658
michael@428 659 # retrieve the local installed package base.
michael@428 660 # for packages that provide option resources (packagename::buildoption)
michael@428 661 # the options are parsed into the OPTIONS hash.
michael@428 662 # other packages will query options on demand.
michael@428 663 sub get_installed () {
michael@428 664 my (%map);
michael@428 665 my (@l, $p);
michael@428 666 my ($nam, $val, %options);
michael@428 667 my ($vs, $rec, @list);
michael@428 668 my ($name, $version, $release);
michael@428 669 my ($req);
michael@428 670
michael@428 671 # generated total result:
michael@428 672 # $map = {
michael@428 673 # # regular package
michael@428 674 # "<package-name>" <foo> => {
michael@428 675 # "<version>-<release>" <1.2.3-20060622> => [
michael@428 676 # <<1>{
michael@428 677 # "name" => $name,
michael@428 678 # "version" => $version,
michael@428 679 # "release" => $release,
michael@428 680 # "PROXY" => $proxy,
michael@428 681 # "depends" => [
michael@428 682 # <<3>>{
michael@428 683 # "cond" => '',
michael@428 684 # "value" => {
michael@428 685 # name => $name,
michael@428 686 # op => $op,
michael@428 687 # val => $val
michael@428 688 # },
michael@428 689 # },
michael@428 690 # ...
michael@428 691 # ],
michael@428 692 # "keeps" => [
michael@428 693 # \<<3>
michael@428 694 # ...
michael@428 695 # ],
michael@428 696 # "OPTIONS" => {
michael@428 697 # "<option>" => "<value>",
michael@428 698 # "<option>" => "<value>",
michael@428 699 # ...
michael@428 700 # },
michael@428 701 # },
michael@428 702 # ...
michael@428 703 # ],
michael@428 704 # },
michael@428 705 # # build option
michael@428 706 # "<package-name>::<option>" <foo::with_baz> => {
michael@428 707 # "<value>" <yes> => [
michael@428 708 # \<<1>>
michael@428 709 # ...
michael@428 710 # ],
michael@428 711 # },
michael@428 712 # # virtual package
michael@428 713 # "<package-name>" <BAR> => {
michael@428 714 # "" => [
michael@428 715 # \<<1>>,
michael@428 716 # ...
michael@428 717 # ],
michael@428 718 # },
michael@428 719 # ...
michael@428 720 # };
michael@428 721
michael@428 722 # query and parse all provides of all packages
michael@428 723 # HINT: We assume(!) that OpenPKG RPM outputs "provides" in order:
michael@428 724 # 1. virtual package & build option
michael@428 725 # 2. regular package
michael@428 726 # FIXME: The better long-term solution for all this fiddling would be something like:
michael@428 727 # "openpkg rpm -qa -qf '%{NAME} %{VERSION} %{RELEASE}[ .%{PROVIDENAME} .%{PROVIDEFLAGS:depflags} .%{PROVIDEVERSION}]\\n'"
michael@428 728 @l = run($config->{"rpm"}. " --provides -qa");
michael@428 729 @list = ();
michael@428 730 foreach (@l) {
michael@428 731 # parse into provide object
michael@428 732 $p = parse_provides($_) or next;
michael@428 733
michael@428 734 # short-circuit processing for RPM special case
michael@428 735 next if ($p->{name} =~ m/^gpg\(/);
michael@428 736
michael@428 737 # is this an option?
michael@428 738 if (defined($p->{with})) {
michael@428 739 $options{$p->{prefix}}->{$p->{with}} = $p->{version};
michael@428 740 push(@list, $p);
michael@428 741 next;
michael@428 742 }
michael@428 743
michael@428 744 # is this a virtual target?
michael@428 745 $vs = vs($p);
michael@428 746 if ($vs eq '') {
michael@428 747 push(@list, $p);
michael@428 748 next;
michael@428 749 }
michael@428 750
michael@428 751 # assemble package details
michael@428 752 $name = $p->{name};
michael@428 753 $version = defined($p->{version}) ? $p->{version} : '*';
michael@428 754 $release = defined($p->{release}) ? $p->{release} : '*';
michael@428 755 push(@list, {
michael@428 756 name => $name,
michael@428 757 version => $version,
michael@428 758 release => $release
michael@428 759 });
michael@428 760
michael@428 761 # create target record
michael@428 762 $rec = {
michael@428 763 name => $name,
michael@428 764 version => $version,
michael@428 765 release => $release,
michael@428 766 PROXY => $p->{proxy},
michael@428 767 depends => [],
michael@428 768 keeps => []
michael@428 769 };
michael@428 770 foreach (@list) {
michael@428 771 push(@{$map{$_->{name}}->{vs($_)}}, $rec);
michael@428 772 }
michael@428 773
michael@428 774 # remove assembled details
michael@428 775 @list = ();
michael@428 776 }
michael@428 777 if (@list) {
michael@428 778 print "# ATTENTION: ", scalar(@list), " froods (unassignable RPM 'provides') left\n";
michael@428 779 }
michael@428 780
michael@428 781 # options are provided for a package,
michael@428 782 # apply them to all instances of the package
michael@428 783 # FIXME: duplicate copying because record exists multiple times (but harmless)
michael@428 784 # FIXME: merges all "provides" of all package instances together -- which might be wrong
michael@428 785 foreach $nam (keys(%options)) {
michael@428 786 foreach $val (keys(%{$map{$nam}})) {
michael@428 787 foreach (@{$map{$nam}->{$val}}) {
michael@428 788 $_->{OPTIONS} = $options{$nam};
michael@428 789 }
michael@428 790 }
michael@428 791 }
michael@428 792
michael@428 793 # query all 'requires' of all installed packages
michael@428 794 # to determine the package dependencies
michael@428 795 @l = run($config->{"rpm"} . " --qf '%{NAME}:::%{VERSION}:::%{RELEASE}[ :::%{REQUIRENAME}:::%{REQUIREFLAGS:depflags}:::%{REQUIREVERSION}:::]\\n' -qa");
michael@428 796 @list = ();
michael@428 797 foreach (@l) {
michael@428 798 ($name, $version, $release, $req) = m/^([^:]+):::([^:]+):::([^:]+)(.*?)$/;
michael@428 799 next if ($name eq 'gpg-pubkey');
michael@428 800 $release =~ s/\+PROXY$//;
michael@428 801 # for each requirement triple...
michael@428 802 while ($req =~ m/\s+:::(.+?):::\s*(.*?)\s*:::(.*?):::/g) {
michael@428 803 $p = parse_depends("$1 $2 $3");
michael@428 804 next if ($p->{name} =~ m/^(rpmlib|gpg)\(/);
michael@428 805 $vs = vs({ version => $version, release => $release });
michael@428 806 $p = { cond => '', value => $p };
michael@428 807 foreach $rec (@{$map{$name}->{$vs}}) {
michael@428 808 push(@{$rec->{depends}}, $p);
michael@428 809 push(@{$rec->{keeps}}, $p);
michael@428 810 }
michael@428 811 }
michael@428 812 }
michael@428 813 if (@list) {
michael@428 814 print "# ATTENTION: ",scalar(@list)," fnords (unassignable RPM 'requires') left\n";
michael@428 815 }
michael@428 816
michael@428 817 # return final result
michael@428 818 return \%map;
michael@428 819 }
michael@428 820
michael@428 821 #############################################################################
michael@428 822 ##
michael@428 823 ## FUNCTIONS: REPOSITORY INDEX INFORMATION
michael@428 824 ##
michael@428 825 #############################################################################
michael@428 826
michael@428 827 # fetch XML/RDF index from file or URL
michael@428 828 # (recursively fetches sub-indexes, too)
michael@428 829 sub get_index ($$$$$) {
michael@428 830 my ($url, $fn, $xml, $pfmatch, $installed) = @_;
michael@428 831 my (%map, $include);
michael@428 832 my ($fetch, $bzip2, $path);
michael@428 833 my ($parser);
michael@428 834
michael@428 835 # determine command/path to fetch/open index
michael@428 836 $bzip2 = $config->{"bzip2"};
michael@428 837 $fetch = defined($fn) ? $fn : $url;
michael@428 838 $fetch !~ m/\.bz2$/ || -x $bzip2
michael@428 839 or die "openpkg:build:FATAL: $bzip2 not found\n";
michael@428 840 if ($fetch =~ m/^\w+:/) {
michael@428 841 # looks like URL scheme
michael@428 842 print "# fetching XML/RDF index from URL $fetch\n";
michael@428 843 $path = $config->{"curl"} . " -s -o - \"$fetch\" |";
michael@428 844 $path .= "$bzip2 -dc |" if ($fetch =~ m/\.bz2$/);
michael@428 845 } else {
michael@428 846 print "# reading XML/RDF index from file $fetch\n";
michael@428 847 if ($fetch =~ m/\.bz2$/) {
michael@428 848 $path = "$bzip2 -dc $fetch |";
michael@428 849 } else {
michael@428 850 $path = "<$fetch";
michael@428 851 }
michael@428 852 }
michael@428 853
michael@428 854 # open index
michael@428 855 open(RFH, $path) or
michael@428 856 die "openpkg:build:FATAL: cannot open '$fetch' ($!)\n";
michael@428 857
michael@428 858 # if XML parser can be used, try to lazy-load it
michael@428 859 if ($xml) {
michael@428 860 eval { require XML::Simple; };
michael@428 861 $xml = 0 if ($@);
michael@428 862 }
michael@428 863
michael@428 864 # determine and run XML parser
michael@428 865 # (returns contained index includes)
michael@428 866 $parser = ($xml ? \&xml_parser : \&simple_text_parser);
michael@428 867 $include = $parser->(\*RFH, $url, \%map, $pfmatch, $installed);
michael@428 868
michael@428 869 # close index
michael@428 870 close(RFH)
michael@428 871 or die "openpkg:build:FATAL: an I/O error occured\n";
michael@428 872
michael@428 873 # cannot do real recursions on file handles, so we simply append
michael@428 874 # (instead of inserting at the correct position) all sub-RDFs, as
michael@428 875 # the result is flattend into a big hash anyway
michael@428 876 foreach (@$include) {
michael@428 877 my ($submap);
michael@428 878 my ($suburl, $subfn) = relurl($url, $fn, $_);
michael@428 879 $submap = get_index($suburl, $subfn, $xml, $pfmatch, $installed); # RECURSION
michael@428 880 while (my ($name, $vmap) = each(%$submap)) {
michael@428 881 while (my ($vs, $recs) = each(%$vmap)) {
michael@428 882 push(@{$map{$name}->{$vs}}, @$recs);
michael@428 883 }
michael@428 884 }
michael@428 885 }
michael@428 886
michael@428 887 # return final result
michael@428 888 # $map = {
michael@428 889 # <package-name> => {
michael@428 890 # "<version>-<release>" => {
michael@428 891 # href => ...,
michael@428 892 # name => ...,
michael@428 893 # version => ...,
michael@428 894 # release => ...,
michael@428 895 # platform => ...,
michael@428 896 # prefix => ...,
michael@428 897 # depends => [ ... ],
michael@428 898 # keeps => [ ... ],
michael@428 899 # conflicts => [ ... ],
michael@428 900 # source => ...,
michael@428 901 # nosource => ...,
michael@428 902 # desc => $desc,
michael@428 903 # OPTIONS => $options,
michael@428 904 # DEFOPTS => { %$options },
michael@428 905 # };
michael@428 906 # };
michael@428 907 # };
michael@428 908 return \%map;
michael@428 909 }
michael@428 910
michael@428 911 # compute absolute paths
michael@428 912 # - (url, fn) point to a base document
michael@428 913 # the location is the file path fn if fn is
michael@428 914 # defined, otherwise it is url.
michael@428 915 # - augment the pointer with suburl
michael@428 916 # - suburl can be an absolute url
michael@428 917 # then the new pointer is (suburl, undef)
michael@428 918 # - suburl can be a absolute file path
michael@428 919 # then the new pointer is (suburl, suburl)
michael@428 920 # - suburl can be a relative path
michael@428 921 # then it augments url or fn accordingly
michael@428 922 sub relurl ($$$) {
michael@428 923 my ($url, $fn, $suburl) = @_;
michael@428 924 my ($subfn);
michael@428 925
michael@428 926 if ($suburl =~ m/^\w+:\/\//) {
michael@428 927 # NOP
michael@428 928 } elsif ($suburl =~ m/^\//) {
michael@428 929 $subfn = $suburl;
michael@428 930 } else {
michael@428 931 if (defined($fn)) {
michael@428 932 $subfn = $fn;
michael@428 933 $subfn =~ s/(\/)?\/*[^\/]*$/$1$suburl/;
michael@428 934 $suburl = $subfn;
michael@428 935 } else {
michael@428 936 $subfn = $url;
michael@428 937 $subfn =~ s/(\/)?\/*[^\/]*$/$1$suburl/;
michael@428 938 $suburl = $subfn;
michael@428 939 $subfn = undef;
michael@428 940 }
michael@428 941 }
michael@428 942 1 while ($suburl =~ s/\/\.\//\//s);
michael@428 943 1 while ($suburl =~ s/\/[^\/]+\/\.\.\//\//s);
michael@428 944 return ($suburl, $subfn);
michael@428 945 }
michael@428 946
michael@428 947 # XML/RDF parser (simple way)
michael@428 948 sub simple_text_parser ($$$$$) {
michael@428 949 my ($fh, $url, $map, $pfmatch, $installed) = @_;
michael@428 950 my (@include);
michael@428 951 my ($section);
michael@428 952 my ($name, $version);
michael@428 953 my ($href, $release, $desc, $bags);
michael@428 954 my (%options, @provides);
michael@428 955 my ($platform, $prefix);
michael@428 956 my ($rec);
michael@428 957 my ($tag, $cond, $attrname, $attrval, $body);
michael@428 958 my ($usecond);
michael@428 959 my ($options);
michael@428 960
michael@428 961 print "# using internal XML/RDF parser\n";
michael@428 962
michael@428 963 # read XML/RDF line-wise as we know that our
michael@428 964 # OpenPKG XML/RDF indices follow a strict formatting
michael@428 965 while (<$fh>) {
michael@428 966 # unescape some XML entities
michael@428 967 s/&gt;/>/g;
michael@428 968 s/&lt;/</g;
michael@428 969
michael@428 970 if (!(defined($href)) && m/<rdf:Description.*?href="([^"]*)"/) {
michael@428 971 # start of new package description
michael@428 972 $href = $1;
michael@428 973 $section = undef;
michael@428 974 $name = undef;
michael@428 975 $release = undef;
michael@428 976 $desc = '';
michael@428 977 $platform = undef;
michael@428 978 $prefix = undef;
michael@428 979 $bags = {};
michael@428 980 @provides = ();
michael@428 981 }
michael@428 982
michael@428 983 if (!(defined($href)) && m/<Repository.*?href="([^"]*)"(?:\s*platform="([^"]*)")?/) {
michael@428 984 # external XML/RDF index reference for particular binary platform
michael@428 985 if (goodpf($2, $pfmatch)) {
michael@428 986 push(@include, $1);
michael@428 987 }
michael@428 988 next;
michael@428 989 }
michael@428 990
michael@428 991 # skip content unless referenced piece was found
michael@428 992 next if (not defined($href));
michael@428 993
michael@428 994 # parse XML/RDF element into components
michael@428 995 ($tag, $cond, $attrname, $attrval, $body) = m{
michael@428 996 < # start delimiter
michael@428 997 (\/?[\w:]+) # begin element name
michael@428 998 \s* # optional space before attributes
michael@428 999 (?:cond="([^"]+)")? # known attribute
michael@428 1000 (?:(\w+)="([^"]+)")? # unknown attribute
michael@428 1001 > # end delimiter
michael@428 1002 (.*?) # optional element body
michael@428 1003 (?:<\/\1>)? # optional end tag
michael@428 1004 $ # end of string
michael@428 1005 }mx;
michael@428 1006
michael@428 1007 # recognize the various XML/RDF elements
michael@428 1008 if ($tag eq 'Description') {
michael@428 1009 $usecond = $cond;
michael@428 1010 $section = 'description';
michael@428 1011 } elsif ($tag eq '/Description') {
michael@428 1012 $usecond = $cond;
michael@428 1013 $section = undef;
michael@428 1014 } elsif ($section eq 'description') {
michael@428 1015 $desc .= $_;
michael@428 1016 } elsif ($tag eq 'PreReq') {
michael@428 1017 $usecond = $cond;
michael@428 1018 $section = 'prereq';
michael@428 1019 } elsif ($tag eq '/PreReq') {
michael@428 1020 $usecond = undef;
michael@428 1021 $section = undef;
michael@428 1022 } elsif ($tag eq 'BuildPreReq') {
michael@428 1023 $usecond = $cond;
michael@428 1024 $section = 'bprereq';
michael@428 1025 } elsif ($tag eq '/BuildPreReq') {
michael@428 1026 $usecond = undef;
michael@428 1027 $section = undef;
michael@428 1028 } elsif ($tag eq 'Provides') {
michael@428 1029 $usecond = $cond;
michael@428 1030 $section = 'provides';
michael@428 1031 } elsif ($tag eq '/Provides') {
michael@428 1032 $usecond = undef;
michael@428 1033 $section = undef;
michael@428 1034 } elsif ($tag eq 'Conflicts') {
michael@428 1035 $usecond = $cond;
michael@428 1036 $section = 'conflicts';
michael@428 1037 } elsif ($tag eq '/Conflicts') {
michael@428 1038 $usecond = undef;
michael@428 1039 $section = undef;
michael@428 1040 } elsif ($tag eq 'NoSource') {
michael@428 1041 $usecond = $cond;
michael@428 1042 $section = 'nosource';
michael@428 1043 } elsif ($tag eq '/NoSource') {
michael@428 1044 $usecond = undef;
michael@428 1045 $section = undef;
michael@428 1046 } elsif ($tag eq 'Source') {
michael@428 1047 $usecond = $cond;
michael@428 1048 $section = 'source';
michael@428 1049 } elsif ($tag eq '/Source') {
michael@428 1050 $usecond = undef;
michael@428 1051 $section = undef;
michael@428 1052 } elsif ($tag eq 'Name') {
michael@428 1053 $name = $body;
michael@428 1054 } elsif ($tag eq 'Version') {
michael@428 1055 $version = $body;
michael@428 1056 } elsif ($tag eq 'Release') {
michael@428 1057 $release = $body;
michael@428 1058 } elsif ($tag eq 'Platform') {
michael@428 1059 $platform = $body;
michael@428 1060 } elsif ($tag eq 'Prefixes') {
michael@428 1061 $prefix = $body;
michael@428 1062 } elsif ($tag eq 'rdf:li' || $tag eq 'resource') {
michael@428 1063 if (defined($attrname)) {
michael@428 1064 $body = {
michael@428 1065 $attrname => $attrval,
michael@428 1066 content => $body
michael@428 1067 };
michael@428 1068 }
michael@428 1069 if ($section eq 'provides') {
michael@428 1070 push(@provides, $body) if (!defined($usecond));
michael@428 1071 } elsif ($section ne '') {
michael@428 1072 push(@{$bags->{"$usecond"}->{$section}}, $body);
michael@428 1073 }
michael@428 1074 } elsif ($tag eq '/rdf:Description') {
michael@428 1075 if ( defined($href)
michael@428 1076 && defined($name)
michael@428 1077 && defined($version)
michael@428 1078 && defined($release)) {
michael@428 1079 # process the accumulated package information
michael@428 1080 @provides = map {
michael@428 1081 depends2provides(parse_depends($_))
michael@428 1082 } @provides;
michael@428 1083 %options = map {
michael@428 1084 ($_->{with} => $_->{version})
michael@428 1085 } grep {
michael@428 1086 defined($_->{with})
michael@428 1087 } @provides;
michael@428 1088 push(@provides, {
michael@428 1089 name => $name,
michael@428 1090 version => $version,
michael@428 1091 release => $release
michael@428 1092 });
michael@428 1093 $options =
michael@428 1094 %options
michael@428 1095 ? { %options }
michael@428 1096 : parse_options($desc);
michael@428 1097 if ($options) {
michael@428 1098 my (@t) = get_targets($installed->{$name}, sub { 1; });
michael@428 1099 }
michael@428 1100 # store accumulated package information
michael@428 1101 eval {
michael@428 1102 $rec = {
michael@428 1103 href => (relurl($url, undef, $href))[0],
michael@428 1104 name => $name,
michael@428 1105 version => $version,
michael@428 1106 release => $release,
michael@428 1107 depends => depend_list(swith($bags, 'bprereq')),
michael@428 1108 keeps => depend_list(swith($bags, 'prereq')),
michael@428 1109 conflicts => swith($bags, 'conflicts'),
michael@428 1110 source => swith($bags, 'source'),
michael@428 1111 nosource => swith($bags, 'nosource'),
michael@428 1112 desc => $desc,
michael@428 1113 platform => $platform,
michael@428 1114 prefix => $prefix,
michael@428 1115 OPTIONS => $options,
michael@428 1116 DEFOPTS => { %$options }
michael@428 1117 };
michael@428 1118 };
michael@428 1119 if ($@) {
michael@428 1120 die "openpkg:build:FATAL: when reading entry '$name':\n" . $@;
michael@428 1121 }
michael@428 1122 foreach (@provides) {
michael@428 1123 push(@{$map->{$_->{name}}->{vs($_)}}, $rec);
michael@428 1124 }
michael@428 1125 }
michael@428 1126 # prepare to recognize next package
michael@428 1127 $href = undef;
michael@428 1128 }
michael@428 1129 }
michael@428 1130
michael@428 1131 # return contained XML/RDF indices
michael@428 1132 return \@include;
michael@428 1133 }
michael@428 1134
michael@428 1135 # XML/RDF parser (usual way)
michael@428 1136 sub xml_parser ($$$$$) {
michael@428 1137 my ($fh, $url, $map, $pfmatch, $installed) = @_;
michael@428 1138 my (@include);
michael@428 1139 my ($xml, $rep, $sub);
michael@428 1140 my (@provides, %options, $rec);
michael@428 1141 my ($href, $name, $version, $release, $desc);
michael@428 1142 my ($options);
michael@428 1143
michael@428 1144 print "# using external XML/RDF parser\n";
michael@428 1145
michael@428 1146 # parse XML/RDF with XML::Simple parser
michael@428 1147 $xml = XML::Simple::XMLin($fh, forcearray => 1);
michael@428 1148 $rep = $xml->{'Repository'}->[0]->{'rdf:Description'};
michael@428 1149 $sub = $xml->{'Repository'}->[0]->{'Repository'};
michael@428 1150
michael@428 1151 # iterate over all package descriptions
michael@428 1152 foreach (@$rep) {
michael@428 1153 # fetch package information
michael@428 1154 $href = $_->{'href'};
michael@428 1155 $name = xel($_->{'Name'});
michael@428 1156 $version = xel($_->{'Version'});
michael@428 1157 $release = xel($_->{'Release'});
michael@428 1158 next if (not (
michael@428 1159 defined($href)
michael@428 1160 && defined($name)
michael@428 1161 && defined($version)
michael@428 1162 && defined($release)
michael@428 1163 ));
michael@428 1164
michael@428 1165 # determine package "provides"
michael@428 1166 @provides = ();
michael@428 1167 if ($_->{'Provides'}) {
michael@428 1168 @provides = map {
michael@428 1169 $_ = $_->{'rdf:bag'}->[0];
michael@428 1170 $_ = $_->{'rdf:li'} ? $_->{'rdf:li'} : $_->{'resource'};
michael@428 1171 @$_;
michael@428 1172 } grep {
michael@428 1173 !exists $_->{'cond'}
michael@428 1174 } @{$_->{'Provides'}};
michael@428 1175 }
michael@428 1176 @provides = map {
michael@428 1177 depends2provides(parse_depends($_))
michael@428 1178 } @provides;
michael@428 1179 %options = map {
michael@428 1180 ($_->{with} => $_->{version})
michael@428 1181 } grep {
michael@428 1182 defined $_->{with}
michael@428 1183 } @provides;
michael@428 1184 push(@provides, {
michael@428 1185 name => $name,
michael@428 1186 version => $version,
michael@428 1187 release => $release
michael@428 1188 });
michael@428 1189
michael@428 1190 # determine targets
michael@428 1191 $desc = xel($_->{'Description'});
michael@428 1192 $options =
michael@428 1193 %options
michael@428 1194 ? { %options }
michael@428 1195 : parse_options($desc);
michael@428 1196 if ($options) {
michael@428 1197 my (@t) = get_targets($installed->{$name}, sub { 1; });
michael@428 1198 }
michael@428 1199
michael@428 1200 # store accumulated package information
michael@428 1201 eval {
michael@428 1202 $rec = {
michael@428 1203 href => (relurl($url, undef, $href))[0],
michael@428 1204 name => $name,
michael@428 1205 version => $version,
michael@428 1206 release => $release,
michael@428 1207 platform => xel($_->{'Platform'}),
michael@428 1208 prefix => xel($_->{'Prefixes'}),
michael@428 1209 depends => depend_list(xwith($_->{'BuildPreReq'})),
michael@428 1210 keeps => depend_list(xwith($_->{'PreReq'})),
michael@428 1211 conflicts => xwith($_->{'Conflicts'}),
michael@428 1212 source => xwith($_->{'Source'}),
michael@428 1213 nosource => xwith($_->{'NoSource'}),
michael@428 1214 desc => $desc,
michael@428 1215 OPTIONS => $options,
michael@428 1216 DEFOPTS => { %$options }
michael@428 1217 };
michael@428 1218 };
michael@428 1219 if ($@) {
michael@428 1220 die "openpkg:build:FATAL: when reading entry '$name'\n".$@;
michael@428 1221 }
michael@428 1222 foreach (@provides) {
michael@428 1223 push(@{$map->{$_->{name}}->{vs($_)}}, $rec);
michael@428 1224 }
michael@428 1225 }
michael@428 1226
michael@428 1227 # determine contained XML/RDF indices
michael@428 1228 if ($sub) {
michael@428 1229 @include = map {
michael@428 1230 goodpf($_->{platform}, $pfmatch)
michael@428 1231 ? ( $_->{href} )
michael@428 1232 : ( )
michael@428 1233 } @$sub;
michael@428 1234 }
michael@428 1235
michael@428 1236 # return contained XML/RDF indices
michael@428 1237 return \@include;
michael@428 1238 }
michael@428 1239
michael@428 1240 # convert XML parser output to dependency records
michael@428 1241 sub depend_list ($) {
michael@428 1242 my ($dl) = @_;
michael@428 1243 foreach (@$dl) {
michael@428 1244 $_->{value} = parse_depends($_->{value});
michael@428 1245 }
michael@428 1246 return $dl;
michael@428 1247 }
michael@428 1248
michael@428 1249 # convert simple XML parser Bag into flat list
michael@428 1250 sub swith ($$) {
michael@428 1251 my ($bags,$name) = @_;
michael@428 1252 my ($cond);
michael@428 1253 my (@out);
michael@428 1254
michael@428 1255 foreach $cond (keys %$bags) {
michael@428 1256 foreach (@{$bags->{$cond}->{$name}}) {
michael@428 1257 push @out, {
michael@428 1258 cond => $cond,
michael@428 1259 value => $_
michael@428 1260 };
michael@428 1261 }
michael@428 1262 }
michael@428 1263 return \@out;
michael@428 1264 }
michael@428 1265
michael@428 1266 # convert (conditional) XML/RDF Bag into flat list
michael@428 1267 sub xwith ($) {
michael@428 1268 my ($bags) = @_;
michael@428 1269 my ($bag, $li, $el);
michael@428 1270 my (@out);
michael@428 1271
michael@428 1272 foreach $bag (@$bags) {
michael@428 1273 foreach $li (@{$bag->{'rdf:bag'}}) {
michael@428 1274 $el = $li->{'resource'} || $li->{'rdf:li'};
michael@428 1275 foreach (@$el) {
michael@428 1276 push @out, {
michael@428 1277 cond => $bag->{'cond'},
michael@428 1278 value => $_
michael@428 1279 };
michael@428 1280 }
michael@428 1281 }
michael@428 1282 }
michael@428 1283 return \@out;
michael@428 1284 }
michael@428 1285
michael@428 1286 # return node value from XML parser
michael@428 1287 sub xel($) {
michael@428 1288 my ($a) = @_;
michael@428 1289 my ($l) = $a->[0];
michael@428 1290 return '' if ref($l);
michael@428 1291 return $l;
michael@428 1292 }
michael@428 1293
michael@428 1294 # is the platform a good one?
michael@428 1295 sub goodpf ($$) {
michael@428 1296 my ($l, $p) = @_;
michael@428 1297 return 1 if $l eq '';
michael@428 1298 return ($l =~ m/(?:^|\s)\Q$p\E(?:\s|$)/);
michael@428 1299 }
michael@428 1300
michael@428 1301
michael@428 1302 #############################################################################
michael@428 1303 ##
michael@428 1304 ## FUNCTIONS: HELPER FUNCTIONS FOR XML PARSING & DEPENDENCY PROCESSING
michael@428 1305 ##
michael@428 1306 #############################################################################
michael@428 1307
michael@428 1308 # parse option from RPM output
michael@428 1309 # < "%option with_foo bar"
michael@428 1310 # > $with{"with_foo"} = "bar"
michael@428 1311 sub parse_options ($) {
michael@428 1312 my ($l) = @_;
michael@428 1313 $l = join("\n", @$l) if (ref($l));
michael@428 1314 return {} if ($l !~ m/(--define|\%option\s+)/s);
michael@428 1315 my $with = {};
michael@428 1316 $l =~ s/--define\s*'(\S+)\s+(\S+?)'/$with->{$1} = $2, ''/sge; # before openpkg-20021230
michael@428 1317 $l =~ s/\%option\s+(\S+)\s+(\S+)/$with->{$1} = $2, ''/sge; # after openpkg-20021230
michael@428 1318 return $with;
michael@428 1319 }
michael@428 1320
michael@428 1321 # fetch targets of a name that satisfy a condition and sort by target
michael@428 1322 # version. Input is a hash of versions(?) on which the condition has
michael@428 1323 # to be true and which points to an array of records with package
michael@428 1324 # version information. Output is the list of version sorted package
michael@428 1325 # version information records.
michael@428 1326 sub get_targets ($$) {
michael@428 1327 my ($relmap, $cond) = @_;
michael@428 1328 return (
michael@428 1329 sort {
michael@428 1330 vcmp(vs($a), vs($b));
michael@428 1331 } map {
michael@428 1332 @{$relmap->{$_}}
michael@428 1333 } grep {
michael@428 1334 $cond->($_);
michael@428 1335 } keys %$relmap
michael@428 1336 );
michael@428 1337 }
michael@428 1338
michael@428 1339 #############################################################################
michael@428 1340 ##
michael@428 1341 ## FUNCTIONS: DEPENDENCY PROCESSING
michael@428 1342 ##
michael@428 1343 #############################################################################
michael@428 1344
michael@428 1345 # search environment for packages that match a pattern
michael@428 1346 sub search_pattern ($$) {
michael@428 1347 my ($pattern, $env) = @_;
michael@428 1348 my (@todo);
michael@428 1349
michael@428 1350 if (defined($pattern)) {
michael@428 1351 # explicitly given package pattern
michael@428 1352 @todo = map {
michael@428 1353 my ($p) = $_;
michael@428 1354 my ($s, $iswildcard);
michael@428 1355 $s = $1 if ($p =~ s/(,[^\s,]+)$//);
michael@428 1356 if ($p =~ s/\*+$//) {
michael@428 1357 $p = '^'.quotemeta($p).'';
michael@428 1358 $iswildcard = 1;
michael@428 1359 } else {
michael@428 1360 $p = '^'.quotemeta($p).'$';
michael@428 1361 }
michael@428 1362 map { "$_$s" }
michael@428 1363 grep { m/$p/ && !($iswildcard && exists($env->{exclude}->{$_})) }
michael@428 1364 keys %{$env->{repository}}
michael@428 1365 } split(/\s+/, $pattern);
michael@428 1366 } else {
michael@428 1367 # undefined pattern means "-a" option that selects
michael@428 1368 # all packages from repository that are installed
michael@428 1369 # and not explicitly excluded on command line
michael@428 1370 @todo = grep {
michael@428 1371 my ($n) = $_;
michael@428 1372 (ref($env->{installed}->{$n}))
michael@428 1373 && !exists($env->{exclude}->{$n})
michael@428 1374 && grep { $_ ne '-' } keys %{$env->{installed}->{$n}}
michael@428 1375 } keys(%{$env->{repository}});
michael@428 1376 }
michael@428 1377 return \@todo;
michael@428 1378 }
michael@428 1379
michael@428 1380 # pull in OPTIONS for a package or an RPM file
michael@428 1381 my $get_with_cache = {};
michael@428 1382 sub get_with ($;$) {
michael@428 1383 my ($t, $fn) = @_;
michael@428 1384 my (@l, %with);
michael@428 1385 my ($optmap, $opt);
michael@428 1386
michael@428 1387 if ($t->{OPTIONS}) {
michael@428 1388 $opt = $t->{OPTIONS};
michael@428 1389 } else {
michael@428 1390 if (defined($fn)) {
michael@428 1391 @l = run($config->{"rpm"} . " -q --provides -p $fn");
michael@428 1392 } else {
michael@428 1393 if (not exists($get_with_cache->{-provides})) {
michael@428 1394 # pre-cache the "provides" query for all(!) packages at once for speedup
michael@428 1395 my @c = run($config->{"rpm"} . " -qa --qf " .
michael@428 1396 '\'%{NAME}[ :::%{PROVIDENAME}:::%{PROVIDEFLAGS:depflags}:::%{PROVIDEVERSION}:::]\n\'');
michael@428 1397 $get_with_cache->{-provides} = {};
michael@428 1398 foreach my $c (@c) {
michael@428 1399 if ($c =~ m/^(\S+)(.*)$/s) {
michael@428 1400 my ($name, $provides) = ($1, $2);
michael@428 1401 while ($provides =~ m/\s+:::(.+?):::\s*(.*?)\s*:::(.*?):::/g) {
michael@428 1402 $get_with_cache->{-provides}->{$name} = [] if (not exists($get_with_cache->{-provides}->{$name}));
michael@428 1403 push(@{$get_with_cache->{-provides}->{$name}}, "$1 $2 $3");
michael@428 1404 }
michael@428 1405 }
michael@428 1406 }
michael@428 1407 }
michael@428 1408 @l = $get_with_cache->{-provides}->{$t->{name}};
michael@428 1409 if (not @l) {
michael@428 1410 # (should not happen in practice, but anyway)
michael@428 1411 @l = run($config->{"rpm"} . " -q --provides $t->{name}");
michael@428 1412 $get_with_cache->{-provides}->{$t->{name}} = [ @l ];
michael@428 1413 }
michael@428 1414 }
michael@428 1415 $opt = parse_provideslist(\@l);
michael@428 1416 if (scalar(keys(%$opt)) == 0) {
michael@428 1417 if (defined($fn)) {
michael@428 1418 @l = run($config->{"rpm"} . " -qi -p $fn");
michael@428 1419 } else {
michael@428 1420 if (not exists($get_with_cache->{-infos})) {
michael@428 1421 # pre-cache the "infos" query for all(!) packages at once for speedup
michael@428 1422 my @c = run($config->{"rpm"} . " -qi -a");
michael@428 1423 my $p = "";
michael@428 1424 $get_with_cache->{-infos} = {};
michael@428 1425 foreach my $c (@c) {
michael@428 1426 $p = $1 if ($c =~ m/^Name:\s+(\S+)/s);
michael@428 1427 $get_with_cache->{-infos}->{$p} = [] if (not exists($get_with_cache->{-infos}->{$p}));
michael@428 1428 push(@{$get_with_cache->{-infos}->{$p}}, $c);
michael@428 1429 }
michael@428 1430 }
michael@428 1431 @l = $get_with_cache->{-infos}->{$t->{name}};
michael@428 1432 if (not @l) {
michael@428 1433 # (should not happen in practice, but anyway)
michael@428 1434 @l = run($config->{"rpm"} . " -qi $t->{name}");
michael@428 1435 $get_with_cache->{-infos}->{$t->{name}} = [ @l ];
michael@428 1436 }
michael@428 1437 }
michael@428 1438 $opt = parse_options(\@l);
michael@428 1439 }
michael@428 1440 $t->{OPTIONS} = $opt;
michael@428 1441 }
michael@428 1442 return $opt;
michael@428 1443 }
michael@428 1444
michael@428 1445 # copy options from new to old
michael@428 1446 # where option already exists in old or option key
michael@428 1447 # matches regular expression
michael@428 1448 sub override_options ($$$) {
michael@428 1449 my ($old, $new, $reg) = @_;
michael@428 1450 my ($k);
michael@428 1451
michael@428 1452 foreach $k (keys(%$new)) {
michael@428 1453 if ((exists($old->{$k}) && $old->{$k} ne $new->{$k}) || $k =~ m/^$reg$/) {
michael@428 1454 $old->{$k} = $new->{$k};
michael@428 1455 }
michael@428 1456 }
michael@428 1457 }
michael@428 1458
michael@428 1459 # filter package options
michael@428 1460 sub filter_name_with ($$$) {
michael@428 1461 my ($name, $with, $global) = @_;
michael@428 1462 my (@keys);
michael@428 1463
michael@428 1464 if ($global) {
michael@428 1465 push(@keys, grep { !/::/ } keys %$with);
michael@428 1466 }
michael@428 1467 push(@keys, grep { m/::/ } keys %$with);
michael@428 1468 return {
michael@428 1469 map {
michael@428 1470 my ($k) = $_;
michael@428 1471 $k !~ m/::/ || $k =~ s/^\Q$name\E:://
michael@428 1472 ? ( $k => $with->{$_} )
michael@428 1473 : ( )
michael@428 1474 } @keys
michael@428 1475 };
michael@428 1476 }
michael@428 1477
michael@428 1478 # filter out package relevant options
michael@428 1479 sub name_with ($$) {
michael@428 1480 filter_name_with($_[0], $_[1], 1);
michael@428 1481 }
michael@428 1482
michael@428 1483 # filter out package specific options
michael@428 1484 sub name_only_with ($$) {
michael@428 1485 filter_name_with($_[0], $_[1], 0);
michael@428 1486 }
michael@428 1487
michael@428 1488 # evaluate a condition attribute from an option set
michael@428 1489 sub conditional ($$) {
michael@428 1490 my ($cond, $with) = @_;
michael@428 1491 my (@s, $res);
michael@428 1492
michael@428 1493 return 1 if ($cond eq '' || !defined($with));
michael@428 1494 foreach (split(/\s+/,$cond)) {
michael@428 1495 if ($_ eq '+') {
michael@428 1496 die "openpkg:build:FATAL: stack underflow in: $cond\n" if scalar(@s) < 2;
michael@428 1497 my ($a) = pop(@s);
michael@428 1498 my ($b) = pop(@s);
michael@428 1499 push(@s, $a && $b);
michael@428 1500 } elsif ($_ eq '|') {
michael@428 1501 die "openpkg:build:FATAL: stack underflow in: $cond\n" if scalar(@s) < 2;
michael@428 1502 my ($a) = pop(@s);
michael@428 1503 my ($b) = pop(@s);
michael@428 1504 push(@s, $a || $b);
michael@428 1505 } elsif ($_ eq '!') {
michael@428 1506 die "openpkg:build:FATAL: stack underflow in: $cond\n" if scalar(@s) < 1;
michael@428 1507 my ($a) = pop(@s);
michael@428 1508 push(@s, !$a);
michael@428 1509 } else {
michael@428 1510 push(@s, ($with->{$_} eq 'yes') ? 1 : 0);
michael@428 1511 }
michael@428 1512 }
michael@428 1513 die "openpkg:build:FATAL: stack underflow in: $cond\n" if scalar(@s) < 1;
michael@428 1514 $res = pop(@s);
michael@428 1515
michael@428 1516 die "openpkg:build:FATAL: stack not empty in: $cond\n" if scalar(@s) > 0;
michael@428 1517 return $res;
michael@428 1518 }
michael@428 1519
michael@428 1520 # retrieve conditional target attributes in map
michael@428 1521 sub target_attribute ($$$;$) {
michael@428 1522 my ($target, $env, $attr, $with) = @_;
michael@428 1523 my ($optreg) = $env->{config}->{optreg};
michael@428 1524 my ($name, @out);
michael@428 1525
michael@428 1526 return if (not $target);
michael@428 1527 $name = $target->{name};
michael@428 1528
michael@428 1529 my ($mywith) = ($with ? $with : get_with($target));
michael@428 1530 override_options($mywith, name_with($name, $env->{with}), $optreg);
michael@428 1531
michael@428 1532 foreach (@{$target->{$attr}}) {
michael@428 1533 next if (not conditional($_->{'cond'}, $mywith));
michael@428 1534 push(@out, $_->{'value'});
michael@428 1535 }
michael@428 1536 return \@out;
michael@428 1537 }
michael@428 1538
michael@428 1539 # see whether target is in map
michael@428 1540 sub target_exists ($$) {
michael@428 1541 my ($target, $map) = @_;
michael@428 1542 my ($vmap) = $map->{$target->{name}};
michael@428 1543 return if (not $vmap);
michael@428 1544 return (
michael@428 1545 !defined $target->{version}
michael@428 1546 || defined $vmap->{vs($target)}
michael@428 1547 );
michael@428 1548 }
michael@428 1549
michael@428 1550 # see whether target has conflicts
michael@428 1551 sub target_conflicts ($$) {
michael@428 1552 my ($target, $env) = @_;
michael@428 1553 return target_attribute($target, $env, 'conflicts');
michael@428 1554 }
michael@428 1555
michael@428 1556 # retrieve build dependencies for target
michael@428 1557 sub target_depends ($$) {
michael@428 1558 my ($target, $env) = @_;
michael@428 1559 return target_attribute($target, $env, 'depends');
michael@428 1560 }
michael@428 1561
michael@428 1562 # retrieve runtime dependencies for target
michael@428 1563 sub target_keeps ($$) {
michael@428 1564 my ($target, $env) = @_;
michael@428 1565 return target_attribute($target, $env, 'keeps');
michael@428 1566 }
michael@428 1567
michael@428 1568 # retrieve source list for target
michael@428 1569 sub target_source ($$) {
michael@428 1570 my ($target, $env) = @_;
michael@428 1571 return target_attribute($target, $env, 'source');
michael@428 1572 }
michael@428 1573
michael@428 1574 # retrieve nosource list for target
michael@428 1575 sub target_nosource ($$) {
michael@428 1576 my ($target, $env) = @_;
michael@428 1577 return target_attribute($target, $env, 'nosource');
michael@428 1578 }
michael@428 1579
michael@428 1580 # check whether target conflicts against map
michael@428 1581 sub target_has_conflicts ($$$) {
michael@428 1582 my ($target, $map, $env) = @_;
michael@428 1583 my ($conflicts, $t);
michael@428 1584
michael@428 1585 $conflicts = target_conflicts($target, $env);
michael@428 1586 foreach (@$conflicts) {
michael@428 1587 my ($t) = find_target($_, $map, 0);
michael@428 1588 return $t if $t;
michael@428 1589 }
michael@428 1590 return;
michael@428 1591 }
michael@428 1592
michael@428 1593 # record target status
michael@428 1594 sub target_setstatus ($$$) {
michael@428 1595 my ($target, $status, $pri) = @_;
michael@428 1596
michael@428 1597 if ($pri > $target->{STATUSPRI}) {
michael@428 1598 $target->{STATUSPRI} = $pri;
michael@428 1599 $target->{STATUS} = $status;
michael@428 1600 }
michael@428 1601 }
michael@428 1602
michael@428 1603 # strip doubles from depend/keep lists
michael@428 1604 # and return a map name => depend/keep
michael@428 1605 sub unique_map {
michael@428 1606 my (%out);
michael@428 1607 foreach (@_) {
michael@428 1608 foreach (@$_) {
michael@428 1609 $out{$_->{name}} = $_;
michael@428 1610 }
michael@428 1611 }
michael@428 1612 return %out;
michael@428 1613 }
michael@428 1614
michael@428 1615 # check whether installed package matches build options. If default
michael@428 1616 # = 1 then options which are not required must be identical to the
michael@428 1617 # DEFOPTS.
michael@428 1618 sub target_suitable ($$$) {
michael@428 1619 my ($target, $with, $default) = @_;
michael@428 1620 my ($iwith, $dwith);
michael@428 1621 my ($k, $v);
michael@428 1622
michael@428 1623 if ($target->{GOAL}) {
michael@428 1624 $with = name_with($target->{name}, $with);
michael@428 1625 } else {
michael@428 1626 $with = name_only_with($target->{name}, $with);
michael@428 1627 }
michael@428 1628 $iwith = $target->{OPTIONS};
michael@428 1629 $dwith = $target->{DEFOPTS};
michael@428 1630 while (($k,$v) = each(%$iwith)) {
michael@428 1631 if (exists($with->{$k})) {
michael@428 1632 return 0 if ($iwith->{$k} ne $with->{$k});
michael@428 1633 } elsif ($default) {
michael@428 1634 return 0 if ($iwith->{$k} ne $dwith->{$k});
michael@428 1635 }
michael@428 1636 }
michael@428 1637 return 1;
michael@428 1638 }
michael@428 1639
michael@428 1640 # determine whether target should be rebuild
michael@428 1641 sub target_better ($$$) {
michael@428 1642 my ($env, $target, $map) = @_;
michael@428 1643 my ($vs) = vs($target);
michael@428 1644 my ($vmap) = $map->{$target->{name}};
michael@428 1645
michael@428 1646 # rebuild if target isn't installed
michael@428 1647 return 'new' unless $vmap;
michael@428 1648
michael@428 1649 # if "-e" then
michael@428 1650 # always update if installed version is different from repository
michael@428 1651 if ($env->{exact} && !grep { vcmp($vs, $_) == 0; } keys(%$vmap)) {
michael@428 1652 return 'exact';
michael@428 1653 }
michael@428 1654
michael@428 1655 # if target is goal
michael@428 1656 # always update if installed version is older than repository
michael@428 1657 if ($target->{GOAL} && !grep { vcmp($vs, $_) <= 0; } keys(%$vmap)) {
michael@428 1658 return 'goal';
michael@428 1659 }
michael@428 1660
michael@428 1661 # if -U then
michael@428 1662 # always update if installed version is older than repository
michael@428 1663 if ($env->{upgrade} && !grep { vcmp($vs, $_) <= 0; } keys(%$vmap)) {
michael@428 1664 return 'upgrade';
michael@428 1665 }
michael@428 1666
michael@428 1667 # if -z/-Z then
michael@428 1668 # always update if installed version is equal or older than repository
michael@428 1669 if ($env->{zero} && grep { vcmp($vs, $_) >= 0; } keys(%$vmap)) {
michael@428 1670 return 'zero';
michael@428 1671 }
michael@428 1672
michael@428 1673 # keep installed target
michael@428 1674 return;
michael@428 1675 }
michael@428 1676
michael@428 1677 # check if target record describes a source package
michael@428 1678 sub is_source ($) {
michael@428 1679 my ($t) = @_;
michael@428 1680 return !(defined $t->{'prefix'});
michael@428 1681 }
michael@428 1682
michael@428 1683 # there can be multiple sources for a target release
michael@428 1684 sub chose_source ($$$$$) {
michael@428 1685 my ($env, $name, $select, $vmap, $cond) = @_;
michael@428 1686 my (@targ, @recs, @nrecs, $rec, %nam);
michael@428 1687
michael@428 1688 # resolve name into a list of versions
michael@428 1689 # for virtual targets this resolves to a list
michael@428 1690 # of real targets that provide the virtual target
michael@428 1691 @targ = get_targets($vmap, sub { 1; });
michael@428 1692 return unless @targ;
michael@428 1693
michael@428 1694 # find usable binary targets add all source targets
michael@428 1695 @recs = (
michael@428 1696 ( grep {
michael@428 1697 !$env->{sourceonly}
michael@428 1698 && !is_source($_)
michael@428 1699 && $_->{'platform'} eq $env->{config}->{platform}
michael@428 1700 && $_->{'prefix'} eq $env->{config}->{prefix}
michael@428 1701 } get_targets($vmap, $cond) ),
michael@428 1702 ( grep {
michael@428 1703 is_source($_)
michael@428 1704 } @targ )
michael@428 1705 );
michael@428 1706 return if (not @recs);
michael@428 1707
michael@428 1708 # limit list to exact matches if provided by "-e"
michael@428 1709 if (defined($select)) {
michael@428 1710 @recs = grep {
michael@428 1711 vsn($_) =~ m/^\Q$select\E/
michael@428 1712 } @recs;
michael@428 1713 }
michael@428 1714
michael@428 1715 # try to resolve ambiguity against installed targets
michael@428 1716 # and targets previously selected
michael@428 1717 if (scalar(@recs) > 1) {
michael@428 1718 @nrecs = grep {
michael@428 1719 $env->{built}->{$_->{name}}
michael@428 1720 || $env->{installed}->{$_->{name}}
michael@428 1721 } @recs;
michael@428 1722 @recs = @nrecs if (@nrecs);
michael@428 1723 }
michael@428 1724
michael@428 1725 # try to resolve ambiguity against hints
michael@428 1726 if ($env->{hint}) {
michael@428 1727 @nrecs = grep {
michael@428 1728 exists($env->{hint}->{$_->{name}})
michael@428 1729 } @recs;
michael@428 1730 @recs = @nrecs if (@nrecs);
michael@428 1731 }
michael@428 1732
michael@428 1733 # try to resolve ambiguity against targets that match
michael@428 1734 # the exact name
michael@428 1735 if (scalar(@recs) > 1) {
michael@428 1736 @nrecs = grep {
michael@428 1737 $name eq $_->{name}
michael@428 1738 } @recs;
michael@428 1739 @recs = @nrecs if (@nrecs);
michael@428 1740 }
michael@428 1741
michael@428 1742 # try to resolve ambiguity by preferring binaries
michael@428 1743 if (scalar(@recs) > 1 && !$env->{sourceonly}) {
michael@428 1744 @nrecs = grep {
michael@428 1745 defined($_->{'platform'})
michael@428 1746 } @recs;
michael@428 1747 @recs = @nrecs if (@nrecs);
michael@428 1748 }
michael@428 1749
michael@428 1750 # if we still have non-unique targets, complain
michael@428 1751 if (scalar(@recs) > 1) {
michael@428 1752 %nam = map { $_->{name} => 1 } @recs;
michael@428 1753 if (scalar(keys(%nam)) > 1) {
michael@428 1754 print "# ambigous sources for $name\n";
michael@428 1755 my ($i) = 0;
michael@428 1756 foreach (@recs) {
michael@428 1757 print "# $i: ".vsn($_)." = $_->{href}\n";
michael@428 1758 $i++;
michael@428 1759 }
michael@428 1760 return;
michael@428 1761 }
michael@428 1762 }
michael@428 1763
michael@428 1764 # prefer full-source packages
michael@428 1765 if (scalar(@recs) > 1) {
michael@428 1766 @nrecs = grep {
michael@428 1767 ! $_->{nosource}
michael@428 1768 || ! @{$_->{nosource}}
michael@428 1769 } @recs;
michael@428 1770 unless (@nrecs) {
michael@428 1771 @nrecs = grep {
michael@428 1772 $_->{href} !~ m/\.nosrc.rpm$/
michael@428 1773 } @recs;
michael@428 1774 }
michael@428 1775 @recs = @nrecs if (@nrecs);
michael@428 1776 }
michael@428 1777
michael@428 1778 # nothing left -> exit
michael@428 1779 return if (scalar(@recs) == 0);
michael@428 1780
michael@428 1781 # chose last (= max version) in list of targets
michael@428 1782 $rec = $recs[-1];
michael@428 1783 print "# source for $name is ".vsn($rec)."\n";
michael@428 1784 return $rec;
michael@428 1785 }
michael@428 1786
michael@428 1787 # locate target for a dependency
michael@428 1788 sub dep2target ($$$) {
michael@428 1789 my ($dep, $env, $source) = @_;
michael@428 1790 my ($name, $op, @targ);
michael@428 1791 my ($i, $r, $b, $cond, $version);
michael@428 1792 my ($t, $tdef, $why);
michael@428 1793
michael@428 1794 ($name, $op, $version) = ($dep->{name}, $dep->{op}, $dep->{val});
michael@428 1795
michael@428 1796 $i = $env->{installed}->{$name};
michael@428 1797 $r = $env->{repository}->{$name};
michael@428 1798 $b = $env->{built}->{$name};
michael@428 1799
michael@428 1800 return if (not ($i || $r || $b));
michael@428 1801
michael@428 1802 if (!defined($op)) {
michael@428 1803 $cond = sub { 1; };
michael@428 1804 } elsif ($op eq 'geq') {
michael@428 1805 $cond = sub { vcmp($_[0],$version) >= 0; };
michael@428 1806 } elsif ($op eq 'leq') {
michael@428 1807 $cond = sub { vcmp($_[0],$version) <= 0; };
michael@428 1808 } elsif ($op eq 'gt') {
michael@428 1809 $cond = sub { vcmp($_[0],$version) > 0; };
michael@428 1810 } elsif ($op eq 'lt') {
michael@428 1811 $cond = sub { vcmp($_[0],$version) < 0; };
michael@428 1812 } elsif ($op eq 'equ') {
michael@428 1813 $cond = sub { vcmp($_[0],$version) == 0; };
michael@428 1814 } else {
michael@428 1815 die "openpkg:build:FATAL: internal error in dep2target\n";
michael@428 1816 }
michael@428 1817
michael@428 1818 $tdef = undef;
michael@428 1819
michael@428 1820 # search installed target that matches requirement
michael@428 1821 # use it if we are not upgrading (no -U and no -z/-Z)
michael@428 1822 if ($i && (@targ = get_targets($i, $cond))) {
michael@428 1823 foreach $t (@targ) {
michael@428 1824 get_with($t);
michael@428 1825 if (target_suitable($t, $env->{with}, 0)) {
michael@428 1826 $tdef = $t;
michael@428 1827 if (not ($env->{upgrade} || $env->{zero} || $source)) {
michael@428 1828 return ($t, 1);
michael@428 1829 }
michael@428 1830 }
michael@428 1831 }
michael@428 1832 }
michael@428 1833
michael@428 1834 # search target in current build list that matches requirement
michael@428 1835 # use it if it exists
michael@428 1836 if ($b && (@targ = get_targets($b, $cond))) {
michael@428 1837 $t = $targ[0];
michael@428 1838 return ($t, 1);
michael@428 1839 }
michael@428 1840
michael@428 1841 # search target in repository and install it, if it is newer than
michael@428 1842 # corresponding installed versions avoid repository packages that
michael@428 1843 # would install 'new' (i.e. are not an upgrade of an existing
michael@428 1844 # package)
michael@428 1845 $t = chose_source($env, $name, undef, $r, $cond);
michael@428 1846 if ($t) {
michael@428 1847 if (!$tdef || (
michael@428 1848 ($why = target_better($env, $t, $env->{installed})) &&
michael@428 1849 $why ne 'new'
michael@428 1850 ) || $source) {
michael@428 1851 return ($t, 0);
michael@428 1852 }
michael@428 1853 }
michael@428 1854
michael@428 1855 # if nothing is suitable in repository then fall back to
michael@428 1856 # anything we already have installed but that we skipped
michael@428 1857 # above to look for upgrades.
michael@428 1858 if ($tdef) {
michael@428 1859 return ($tdef, 1);
michael@428 1860 }
michael@428 1861
michael@428 1862 return;
michael@428 1863 }
michael@428 1864
michael@428 1865 # compute reverse dependency map
michael@428 1866 sub get_revdep ($) {
michael@428 1867 my ($env) = @_;
michael@428 1868 my ($i) = $env->{'installed'};
michael@428 1869 my ($r) = $env->{'repository'};
michael@428 1870 my ($pkg, %dep, %dlist, %rev);
michael@428 1871 my (@targ, $t, $t1, $t2, $with, $name, $vmap);
michael@428 1872 my ($d, $k, %d, $old, %name, %pkg);
michael@428 1873
michael@428 1874 print "# computing reverse dependencies\n";
michael@428 1875
michael@428 1876 # iterate over all installed packages
michael@428 1877 foreach $pkg (keys(%$i)) {
michael@428 1878 $vmap = $r->{$pkg};
michael@428 1879 if (not $vmap) {
michael@428 1880 print "# ATTENTION: $pkg has no upgrade path (installed package not found in repository)\n";
michael@428 1881 next;
michael@428 1882 }
michael@428 1883
michael@428 1884 # get forward dependencies from installed packages
michael@428 1885 # dep{a}{b} is true if b depends directly on a
michael@428 1886 # dlist{a} is list of packages that depend on a
michael@428 1887 @targ = get_targets($i->{$pkg}, sub { 1; });
michael@428 1888 foreach $t (@targ) {
michael@428 1889 $with = get_with($t);
michael@428 1890 $d = target_attribute($t, $env, 'depends', $with);
michael@428 1891 $k = target_attribute($t, $env, 'keeps', $with);
michael@428 1892 next if (not (@$d || @$k));
michael@428 1893 %d = unique_map($d,$k);
michael@428 1894
michael@428 1895 # resolve package
michael@428 1896 if (not exists($pkg{$pkg})) {
michael@428 1897 ($t2, $old) = dep2target({ name => $pkg }, $env, 1);
michael@428 1898 $t2 = undef if ($old);
michael@428 1899 $pkg{$pkg} = $t2;
michael@428 1900 }
michael@428 1901 $t2 = $pkg{$pkg};
michael@428 1902 next if (not $t2);
michael@428 1903 foreach (keys(%d)) {
michael@428 1904 next if ($_ eq 'OpenPKG');
michael@428 1905 # resolve target
michael@428 1906 unless (exists($name{$_})) {
michael@428 1907 ($t1, $old) = dep2target($d{$_}, $env, 0);
michael@428 1908 $name{$_} = ($t1 ? $t1->{name} : $_);
michael@428 1909 }
michael@428 1910 $name = $name{$_};
michael@428 1911 if (not $dep{$name}{$t->{name}}) {
michael@428 1912 $dep{$name}{$t->{name}} = 1;
michael@428 1913 push(@{$dlist{$name}}, $t2);
michael@428 1914 }
michael@428 1915 }
michael@428 1916 }
michael@428 1917 }
michael@428 1918
michael@428 1919 # sort reverse dependencies
michael@428 1920 foreach $pkg (keys(%dep)) {
michael@428 1921 $rev{$pkg} = [
michael@428 1922 sort {
michael@428 1923 $dep{$b->{name}}{$a->{name}}
michael@428 1924 || -$dep{$a->{name}}{$b->{name}}
michael@428 1925 || $a->{name} cmp $b->{name}
michael@428 1926 } @{$dlist{$pkg}}
michael@428 1927 ];
michael@428 1928 }
michael@428 1929
michael@428 1930 # return hash of package reverse dependencies
michael@428 1931 return \%rev;
michael@428 1932 }
michael@428 1933
michael@428 1934 # build package dependencies
michael@428 1935 # (all input and output is passed in 'env' hash)
michael@428 1936 sub build_deps ($$) {
michael@428 1937 my ($pattern, $env) = @_;
michael@428 1938 my ($todo, @list, $list, @out);
michael@428 1939
michael@428 1940 # determine all packages which match the pattern
michael@428 1941 $todo = search_pattern($pattern, $env);
michael@428 1942
michael@428 1943 # unfold target names into all(!) real targets names
michael@428 1944 @list =
michael@428 1945 map {
michael@428 1946 map {
michael@428 1947 map {
michael@428 1948 $_->{name}
michael@428 1949 } @$_
michael@428 1950 } values(%{$env->{repository}->{$_}})
michael@428 1951 } @$todo;
michael@428 1952
michael@428 1953 # also add target name
michael@428 1954 push @list, @$todo;
michael@428 1955
michael@428 1956 # strip duplicate names
michael@428 1957 @list = keys %{ { map { $_ => 1 } @list } };
michael@428 1958
michael@428 1959 # cache reverse dependencies
michael@428 1960 if (not $env->{revdep}) {
michael@428 1961 $env->{revdep} = get_revdep($env);
michael@428 1962 }
michael@428 1963
michael@428 1964 # map targets into list of dependency names
michael@428 1965 @list = map {
michael@428 1966 $env->{revdep}->{$_}
michael@428 1967 ? (@{$env->{revdep}->{$_}})
michael@428 1968 : ()
michael@428 1969 } @list;
michael@428 1970
michael@428 1971 # iterate over dependencies
michael@428 1972 foreach (@list) {
michael@428 1973 # avoiding cycles
michael@428 1974 next if ($env->{builddeps}->{$_->{name}});
michael@428 1975 $env->{builddeps}->{$_->{name}} = 1;
michael@428 1976
michael@428 1977 # generate result
michael@428 1978 push(@out, $_);
michael@428 1979
michael@428 1980 # generate result recursively
michael@428 1981 $list = build_deps($_->{name}, $env); # RECURSION
michael@428 1982 push(@out, @$list);
michael@428 1983 }
michael@428 1984
michael@428 1985 # return final results
michael@428 1986 return \@out;
michael@428 1987 }
michael@428 1988
michael@428 1989 # report options that are not used for
michael@428 1990 sub warn_about_options ($$$) {
michael@428 1991 my ($target, $with, $c) = @_;
michael@428 1992 my ($iwith) = $target->{OPTIONS};
michael@428 1993 my ($k, $v);
michael@428 1994
michael@428 1995 return if (not defined($iwith));
michael@428 1996 $with = name_with($target->{name}, $with);
michael@428 1997 while (($k,$v) = each %$with) {
michael@428 1998 if (not ($k =~ m/^$c->{optreg}$/ || exists $iwith->{$k})) {
michael@428 1999 print "# ATTENTION: $target->{name} ignores option '$k'\n";
michael@428 2000 }
michael@428 2001 }
michael@428 2002 }
michael@428 2003
michael@428 2004 # find target in map
michael@428 2005 sub find_target ($$$) {
michael@428 2006 my ($name, $map, $pos) = @_;
michael@428 2007 my ($vmap) = $map->{$name};
michael@428 2008 my (@vs);
michael@428 2009
michael@428 2010 return if (not $vmap);
michael@428 2011 @vs = sort { vcmp($b,$a) } keys(%$vmap);
michael@428 2012 return $vmap->{$vs[$pos]}->[-1];
michael@428 2013 }
michael@428 2014
michael@428 2015 # add dependency as build option
michael@428 2016 sub depend_option ($$$) {
michael@428 2017 my ($target, $dep, $env) = @_;
michael@428 2018 my ($with, $opt, $relmap, @t, $t);
michael@428 2019 my ($pro) = depends2provides($dep);
michael@428 2020 my ($conflict) = 0;
michael@428 2021
michael@428 2022 return 1 if (not defined($pro->{with}));
michael@428 2023
michael@428 2024 my ($val) = defined($pro->{version}) ? $pro->{version} : 'yes';
michael@428 2025
michael@428 2026 $with = $env->{with};
michael@428 2027 $opt = $pro->{prefix}.'::'.$pro->{with};
michael@428 2028 if (defined($with->{$opt}) && $with->{$opt} ne $val) {
michael@428 2029 print "# ", vsn($target), " has conflicting requirement $opt = $with->{$opt} != $val\n";
michael@428 2030 $conflict = 1;
michael@428 2031 }
michael@428 2032
michael@428 2033 $relmap = $env->{built}->{$pro->{prefix}};
michael@428 2034 @t = get_targets($relmap, sub { 1; });
michael@428 2035 foreach $t (@t) {
michael@428 2036 $with = $t->{OPTIONS};
michael@428 2037 $opt = $pro->{with};
michael@428 2038 if (defined($with->{$opt}) && $with->{$opt} ne $val) {
michael@428 2039 print "# ", vsn($t), " has conflicting requirement $opt = $with->{$opt} != $val\n";
michael@428 2040 $conflict = 1;
michael@428 2041 }
michael@428 2042 }
michael@428 2043
michael@428 2044 return 0 if ($conflict);
michael@428 2045
michael@428 2046 print "# ", vsn($target), " adds option $opt = $val\n";
michael@428 2047 $with->{$opt} = $val;
michael@428 2048 return 1;
michael@428 2049 }
michael@428 2050
michael@428 2051 # remember fatal error
michael@428 2052 sub pusherr ($$$) {
michael@428 2053 my ($env, $target, $mess) = @_;
michael@428 2054 print "# $mess\n";
michael@428 2055 push(@{$env->{fatal}}, vsn($target).": $mess\n");
michael@428 2056 }
michael@428 2057
michael@428 2058 # generate dependencies
michael@428 2059 sub make_dep ($$$$$$$) {
michael@428 2060 my ($who, $target, $depth, $env, $list, $blist, $clist) = @_;
michael@428 2061 my ($d, $k, %d, %k, $t, $old);
michael@428 2062 my (@deps, $conflict, $why);
michael@428 2063
michael@428 2064 # check whether target is already in list of to be build packages
michael@428 2065 if (target_exists($target, $env->{built})) {
michael@428 2066 print "# $target->{name} is already in list\n";
michael@428 2067 return;
michael@428 2068 }
michael@428 2069
michael@428 2070 # check whether target is in conflict with already installed package
michael@428 2071 if ($t = target_has_conflicts($target, $env->{installed}, $env)) {
michael@428 2072 target_setstatus($target, 'CONFLICT', 4);
michael@428 2073 push(@$clist, $target);
michael@428 2074 pusherr($env, $target, "$target->{name} conflicts with ".vsn($t));
michael@428 2075 return;
michael@428 2076 }
michael@428 2077
michael@428 2078 # check whether target is in conflict with to be build package
michael@428 2079 if ($t = target_has_conflicts($target, $env->{built}, $env)) {
michael@428 2080 target_setstatus($target, 'CONFLICT', 4);
michael@428 2081 push(@$clist, $target);
michael@428 2082 pusherr($env, $target, "$target->{name} conflicts with ".vsn($t));
michael@428 2083 return;
michael@428 2084 }
michael@428 2085
michael@428 2086 # see if a target is already installed and requires a rebuild
michael@428 2087 if ($t = find_target($target->{name}, $env->{installed}, -1)) {
michael@428 2088 if (exists($env->{exclude}->{$target->{name}})) {
michael@428 2089 print "# excluding $target->{name} (no upgrade allowed)\n";
michael@428 2090 return;
michael@428 2091 }
michael@428 2092
michael@428 2093 # pull in options (for status report)
michael@428 2094 if ($target->{REBUILD}) {
michael@428 2095 target_setstatus($target, 'DEPEND', 1);
michael@428 2096 print "# rebuilding $target->{name} (dependency)\n";
michael@428 2097 } elsif ($env->{zero}) {
michael@428 2098 target_setstatus($target, 'ZERO', 1);
michael@428 2099 print "# rebuilding $target->{name} (zero)\n";
michael@428 2100 } elsif (vs($target) ne vs($t)) {
michael@428 2101 target_setstatus($target, 'UPDATE', 3);
michael@428 2102 print "# rebuilding $target->{name} (update)\n";
michael@428 2103 } elsif (!target_suitable($t, $env->{with}, 0)) {
michael@428 2104 target_setstatus($target, 'MISMATCH', 2);
michael@428 2105 print "# rebuilding $target->{name} (parameter mismatch)\n";
michael@428 2106 } elsif ($env->{goals} && $target->{GOAL}) {
michael@428 2107 target_setstatus($target, 'GOAL', 3);
michael@428 2108 print "# rebuilding $target->{name} (goal)\n";
michael@428 2109 } else {
michael@428 2110 print "# $target->{name} is already installed\n";
michael@428 2111 return;
michael@428 2112 }
michael@428 2113
michael@428 2114 # use options from installed base
michael@428 2115 override_options(get_with($target), get_with($t), $env->{config}->{optreg});
michael@428 2116
michael@428 2117 # remember this is a rebuild for a proxy package
michael@428 2118 $target->{PROXY} = $t->{PROXY};
michael@428 2119 $target->{REBUILD} = 1;
michael@428 2120 } else {
michael@428 2121 print "# creating $target->{name}\n";
michael@428 2122 target_setstatus($target, 'ADD', 3);
michael@428 2123 }
michael@428 2124
michael@428 2125 if (exists($env->{exclude}->{$target->{name}})) {
michael@428 2126 die "openpkg:build:FATAL: target ".vsn($target)." is forbidden\n";
michael@428 2127 }
michael@428 2128
michael@428 2129 # mark this as a target before reverse dependencies trigger it again
michael@428 2130 push(@{$env->{built}->{$target->{name}}->{vs($target)}}, $target);
michael@428 2131 $target->{LIMBO} = 1;
michael@428 2132
michael@428 2133 # recurse over dependencies
michael@428 2134 $d = target_depends($target, $env);
michael@428 2135 $k = target_keeps($target, $env);
michael@428 2136 if (@$d || @$k) {
michael@428 2137 %d = unique_map($d, $k);
michael@428 2138 %k = unique_map($k);
michael@428 2139 @deps = ();
michael@428 2140 $conflict = 0;
michael@428 2141 foreach (keys %d) {
michael@428 2142 # old index misses a OpenPKG provider in the index... skip it
michael@428 2143 next if ($_ eq 'OpenPKG');
michael@428 2144 ($t, $old) = dep2target($d{$_}, $env, 0);
michael@428 2145 if ($t) {
michael@428 2146 if ($old) {
michael@428 2147 print "# $target->{name} uses ".vsn($t)." for $_\n";
michael@428 2148 if ($t->{LIMBO}) {
michael@428 2149 print "# ATTENTION: ".vsn($t)." is in LIMBO\n";
michael@428 2150 }
michael@428 2151 next;
michael@428 2152 }
michael@428 2153 if (not depend_option($t, $d{$_}, $env)) {
michael@428 2154 push(@$clist, $target);
michael@428 2155 pusherr($env, $target, "$target->{name} has conflicting requirement");
michael@428 2156 target_setstatus($target, 'UNDEF', 4);
michael@428 2157 $conflict = 1;
michael@428 2158 next;
michael@428 2159 }
michael@428 2160 if ($k{$_}) {
michael@428 2161 push(@$blist, $t);
michael@428 2162 print "# $target->{name} installs ".vsn($t)." for $_\n";
michael@428 2163 } else {
michael@428 2164 print "# $target->{name} requires ".vsn($t)." for $_\n";
michael@428 2165 }
michael@428 2166 push(@deps, $t);
michael@428 2167 } else {
michael@428 2168 push(@$clist, $target);
michael@428 2169 pusherr($env, $target, "$target->{name} searches a frood called '$_'");
michael@428 2170 target_setstatus($target, 'UNDEF', 4);
michael@428 2171 $conflict = 1;
michael@428 2172 }
michael@428 2173 }
michael@428 2174 if (not $conflict) {
michael@428 2175 foreach $t (@deps) {
michael@428 2176 make_dep($target, $t, $depth+1, $env, $list, $blist, $clist); # RECURSION
michael@428 2177 }
michael@428 2178 }
michael@428 2179 }
michael@428 2180
michael@428 2181 print "# adding ".vsn($target)." to list\n";
michael@428 2182 $target->{WHO} = $who;
michael@428 2183 $target->{WHY} = $target->{STATUS};
michael@428 2184 push(@$list, $target);
michael@428 2185
michael@428 2186 # remember new options
michael@428 2187 override_options(get_with($target), name_with($target->{name}, $env->{with}), '');
michael@428 2188
michael@428 2189 # moan about non-source packages
michael@428 2190 foreach (@{target_nosource($target, $env)}) {
michael@428 2191 my ($p) = target_source($target, $env)->[$_];
michael@428 2192 $p =~ s/.*\///;
michael@428 2193 print "# ATTENTION: unpackaged source $_: $p\n";
michael@428 2194 }
michael@428 2195
michael@428 2196 # cleanup limbo
michael@428 2197 $target->{LIMBO} = 0;
michael@428 2198
michael@428 2199 # a dependency could not be resolved, don't bother with reverse
michael@428 2200 # dependencies for this target
michael@428 2201 return if ($conflict);
michael@428 2202
michael@428 2203 if (!$env->{quick} && $target->{name} ne 'openpkg' ) {
michael@428 2204 if (not $env->{revdep}) {
michael@428 2205 $env->{revdep} = get_revdep($env);
michael@428 2206 }
michael@428 2207 foreach $t (@{$env->{revdep}->{$target->{name}}}) {
michael@428 2208 # this is a rebuild, triggering further revdeps
michael@428 2209 $t->{REBUILD} = 1;
michael@428 2210
michael@428 2211 # this is a rebuild, keep this installed
michael@428 2212 push(@$blist, $t);
michael@428 2213
michael@428 2214 print "# rebuilding reverse dependency ".vsn($t)."\n";
michael@428 2215 make_dep($target, $t, $depth+1, $env, $list, $blist, $clist); # RECURSION
michael@428 2216 }
michael@428 2217 }
michael@428 2218 }
michael@428 2219
michael@428 2220 # generate build lists for targets matched by pattern
michael@428 2221 # (all input and output is passed in 'env' hash)
michael@428 2222 sub build_list ($$) {
michael@428 2223 my ($pattern, $env) = @_;
michael@428 2224 my (@goals, @targets, @keeps, @conflicts, @bonly, $t);
michael@428 2225 my ($name, $select, $r, $i);
michael@428 2226 my ($todo, %keep);
michael@428 2227
michael@428 2228 # determine all packages which match the pattern
michael@428 2229 $todo = search_pattern($pattern, $env);
michael@428 2230
michael@428 2231 # chose sources for goals from repository
michael@428 2232 foreach $name (@$todo) {
michael@428 2233 $select = undef;
michael@428 2234 $select = $1 if ($name =~ s/,([^\s,]+)$//);
michael@428 2235 $t = undef;
michael@428 2236
michael@428 2237 # keeping installed packages for goals is ugly
michael@428 2238 # - we currently do not support installed source RPMs
michael@428 2239 # - source RPMs might already have expired from repository
michael@428 2240 # consequence:
michael@428 2241 # - goals are always upgraded to repository versions
michael@428 2242 #if (not $env->{upgrade}) {
michael@428 2243 # $i = $env->{installed}->{$name};
michael@428 2244 # $t = chose_source($env, $name, $select, $i, sub { 1; });
michael@428 2245 #}
michael@428 2246 if (not $t) {
michael@428 2247 $r = $env->{repository}->{$name};
michael@428 2248 $t = chose_source($env, $name, $select, $r, sub { 1; });
michael@428 2249 }
michael@428 2250
michael@428 2251 if ($t) {
michael@428 2252 warn_about_options($t, $env->{with}, $env->{config});
michael@428 2253 $t->{GOAL} = 1;
michael@428 2254 push @goals, $t;
michael@428 2255 } else {
michael@428 2256 # error
michael@428 2257 if ($env->{status}) {
michael@428 2258 print "# dropping goal '$name'\n";
michael@428 2259 } else {
michael@428 2260 die "openpkg:build:FATAL: cannot find source for '$name'\n";
michael@428 2261 }
michael@428 2262 }
michael@428 2263 }
michael@428 2264 return if (not @goals);
michael@428 2265
michael@428 2266 # recurse over dependencies
michael@428 2267 @targets = ();
michael@428 2268 @keeps = @goals;
michael@428 2269 foreach $t (@goals) {
michael@428 2270 print "# recursing over dependencies for ".vsn($t)."\n";
michael@428 2271 make_dep(undef, $t, 0, $env, \@targets, \@keeps, \@conflicts);
michael@428 2272 }
michael@428 2273
michael@428 2274 # determine "binary only" packages which should be not kept
michael@428 2275 # as they were not installed and are used temporarily only.
michael@428 2276 %keep = map { $_ => 1 } @keeps;
michael@428 2277 @bonly = reverse grep {
michael@428 2278 !$keep{$_} && !$env->{installed}->{$_->{name}}
michael@428 2279 } @targets;
michael@428 2280
michael@428 2281 # return results
michael@428 2282 return (\@targets, \@bonly, \@conflicts);
michael@428 2283 }
michael@428 2284
michael@428 2285
michael@428 2286 #############################################################################
michael@428 2287 ##
michael@428 2288 ## FUNCTIONS: RESULT PRINTING
michael@428 2289 ##
michael@428 2290 #############################################################################
michael@428 2291
michael@428 2292 # determine execution command
michael@428 2293 sub cmd ($$) {
michael@428 2294 my ($w,$s) = @_;
michael@428 2295 if (!defined($w)) {
michael@428 2296 return $s;
michael@428 2297 } elsif ($w =~ m/^-(.*)/) {
michael@428 2298 return "$1 \"$s\"";
michael@428 2299 } else {
michael@428 2300 return "$w $s";
michael@428 2301 }
michael@428 2302 }
michael@428 2303 sub priv ($) { cmd($opt_P, $_[0]); }
michael@428 2304 sub npriv ($) { cmd($opt_N, $_[0]); }
michael@428 2305
michael@428 2306 # execute a command
michael@428 2307 my $run_cache = {};
michael@428 2308 sub run ($) {
michael@428 2309 my $cmd = cmd($opt_N, $_[0]);
michael@428 2310 my $out = $run_cache->{$cmd};
michael@428 2311 if (not defined($out)) {
michael@428 2312 my @out = `$cmd`;
michael@428 2313 $out = [ @out ];
michael@428 2314 $run_cache->{$cmd} = $out;
michael@428 2315 }
michael@428 2316 return (wantarray ? @{$out} : join(//, @{$out}));
michael@428 2317 }
michael@428 2318
michael@428 2319 # print dependency list
michael@428 2320 sub print_deps ($) {
michael@428 2321 my ($list) = @_;
michael@428 2322
michael@428 2323 print join("\n", sort map { vsn($_) } @$list), "\n";
michael@428 2324 }
michael@428 2325
michael@428 2326 # print dependency map
michael@428 2327 sub print_map ($$$$$) {
michael@428 2328 my ($installed, $repository, $list, $bonly, $clist) = @_;
michael@428 2329 my (%dep);
michael@428 2330
michael@428 2331 foreach (@$bonly) {
michael@428 2332 $_->{status} = 'TEMP';
michael@428 2333 }
michael@428 2334 foreach (reverse(@$list)) {
michael@428 2335 printf("%-35s %-8s %s\n",
michael@428 2336 $_->{WHO} ? vsn($_->{WHO}) : "GOAL",
michael@428 2337 $_->{WHY} ? $_->{WHY} : '???',
michael@428 2338 vsn($_)
michael@428 2339 );
michael@428 2340 }
michael@428 2341 }
michael@428 2342
michael@428 2343 # instead of printing a command list, print a status map
michael@428 2344 # that shows all packages and how the build process would
michael@428 2345 # change their status
michael@428 2346 sub print_status ($$$$$) {
michael@428 2347 my ($installed, $repository, $list, $bonly, $clist) = @_;
michael@428 2348 my (%bonly) = map { $_ => 1 } @$bonly;
michael@428 2349 my (%map, $n, @names, $t);
michael@428 2350 my ($old, $tag, $new);
michael@428 2351
michael@428 2352 # augment map with additional information
michael@428 2353 # about conflicting and binary only (temporary) packages
michael@428 2354 foreach (@$list, @$clist) {
michael@428 2355 next if (not $_->{release} =~ m/\S/);
michael@428 2356 $map{$_->{name}} = {
michael@428 2357 rel => "$_->{version}-$_->{release}",
michael@428 2358 status => $_->{STATUS}
michael@428 2359 };
michael@428 2360 }
michael@428 2361 foreach (@$bonly) {
michael@428 2362 next if (not $_->{release} =~ m/\S/);
michael@428 2363 $map{$_->{name}} = {
michael@428 2364 rel => "$_->{version}-$_->{release}",
michael@428 2365 status => 'TEMP'
michael@428 2366 };
michael@428 2367 }
michael@428 2368
michael@428 2369 # augment map with additional information
michael@428 2370 # about up-to-date and new packages
michael@428 2371 @names = keys(%map);
michael@428 2372 foreach $n (keys(%$installed)) {
michael@428 2373 next if ($n =~ m/::/);
michael@428 2374 next if (exists($map{$n}));
michael@428 2375 next if (not (grep { $_ ne '' } keys(%{$installed->{$n}})));
michael@428 2376 $map{$n}->{'status'} = 'OK';
michael@428 2377 push(@names, $n);
michael@428 2378 }
michael@428 2379 foreach $n (keys(%$repository)) {
michael@428 2380 next if ($n =~ m/::/);
michael@428 2381 next if (exists($map{$n}));
michael@428 2382 next if (not (grep { $_ ne '' } keys(%{$repository->{$n}})));
michael@428 2383 $t = find_target($n, $repository, 0);
michael@428 2384 $map{$n}->{'status'} = 'NEW';
michael@428 2385 $map{$n}->{'rel'} = vs($t);
michael@428 2386 push(@names, $n);
michael@428 2387 }
michael@428 2388
michael@428 2389 # generate status output
michael@428 2390 foreach $n (sort(@names)) {
michael@428 2391 $old = join(',',
michael@428 2392 map { "$n-$_" }
michael@428 2393 sort
michael@428 2394 grep { $_ ne '-' }
michael@428 2395 keys(%{$installed->{$n}})
michael@428 2396 );
michael@428 2397 $old = $n if ($old eq '');
michael@428 2398 $tag = $map{$n}->{status};
michael@428 2399 $new = defined($map{$n}->{rel}) ? " $n-$map{$n}->{rel}" : '';
michael@428 2400 printf("%-35s %-8s%s\n", $old, $tag, $new);
michael@428 2401 }
michael@428 2402 }
michael@428 2403
michael@428 2404 # compute path to source RPM from rpm config and target data
michael@428 2405 sub target2srcrpm ($$) {
michael@428 2406 my ($target, $c) = @_;
michael@428 2407 return $c->{srcrpmdir}.'/'.$target->{name}.'-'.$target->{version}.'-'.$target->{release}.'.src.rpm';
michael@428 2408 }
michael@428 2409
michael@428 2410 # compute path to binary RPM from rpm config and target data
michael@428 2411 sub target2rpm ($$) {
michael@428 2412 my ($target, $c) = @_;
michael@428 2413 my ($tmpl) = $c->{template};
michael@428 2414 my ($popt) = $target->{PROXY} ? '+PROXY' : '';
michael@428 2415
michael@428 2416 $tmpl =~ s/%{NAME}/$target->{name}/;
michael@428 2417 $tmpl =~ s/%{VERSION}/$target->{version}/;
michael@428 2418 $tmpl =~ s/%{RELEASE}/$target->{release}$popt/;
michael@428 2419
michael@428 2420 return $c->{rpmdir}.'/'.$tmpl;
michael@428 2421 }
michael@428 2422
michael@428 2423 # merge parameters from installed package
michael@428 2424 # with new parameter set and global parameters
michael@428 2425 # from configuration
michael@428 2426 # then map the result to --define command line arguments
michael@428 2427 # suitable for rpm
michael@428 2428 sub make_defines ($$$$) {
michael@428 2429 my ($old, $new, $def, $c) = @_;
michael@428 2430 my ($with);
michael@428 2431
michael@428 2432 $old = {} unless $old;
michael@428 2433 $def = {} unless $def;
michael@428 2434
michael@428 2435 # override old parameters with new parameters
michael@428 2436 # drop new parameters that do not exist in old set
michael@428 2437 $old = { %$old };
michael@428 2438 override_options($old, $new, $c->{optreg});
michael@428 2439
michael@428 2440 # convert parameters to --define command line options
michael@428 2441 # skip parameter templates from index
michael@428 2442 # skip parameters that are identical to defaults
michael@428 2443 $with = join(' ',
michael@428 2444 map { "--define '$_ $old->{$_}'" }
michael@428 2445 sort grep {
michael@428 2446 $old->{$_} =~ m/\S/ &&
michael@428 2447 $old->{$_} !~ m/^%/ &&
michael@428 2448 $old->{$_} ne $def->{$_}
michael@428 2449 } keys %$old
michael@428 2450 );
michael@428 2451
michael@428 2452 $with = ' '.$with if ($with ne '');
michael@428 2453
michael@428 2454 return $with;
michael@428 2455 }
michael@428 2456
michael@428 2457 # compute new target based on old target augmented with options from
michael@428 2458 # a binary RPM file
michael@428 2459 sub binary_target ($$) {
michael@428 2460 my ($t, $fn) = @_;
michael@428 2461 my (%target) = %$t;
michael@428 2462
michael@428 2463 # pull in options from binary RPM file
michael@428 2464 delete $target{'OPTIONS'};
michael@428 2465 get_with(\%target, $fn);
michael@428 2466 return \%target;
michael@428 2467 }
michael@428 2468
michael@428 2469 # return path to master package for a proxy package
michael@428 2470 sub find_proxy ($$) {
michael@428 2471 my ($t, $bpkg) = @_;
michael@428 2472 my (@l) = run($config->{"rpm"} . " -ql $t->{name}");
michael@428 2473 my ($link) = (grep { $_ =~ m/\/\.prefix-$t->{name}$/ } @l)[0];
michael@428 2474 return if (not defined($link));
michael@428 2475 chomp $link;
michael@428 2476 my ($prefix) = readlink($link);
michael@428 2477 return if (not defined($prefix));
michael@428 2478 $bpkg =~ s/.*\///;
michael@428 2479 $bpkg =~ s/\+PROXY(\.[^-]+-[^-]+)-[^-]+\.rpm$/$1-*.rpm/;
michael@428 2480 return (glob("$prefix/RPM/PKG/$bpkg"))[0];
michael@428 2481 }
michael@428 2482
michael@428 2483 # indent text to form a block
michael@428 2484 sub indent ($) {
michael@428 2485 my ($txt) = @_;
michael@428 2486 $txt =~ s/^/ /gm;
michael@428 2487 return $txt;
michael@428 2488 }
michael@428 2489
michael@428 2490 # print commands from package build list
michael@428 2491 # c -> configuration to derive paths from
michael@428 2492 # uncond -> always do the --rebuild
michael@428 2493 # with -> parameter set passed to build tool
michael@428 2494 # ignore -> generate script that does not stop on error
michael@428 2495 # usebin -> build-time check to skip rebuild when binary exists
michael@428 2496 # allbin -> usebin also for goals
michael@428 2497 sub print_list1 ($$$$$$$) {
michael@428 2498 my ($list, $c, $uncond, $with, $ignore, $usebin, $allbin) = @_;
michael@428 2499 my ($pkg, $spkg, $bpkg, $uvhpkg, $ppkg);
michael@428 2500 my ($opt);
michael@428 2501 my ($cmd1, $cmd2, $mark);
michael@428 2502 my ($cmd3, $srcpkg);
michael@428 2503
michael@428 2504 $mark = '::::';
michael@428 2505
michael@428 2506 my $err;
michael@428 2507 if ($ignore) { $err = "|| true" } else { $err = "|| exit \$?" };
michael@428 2508 foreach (@$list) {
michael@428 2509 $pkg = $_->{name};
michael@428 2510 $spkg = $_->{href};
michael@428 2511 unless ($spkg =~ m/\S/) {
michael@428 2512 die "openpkg:build:FATAL: internal error, ",vsn($_)," without source URL\n";
michael@428 2513 }
michael@428 2514 $bpkg = target2rpm($_, $c); $uvhpkg = $bpkg;
michael@428 2515 $srcpkg = target2srcrpm($_, $c);
michael@428 2516 $cmd3 = '';
michael@428 2517
michael@428 2518 # rebuild binary package IF
michael@428 2519 # 'unconditional' option
michael@428 2520 # OR target is tagged as rebuilding
michael@428 2521 # OR there is no binary package
michael@428 2522 # OR dependency check found that installed package is not suitable
michael@428 2523 # OR existing binary package doesn't satisfy wanted options
michael@428 2524 $cmd1 = undef;
michael@428 2525 if ( $uncond
michael@428 2526 || !-f $bpkg
michael@428 2527 || !target_suitable(binary_target($_, $bpkg), $with, 1)) {
michael@428 2528
michael@428 2529 $opt = make_defines($_->{OPTIONS}, $with,
michael@428 2530 $_->{DEFOPTS}, $c);
michael@428 2531
michael@428 2532 # proxy packages are rebuilt from their maste
michael@428 2533 # hierachy
michael@428 2534 # someone preferred a binary from the repository
michael@428 2535 # just copy it to the local store
michael@428 2536 if ($_->{PROXY}) {
michael@428 2537 $ppkg = find_proxy($_,$bpkg) or
michael@428 2538 die "openpkg:build:FATAL: proxy package ",vsn($_)," does not exist\n";
michael@428 2539
michael@428 2540 # rpm doesn't support additional parameters to the
michael@428 2541 # mkproxy script
michael@428 2542 # $cmd1 = npriv($config->{"mkp"} . " $ppkg -- -o $bpkg");
michael@428 2543 $cmd1 = "( cd $c->{rpmdir} && ".
michael@428 2544 npriv($config->{"mkp"} . " $ppkg").
michael@428 2545 " )";
michael@428 2546 } elsif (defined $_->{prefix}) {
michael@428 2547 $cmd1 = '';
michael@428 2548 if ($spkg =~ m|^\.?/|) {
michael@428 2549 $uvhpkg = $spkg;
michael@428 2550 }
michael@428 2551 else {
michael@428 2552 $cmd1 .= npriv($config->{"curl"} . " -# -o $bpkg $spkg $err\n");
michael@428 2553 $cmd3 = npriv("rm -f $bpkg >/dev/null 2>&1 $err\n") unless ($opt_k);
michael@428 2554 }
michael@428 2555 } else {
michael@428 2556 $cmd1 = '';
michael@428 2557 if ($spkg =~ m|^\.?/|) {
michael@428 2558 $cmd1 .= npriv($config->{"rpm"} . "$opt --rebuild $spkg $err\n");
michael@428 2559 }
michael@428 2560 else {
michael@428 2561 $cmd1 .= "if test ! -f $srcpkg; then\n";
michael@428 2562 $cmd1 .= indent(npriv($config->{"curl"} . " -# -o $srcpkg $spkg $err\n"));
michael@428 2563 $cmd1 .= "fi\n";
michael@428 2564 $cmd1 .= npriv($config->{"rpm"} . "$opt --rebuild $srcpkg $err\n");
michael@428 2565 $cmd1 .= npriv("rm -f $srcpkg >/dev/null 2>&1 $err\n") unless ($opt_k);
michael@428 2566 }
michael@428 2567 }
michael@428 2568 }
michael@428 2569
michael@428 2570 # wrap build command with build-time check for existing
michael@428 2571 # binary target
michael@428 2572 if (defined($cmd1) && ($allbin || ($usebin && !$_->{GOAL}))) {
michael@428 2573 $cmd1 = "if test ! -f $uvhpkg; then\n".indent($cmd1)."fi\n";
michael@428 2574 }
michael@428 2575
michael@428 2576 # if package exist force rpm to copy over new files
michael@428 2577 # better than erasing everything and losing configuration
michael@428 2578 # files
michael@428 2579 $opt = ($_->{REBUILD} || ($allbin || ($usebin && !$_->{GOAL}))) ? ' --force' : '';
michael@428 2580 $cmd2 = '';
michael@428 2581 $cmd2 .= priv($config->{"rpm"} . "$opt -Uvh $uvhpkg $err\n");
michael@428 2582 if ($allbin || ($usebin && !$_->{GOAL})) {
michael@428 2583 $cmd2 = "if test \".`".$config->{"rpm"}." -q --qf '\%{SIGMD5}' $pkg`\" != \".`".$config->{"rpm"}." -qp --qf '\%{SIGMD5}' $uvhpkg`\"; then\n".indent($cmd2)."fi\n";
michael@428 2584 }
michael@428 2585 $cmd2 = $cmd1.$cmd2 if ($cmd1);
michael@428 2586 $cmd2 = $cmd2.$cmd3 if ($cmd3);
michael@428 2587 print "echo $mark $spkg $mark\n".$cmd2."echo $mark $spkg = \$? $mark\n";
michael@428 2588 }
michael@428 2589 }
michael@428 2590
michael@428 2591 # print commands for the temporary package list
michael@428 2592 # temporary packages are only used for building other packages
michael@428 2593 # and are removed when everything is done
michael@428 2594 sub print_list2 ($$) {
michael@428 2595 my ($list, $c) = @_;
michael@428 2596 my ($pkg);
michael@428 2597
michael@428 2598 foreach (@$list) {
michael@428 2599 $pkg = "$_->{name}-$_->{version}-$_->{release}";
michael@428 2600 print priv($config->{"rpm"} . " -e $pkg\n");
michael@428 2601 }
michael@428 2602 }
michael@428 2603

mercurial