Mon, 28 Jan 2013 17:37:18 +0100
Correct socket error reporting improvement with IPv6 portable code,
after helpful recommendation by Saúl Ibarra Corretgé on OSips devlist.
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 |