Thu, 04 Oct 2012 20:30:05 +0200
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/>/>/g; |
michael@428 | 968 | s/</</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 |