From 013ce702bc8eb23ddb401684800292bcc400277d Mon Sep 17 00:00:00 2001 From: "Ralf S. Engelschall" Date: Tue, 26 Nov 2002 19:54:11 +0000 Subject: [PATCH] new package: openpkg-tool 20021126 (OpenPKG Tool) --- openpkg-tool/Makefile | 4 + openpkg-tool/openpkg-build.pl | 1291 ++++++++++++++++++++++++++++++++ openpkg-tool/openpkg-index.pl | 866 +++++++++++++++++++++ openpkg-tool/openpkg-tool.spec | 89 +++ openpkg-tool/openpkg.1 | 327 ++++++++ openpkg-tool/openpkg.pod | 265 +++++++ openpkg-tool/openpkg.sh | 90 +++ 7 files changed, 2932 insertions(+) create mode 100644 openpkg-tool/Makefile create mode 100644 openpkg-tool/openpkg-build.pl create mode 100644 openpkg-tool/openpkg-index.pl create mode 100644 openpkg-tool/openpkg-tool.spec create mode 100644 openpkg-tool/openpkg.1 create mode 100644 openpkg-tool/openpkg.pod create mode 100644 openpkg-tool/openpkg.sh diff --git a/openpkg-tool/Makefile b/openpkg-tool/Makefile new file mode 100644 index 0000000000..7a0ef3bc95 --- /dev/null +++ b/openpkg-tool/Makefile @@ -0,0 +1,4 @@ + +openpkg.1: openpkg.pod + pod2man --section=1 --quotes=none --release="openpkg-tool" --center="OpenPKG Maintainance" openpkg.pod >openpkg.1 + diff --git a/openpkg-tool/openpkg-build.pl b/openpkg-tool/openpkg-build.pl new file mode 100644 index 0000000000..e725dec06d --- /dev/null +++ b/openpkg-tool/openpkg-build.pl @@ -0,0 +1,1291 @@ +## +## openpkg-build -- create build scripts from package index +## +## Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH +## Copyright (c) 2000-2002 The OpenPKG Project +## Copyright (c) 2000-2002 Ralf S. Engelschall +## +## Permission to use, copy, modify, and distribute this software for +## any purpose with or without fee is hereby granted, provided that +## the above copyright notice and this permission notice appear in all +## copies. +## +## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +## SUCH DAMAGE. +## + +require 5; + +$|=1; # autoflush + +use strict; +use vars qw/$opt_R $opt_r $opt_f $opt_u $opt_U $opt_a $opt_A $opt_z $opt_Z $opt_P $opt_N $opt_E $opt_i $opt_D $opt_p $opt_q/; +getopts('R:r:f:uUaAzZP:N:E:iD:p:q'); + +########################################################################## + +sub getopts ($) { + my($opts) = @_; + my(%optf) = map { /(\w)/; $1 => $_ } $opts =~ /(\w:|\w)/g; + my(%opts,@argv,$optarg); + + foreach (@ARGV) { + if (@argv) { + push @argv, $_; + } elsif (defined $optarg) { + if (exists $opts{$optarg}) { + $opts{$optarg} .= " $_"; + } else { + $opts{$optarg} = $_; + } + $optarg = undef; + } elsif (!/^[-]/) { + push @argv, $_; + } else { + while (/^\-(\w)(.*)/) { + if (exists $optf{$1}) { + if (length($optf{$1}) > 1) { + if ($2 ne '') { + if (exists $opts{$1}) { + $opts{$1} .= " $2"; + } else { + $opts{$1} = $2; + } + } else { + $optarg = $1; + } + last; + } else { + $opts{$1} = 1; + } + } else { + warn "warning: unknown option $_\n"; + } + $_ = "-$2"; + } + } + } + if (defined $optarg) { + warn "warning: option $optarg requires an argument\n"; + } + + foreach (keys %opts) { + eval '$opt_'.$_.' = "'.quotemeta($opts{$_}).'";'; + } + + @ARGV = @argv; +} + +my(%env) = ( '' => { } ); +if (open(FH, "< $ENV{'HOME'}/.openpkg/build")) { + my($env) = $env{''}; + while () { + if (/^\s*\[([^\]]*)\]/) { + $env{$1} = { } unless $env{$1}; + $env = $env{$1}; + } elsif (my($opt,$val) = /^\-([RfruUaAzPN])\s*(.*?)\s*$/) { + $val = 1 unless defined $val; + $env->{$opt} = $val; + } + } + close(FH); +} + +die "openpkg:build:USAGE: $0 [-R rpm] [-r repository] [-f index.rdf] [-uUzZiq] [-P priv-cmd] [-N non-priv-cmd] [-p platform] [-Dwith ...] [-Ename ...] ( [-aA] | patternlist )\n" + unless $#ARGV >= 0 || ($#ARGV == -1 && ($opt_a || $opt_A)); + +########################################################################## + +sub conditional ($$) { + my($cond,$with) = @_; + my(@s,$res); + + return 1 if $cond eq ''; + + foreach (split(/\s+/,$cond)) { + if ($_ eq '+') { + die "FATAL: stack underflow in: $cond\n" if scalar(@s)<2; + my($a) = pop @s; + my($b) = pop @s; + push @s, $a && $b; + } elsif ($_ eq '|') { + die "FATAL: stack underflow in: $cond\n" if scalar(@s)<2; + my($a) = pop @s; + my($b) = pop @s; + push @s, $a && $b; + } elsif ($_ eq '!') { + die "FATAL: stack underflow in: $cond\n" if scalar(@s)<1; + my($a) = pop @s; + push @s, !$a; + } else { + push @s, $with->{$_} eq 'yes'; + } + } + die "FATAL: stack underflow in: $cond\n" if scalar(@s)<1; + $res = pop @s; + + die "FATAL: stack not empty in: $cond\n" if scalar(@s)>0; + return $res; +} + +########################################################################## + +my($RPM,$RPM_PRIV,$RPM_NPRIV,$CURL,$PROG); + +$RPM = $opt_R || $env{''}->{opt}->{'R'} || 'rpm'; +$RPM = (`which $RPM` =~ m{^(/.*)})[0]; +die "FATAL: cannot locate rpm in path\n" unless $RPM =~ m{^/}; + +# augment command line parameters +foreach my $env (sort { $a cmp $b } grep { $RPM =~ /^\Q$_\E/ } keys %env) { + while (my($opt,$val) = each %{$env{$env}}) { + eval "\$opt_$opt = '$val' unless defined \$opt_$opt;"; + } +} + +$RPM_PRIV = ($opt_P ? $opt_P." ".$RPM : $RPM); +$RPM_NPRIV = ($opt_N ? $opt_N." ".$RPM : $RPM); +$CURL = $RPM; +$CURL =~ s/bin\/rpm$/lib\/openpkg\/curl/ + or die "FATAL: cannot deduce curl path from $RPM\n"; + +($PROG) = $0 =~ /(?:.*\/)?(.*)/; + +sub version_cmp ($$) { + my($a,$b) = @_; + my(@a,@b,$c); + my($ax,$bx); + + @a = split(/\./, $a); + @b = split(/\./, $b); + + while (@a && @b) { + if ($a[0] =~ /^\d+$/ && $b[0] =~ /^\d+$/) { + $c = $a[0] <=> $b[0]; + } elsif ((($a,$ax) = $a[0] =~ /^(\d+)(.*)$/) && + (($b,$bx) = $b[0] =~ /^(\d+)(.*)$/)) { + $c = $a <=> $b; + $c = $ax cmp $bx unless $c; + } else { + $c = $a[0] cmp $b[0]; + } + return $c if $c; + shift @a; + shift @b; + } + + $c = scalar(@a) <=> scalar(@b); + + return $c; +} + +sub release_cmp ($$) { + my($a,$b) = @_; + + return $a cmp $b; +} + +sub vcmp ($$) { + my($a,$b) = @_; + my($av,$ar) = $a =~ /^(.*?)(?:\-([\d\.]+))?$/; + my($bv,$br) = $b =~ /^(.*?)(?:\-([\d\.]+))?$/; + my($c); + + if ((defined $ar) && (defined $br)) { + $c = release_cmp($ar,$br); + return $c if $c; + } + if ((defined $av) && (defined $bv)) { + $c = version_cmp($av,$bv); + return $c if $c; + } + return 0; +} + +sub vs ($) { + my($t) = @_; + return "$t->{version}-$t->{release}"; +} + +sub vsn ($) { + my($t) = @_; + return "$t->{name}-$t->{version}-$t->{release}"; +} + +########################################################################## + +sub get_config () +{ + my($c,@q); + + $c = `$RPM_NPRIV --eval '%{_rpmdir} %{_rpmfilename} %{_target_os} %{_target_cpu} %{_target_platform} %{_prefix}'`; + chomp($c); + (@q) = split(/\s+/,$c); + + $q[1] =~ s/%{OS}/$q[2]/; + $q[1] =~ s/%{ARCH}/$q[3]/; + + return { + rpmdir => $q[0], + template => $q[1], + platform => $q[4], + prefix => $q[5] + }; +} + +sub get_release () { + my($rel,$url); + + ($rel) =`$RPM_NPRIV -qi openpkg` =~ /Version:\s*(\S+)/m; + + if ($rel =~ /^\d+$/) { + print "# $PROG current($rel)\n"; + print "# using '$RPM_NPRIV' (build) and '$RPM_PRIV' (install)\n"; + $url = "ftp://ftp.openpkg.org/current/"; + } elsif ($rel =~ /^(\d+\.\d+)/) { + $rel = $1; + print "# $PROG release($rel)\n"; + $url = "ftp://ftp.openpkg.org/release/$rel/"; + } else { + die "FATAL: don't know how to handle this release\n"; + } + + return $url; +} + +sub get_installed () { + my(%map); + my(@l) = `$RPM_NPRIV --provides -qa`; + + foreach (@l) { + /^(\S+)\s*(?:=\s*([^\s\-]+)-(\S+))?$/; + push(@{$map{$1}->{"$2-$3"}}, { + name => $1, + version => (defined $2 ? $2 : '*'), + release => (defined $3 ? $3 : '*') + }); + } + + return \%map; +} + +sub revdep ($$$) { + my($rev,$t,$name) = @_; + + return 1 if $name eq $t->{name}; + + foreach (@{$rev->{$_}}) { + return 1 if revdep($rev,$t,$_->{name}); + } + return -1; +} + +sub get_revdep ($) { + my($env) = @_; + my($i) = $env->{'installed'}; + my($r) = $env->{'repository'}; + my($pkg, %rev); + my(@vers,$t,@names); + + print "# computing reverse dependencies\n"; + + foreach $pkg (keys %$i) { + + unless ($r->{$pkg}) { + print "# ATTENTION: $pkg has no upgrade path\n"; + next; + } + + @vers = get_versions($r->{$pkg}, sub { 1; }); + foreach (@vers) { + foreach $t (@{$r->{$pkg}->{$_}}) { + next unless $i->{$t->{name}}; + next unless $t->{depends}; + + @names = grep { $_ ne '' } + map { /^(\S+)/ } + @{$t->{depends}}; + next unless @names; + push @{$rev{$_}}, $t foreach @names; + } + } + } + + foreach $pkg (keys %rev) { + $rev{$pkg} = [ + sort { + revdep(\%rev, $b, $a->{name}); + } @{$rev{$pkg}} + ]; + } + + return \%rev; +} + +sub parse_options ($) { + my($l) = @_; + $l = [ split(/\n+/, $l) ] unless ref $l; + my(%with) = map { /--define\s*'(\S+)\s+(\S+?)'/ } @$l; + return unless %with; + return \%with; +} + +sub override_options ($$) { + my($old, $new) = @_; + while (my ($k,$v) = each %$new) { + $old->{$k} = $v if exists $old->{$k}; + } +} + +sub get_with ($;$) { + my($t,$fn) = @_; + my(@l,%with); + + unless ($t->{OPTIONS}) { + if (defined $fn) { + @l = `$RPM_NPRIV -qi -p $fn`; + } else { + @l = `$RPM_NPRIV -qi $t->{name}`; + } + $t->{OPTIONS} = parse_options(\@l); + } + return $t->{OPTIONS}; +} + +sub relurl ($$$) { + my($url,$fn,$suburl) = @_; + my($subfn,$submap); + + unless ($suburl =~ /^\w+:\/\// || $suburl =~ /^\//) { + if (defined $fn) { + $subfn = $fn; + $subfn =~ s/\/[^\/]*$//; + $subfn .= '/' unless $subfn =~ /\/$/; + $subfn .= $suburl; + $suburl = $subfn; + } else { + $subfn = $url; + $subfn =~ s/\/[^\/]*$//; + $subfn .= '/' unless $subfn =~ /\/$/; + $suburl = "$subfn$suburl"; + $subfn = undef; + } + } + + return ($suburl, $subfn); +} + +sub xel($) { + my($a) = @_; + my($l) = $a->[0]; + return '' if ref $l; + return $l; +} + +sub get_index ($$$) { + my($url,$fn,$with) = @_; + my($ua,$req,$res,$rdf); + my($bzip2,$path); + my(%map,@include); + my($fetch); + + $fetch = defined $fn ? $fn : $url; + + $bzip2 = $RPM; + $bzip2 =~ s/bin\/rpm$/lib\/openpkg\/bzip2/ + or die "FATAL: cannot deduce bzip2 path from $RPM\n"; + + $fetch !~ /\.bz2$/ || -x $bzip2 + or die "FATAL: $bzip2 not found\n"; + + if ($fetch =~ /^\w+:/) { # looks like URL scheme + + print "# curling index $fetch\n"; + if ($fetch =~ /\.bz2$/) { + $path = "$CURL -q -s -o - \"$fetch\" | $bzip2 -dc |"; + } else { + $path = "$CURL -q -s -o - \"$fetch\" |"; + } + } else { + print "# reading index file $fn\n"; + if ($fetch =~ /\.bz2$/) { + $path = "$bzip2 -dc $fetch |"; + } else { + $path = "< $fetch"; + } + } + + open(RFH, $path) or + die "FATAL: cannot open '$fetch' ($!)\n"; + + eval { + require XML::Simple; + }; + if ($@) { + + print "# using simple text parser\n"; + + my($section); + my($name,$version); + my($href,$release,$desc); + my(@prereq,@bprereq); + my(@provides,@conflicts); + my($platform,$prefix); + my($rec); + my($tag,$cond,$body); + my($useit); + + while () { + + s/>/>/g; + s/</([^<]*)/; + + $useit = conditional($cond,$with); + + if ($tag eq 'Description') { + $section = 'description'; + } elsif ($tag eq '/Description') { + $section = undef; + } elsif ($section eq 'description') { + $desc .= $_; + } elsif ($tag eq 'PreReq') { + $section = 'prereq' if $useit; + } elsif ($tag eq '/PreReq') { + $section = undef; + } elsif ($tag eq 'BuildPreReq') { + $section = 'bprereq' if $useit; + } elsif ($tag eq '/BuildPreReq') { + $section = undef; + } elsif ($tag eq 'Provides') { + $section = 'provides' if $useit; + } elsif ($tag eq '/Provides') { + $section = undef; + } elsif ($tag eq 'Conflicts') { + $section = 'conflicts' if $useit; + } elsif ($tag eq '/Conflicts') { + $section = undef; + } elsif ($tag eq 'Name') { + $name = $body; + } elsif ($tag eq 'Version') { + $version = $body; + } elsif ($tag eq 'Release') { + $release = $body; + } elsif ($tag eq 'Platform') { + $platform = $body; + } elsif ($tag eq 'Prefixes') { + $prefix = $body; + } elsif ($tag eq 'rdf:li') { + if ($section eq 'prereq') { + push(@prereq, $body); + } elsif ($section eq 'bprereq') { + push(@bprereq, $body); + } elsif ($section eq 'provides') { + push(@provides, $body); + } elsif ($section eq 'conflicts') { + push(@conflicts, $body); + } + } elsif ($tag eq '/rdf:Description') { + + if (defined $href && + defined $name && + defined $version && + defined $release) { + + @provides = map { + /(\S+)\s*(?:=\s*(\S+?)\-(\S+))?$/; + { + name => $1, + version => $2, + release => $3 + } + } @provides; + + unless (grep($_->{name} eq $name, @provides)) { + push(@provides, { + name => $name, + version => $version, + release => $release + }); + } + + $rec = { + href => (relurl($url, undef, $href))[0], + name => $name, + version => $version, + release => $release, + depends => [ @bprereq ], + keeps => [ @prereq ], + conflicts => [ @conflicts ], + desc => $desc, + platform => $platform, + prefix => $prefix + }; + $rec->{OPTIONS} = parse_options($rec->{desc}); + + foreach (@provides) { + push(@{$map{$_->{name}}->{vs($_)}}, $rec); + } + } + + $href = undef; + } + } + } else { + + print "# using XML parser\n"; + + my($xml) = XML::Simple::XMLin(\*RFH, forcearray => 1); + my($desc) = $xml->{'Repository'}->[0]->{'rdf:Description'}; + my($sub) = $xml->{'Repository'}->[0]->{'Repository'}; + my($provides,@provides,$rec); + my($href,$name,$version,$release); + + foreach (@$desc) { + + $href = $_->{'href'}; + $name = xel($_->{'Name'}); + $version = xel($_->{'Version'}); + $release = xel($_->{'Release'}); + + next unless defined $href && + defined $name && + defined $version && + defined $release; + + $provides = $_->{'Provides'}->[0]->{'rdf:bag'}->[0]->{'rdf:li'}; + + @provides = map { + /(\S+)\s*(?:=\s*(\S+?)\-(\S+))?$/; + { + name => $1, + version => $2, + release => $3 + } + } @$provides; + + unless (grep($_->{name} eq $name, @provides)) { + push(@provides, { + name => $name, + version => $version, + release => $release + }); + } + + $rec = { + href => (relurl($url, undef, $href))[0], + name => $name, + version => $version, + release => $release, + platform => xel($_->{'Platform'}), + prefix => xel($_->{'Prefixes'}), + depends => + ( $_->{'BuildPreReq'}->[0]->{'rdf:bag'}->[0]->{'rdf:li'} + || [] ), + keeps => + ( $_->{'PreReq'}->[0]->{'rdf:bag'}->[0]->{'rdf:li'} + || [] ), + desc => xel($_->{'Description'}) + }; + $rec->{OPTIONS} = parse_options($rec->{desc}); + + foreach (@provides) { + push(@{$map{$_->{name}}->{vs($_)}}, $rec); + } + } + + if ($sub) { + @include = map { $_->{href} } @$sub; + } + } + + close(RFH) + or die "FATAL: an I/O error occured\n"; + + # + # cannot do real recursions on file handles, so we simply append + # all sub-RDFs, the result is flattend into a big hash anyway + # + foreach (@include) { + my($submap); + my($suburl,$subfn) = relurl($url,$fn,$_); + $submap = get_index($suburl,$subfn,$with); + while (my($name,$vmap) = each %$submap) { + while (my($vs,$recs) = each %$vmap) { + push @{$map{$name}->{$vs}}, @$recs; + } + } + } + + return \%map; +} + +# +# grep all versions of a name that +# satisfy a condition +# +sub get_versions ($$) { + my($relmap, $cond) = @_; + return grep { $cond->($_); } + sort { vcmp($a,$b); } keys %$relmap; +} + +# +# there can be multiple sources for a target release +# +sub chose_source ($$@) { + my($env, $name, $vmap, @vers) = @_; + my(@recs,@nrecs,$rec); + + return unless @vers; + + @recs = grep { + $env->{sourceonly} ? ( + !(defined $_->{'platform'}) + ) : ( + !(defined $_->{'platform'}) || ( + defined $_->{'prefix'} && + $_->{'platform'} eq $env->{config}->{platform} && + $_->{'prefix'} eq $env->{config}->{prefix} + ) + ) + } map { @{$vmap->{$_}} } @vers; + return unless @recs; + + if (scalar(@recs) > 1) { + @nrecs = grep { + $env->{built}->{$_->{name}} || + $env->{installed}->{$_->{name}} + } @recs; + @recs = @nrecs if @nrecs; + } + + if (scalar(@recs) > 1 && !$env->{sourceonly}) { + @nrecs = grep { + defined $_->{'platform'} + } @recs; + @recs = @nrecs if @nrecs; + } + + if (scalar(@recs) > 1) { + + print "# ambigous sources for $name\n"; + my($i) = 0; + foreach (@recs) { + print "# $i: ".vsn($_)." = $_->{href}\n"; + $i++; + } + die "ERROR: ambigous dependency\n"; + + } else { + if ($env->{upgrade}) { + $rec = $recs[-1]; + } else { + $rec = $recs[0]; + } + } + + print "# source for $name is ".vsn($rec)."\n"; + + return $rec; +} + +# +# see wether target is in map +# +sub target_exists ($$) { + my($target, $map) = @_; + my($vmap) = $map->{$target->{name}}; + + return unless $vmap; + + return !defined $target->{version} || + defined $vmap->{vs($target)}; +} + +# +# find target in map +# +sub find_target ($$) { + my($name, $map) = @_; + my($vmap) = $map->{$name}; + my(@vs); + + return unless $vmap; + + @vs = sort { vcmp($b,$a) } keys %$vmap; + return $vmap->{$vs[0]}->[-1]; +} + +# +# see wether target has conflicts in map +# +sub target_conflicts ($$) { + my($target, $map) = @_; + my($t); + + foreach (@{$target->{conflicts}}) { + $t = find_target($_, $map); + return $t if $t; + } + + return; +} + +# +# retrieve build dependencies for target in map +# +sub target_depends ($$) { + my($target, $map) = @_; + my($vmap,$vers); + + die "FATAL: ",vsn($target)," not in depend map\n" + unless + ( $vmap = $map->{$target->{name}} ) && + ( defined $target->{version} ) && + ( $vers = $vmap->{vs($target)} ) && + @$vers; + + return $vers->[0]->{depends}; +} + +# +# retrieve runtime dependencies for target in map +# +sub target_keeps ($$) { + my($target, $map) = @_; + my($vmap,$vers); + + die "FATAL: ",vsn($target)," not in keep map\n" + unless + ( $vmap = $map->{$target->{name}} ) && + ( defined $target->{version} ) && + ( $vers = $vmap->{vs($target)} ) && + @$vers; + + return $vers->[0]->{keeps}; +} + +# +# test wether target could be upgraded +# +sub target_newer ($$) { + my($target, $map) = @_; + my($vs) = vs($target); + my($vmap) = $map->{$target->{name}}; + + return 1 unless $vmap; + + return !grep { vcmp($vs, $_) <= 0; } keys %$vmap; +} + +# +# check wether installed package matches +# build options +# +sub target_suitable ($$) { + my($target, $with) = @_; + my($iwith); + my($k,$v); + + $iwith = $target->{OPTIONS}; + while (($k,$v) = each %$with) { + if (exists $iwith->{$k}) { + return 0 if $iwith->{$k} ne $with->{$k}; + } + } + + return 1; +} + +# +# report options that are not used for +# +sub warn_about_options ($$) { + my($target, $with) = @_; + my($iwith) = $target->{OPTIONS}; + my($k,$v); + + return unless defined $iwith; + while (($k,$v) = each %$with) { + if (!exists $iwith->{$k}) { + print "# ATTENTION: $target->{name} ignores option '$k'\n"; + } + } +} + +# +# locate target for a dependency +# +sub dep2target ($$) { + my($dep, $env) = @_; + my($name,@vers); + my($i,$r,$b,$cond,$version); + my($t); + + $dep =~ s/(\S+)\s*//; + $name = $1; + + $i = $env->{installed}->{$name}; + $r = $env->{repository}->{$name}; + $b = $env->{built}->{$name}; + + return unless $i || $r || $b; + + if ($dep =~ /^>=\s*(\S+)$/) { + $version = $1; + $cond = sub { vcmp($_[0],$version) >= 0; }; + } elsif ($dep =~ /^=\s*(\S+)$/) { + $version = $1; + $cond = sub { vcmp($_[0],$version) == 0; }; + } elsif ($dep =~ /^\s*$/) { + $cond = sub { 1; }; + } else { + die "FATAL: don't know how to handle PreReq: $name $dep\n"; + } + + if ($i && (@vers = get_versions($i, $cond))) { + foreach (@vers) { + $t = $i->{$_}->[0]; + if (get_with($t), target_suitable($t, $env->{with})) { + if (!$env->{upgrade}) { + return ($t, 1); + } + } + } + } + if ($b && (@vers = get_versions($b, $cond))) { + return ($b->{$vers[0]}->[0], 1); + } + + return (chose_source($env, $name, $r, get_versions($r, $cond)), 0); +} + + +sub make_dep ($$$$$) { + my($target,$depth,$env,$list,$blist) = @_; + my($d,$k,%d,%k,$t,$old); + + if (target_exists($target, $env->{built})) { + print "# $target->{name} is already in list\n"; + return; + } + + if ($t = target_conflicts($target, $env->{installed})) { + print "# $target->{name} conflicts with ",vsn($t),"\n"; + return; + } + + if ($t = target_conflicts($target, $env->{built})) { + print "# $target->{name} conflicts with ",vsn($t),"\n"; + return; + } + + # + # see if a target is already installed and requires a rebuild + # + if ($t = find_target($target->{name}, $env->{installed})) { + if (exists $env->{exclude}->{$target->{name}}) { + print "# excluding $target->{name} (no upgrade allowed)\n"; + return; + } + get_with($t); + if ($target->{REBUILD}) { + print "# rebuilding $target->{name} (dependency)\n"; + } elsif ($env->{zero}) { + print "# rebuilding $target->{name} (zero)\n"; + } elsif ($env->{upgrade} && target_newer($target, $env->{installed})) { + print "# rebuilding $target->{name} (upgrade)\n"; + } elsif (!target_suitable($t, $env->{with})) { + print "# rebuilding $target->{name} (parameter mismatch)\n"; + } else { + print "# $target->{name} is already installed\n"; + return; + } + # use options from installed base + override_options($target->{OPTIONS}, $t->{OPTIONS}); + $target->{REBUILD} = 1; + } + + if (exists $env->{exclude}->{$target->{name}}) { + die "FATAL: target ".vsn($target)." is forbidden\n"; + } + + # mark this as a target before reverse dependencies trigger + # it again + push(@{$env->{built}->{$target->{name}}->{vs($target)}}, $target); + + $d = target_depends($target, $env->{repository}); + $k = target_keeps($target, $env->{repository}); + + # + # recurse over dependencies + # + if (@$d || @$k) { + + %d = map { $_ => 1 } @$d, @$k; + %k = map { $_ => 1 } @$k; + + foreach (keys %d) { + + # old index misses a OpenPKG provider in the index... skip it + next if $_ eq 'OpenPKG'; + + ($t,$old) = dep2target($_, $env); + if ($t) { + if ($old) { + print "# $target->{name} uses ".vsn($t)." for $_\n"; + next; + } + + # record which targets to keep in blist + if ($k{$_}) { + push(@$blist,$t); + print "# $target->{name} installs ".vsn($t)." for $_\n"; + } else { + print "# $target->{name} requires ".vsn($t)." for $_\n"; + } + make_dep($t,$depth+1,$env,$list,$blist); + } else { + die "FATAL: $target->{name} searches for a frood called '$_'\n"; + } + } + } + + print "# adding ".vsn($target)." to list\n"; + push(@$list, $target); + + if (!$env->{quick} && + $target->{name} ne 'openpkg' && + $target->{REBUILD}) { + + unless ($env->{revdep}) { + $env->{revdep} = get_revdep($env); + } + + foreach $t (@{$env->{revdep}->{$target->{name}}}) { + + # this is a rebuild, triggering further revdeps + $t->{REBUILD} = 1; + + # this is a rebuild, keep this installed + push(@$blist, $t); + + print "# rebuilding revdep ".vsn($t)."\n"; + make_dep($t,$depth+1,$env,$list,$blist); + } + } +} + +sub remove_list ($$$) { + my($targets, $keeps, $installed) = @_; + my(%keep); + + %keep = map { $_ => 1 } @$keeps; + return [ grep { + !$keep{$_} && !$installed->{$_->{name}}->{vs($_)}; + } @$targets + ]; +} + +sub build_list ($$) { + my($pattern, $env) = @_; + my(@goals,@targets,@keeps,$bonly,$t); + my($name,$r,$i,@vers); + my(@todo); + + if (defined $pattern) { + @todo = (); + foreach (split(/\s+/,$pattern)) { + next unless /\S/; + if (s/\*+$//) { + push @todo, '^'.quotemeta($_).''; + } else { + push @todo, '^'.quotemeta($_).'$'; + } + } + $pattern = join('|',@todo); + @todo = grep(/$pattern/, keys %{$env->{repository}}); + } else { + @todo = grep { + my($n) = $_; + (ref $env->{installed}->{$n}) && + grep { $_ ne '-' } keys %{$env->{installed}->{$n}} + } keys %{$env->{repository}}; + } + + + # + # chose sources for goals from repository + # + foreach $name (@todo) { + $t = undef; + + # + # keeping installed packages for goals is ugly + # -> we currently do not support installed source RPMs + # -> source RPMs might already have expired from repository + # + # consequence: + # -> goals are always upgraded to repository versions + # + #unless ($env->{upgrade}) { + # $i = $env->{installed}->{$name}; + # if (@vers = get_versions($i, sub { 1; })) { + # $t = chose_source($env, $name, $i, @vers); + # } + #} + + unless ($t) { + $r = $env->{repository}->{$name}; + if (@vers = get_versions($r, sub { 1; })) { + $t = chose_source($env, $name, $r, @vers); + } + } + + die "FATAL: no known source found for '$name'\n" unless $t; + + warn_about_options($t, $env->{with}); + push(@goals, $t); + } + return unless @goals; + + @targets = (); + @keeps = @goals; + foreach $t (@goals) { + print "# recursing over dependencies for ".vsn($t)."\n"; + make_dep($t,0,$env,\@targets,\@keeps); + } + + $bonly = remove_list(\@targets, \@keeps, $env->{installed}); + + return (\@targets, $bonly); +} + +####################################################################### + +sub target2rpm ($$) { + my($target,$c) = @_; + my($tmpl) = $c->{template}; + + $tmpl =~ s/%{NAME}/$target->{name}/; + $tmpl =~ s/%{VERSION}/$target->{version}/; + $tmpl =~ s/%{RELEASE}/$target->{release}/; + + return $c->{rpmdir}.'/'.$tmpl; +} + +####################################################################### + +sub binary_target ($$) { + my($t, $fn) = @_; + my(%target) = %$t; + + get_with(\%target, $fn); + + return \%target; +} + +sub make_defines ($$) { + my($old, $new) = @_; + my($with); + + # + # override old parameters with new parameters + # drop new parameters that do not exist in old set + # + # if there is no old set at all (which happens if there + # is no template and no installed package), just use the + # new parameters and assume these are useful. + # + if ($old) { + $old = { %$old }; + override_options($old, $new); + } else { + $old = $new; + } + + # + # convert parameters to --define command line options + # skip parameter templates from index + # + $with = join(' ',map { "--define '$_ $old->{$_}'" } + grep { $old->{$_} !~ /^%/ } keys %$old); + + $with = ' '.$with if $with ne ''; + + return $with; +} + +sub print_list1 ($$$@$) { + my($list,$c,$uncond,$with,$ignore) = @_; + my($spkg,$bpkg); + my($opt); + my($cmd1, $cmd2, $mark); + + $mark = '::::'; + + foreach (@$list) { + $spkg = $_->{href}; + $bpkg = target2rpm($_, $c); + + # + # rebuild binary package IF + # + # 'unconditional' option + # OR there is no binary package + # OR dependency check found that installed package is not suitable + # OR existing binary package doesn't satisfy wanted options + # + $cmd1 = undef; + if ($uncond || !-f $bpkg || $_->{REBUILD} || + !target_suitable(binary_target($_, $bpkg),$with)) { + + $opt = make_defines($_->{OPTIONS}, $with); + + # + # someone preferred a binary from the repository + # just copy it to the local store + # + if (defined $_->{platform}) { + $cmd1 = "$CURL -q -s -o $bpkg $spkg"; + } else { + $cmd1 = "$RPM_NPRIV$opt --rebuild $spkg"; + } + } + + # + # if package exist force rpm to copy over new files + # better than erasing everything and losing configuration + # files + # + $opt = $_->{REBUILD} ? ' --force' : ''; + $cmd2 = "$RPM_PRIV$opt -Uvh $bpkg"; + + if ($ignore) { + $cmd2 = "$cmd1 && \\\n$cmd2" if defined $cmd1; + } else { + if (defined $cmd1) { + $cmd2 = "$cmd1 || exit \$?\n$cmd2 || exit \$?" + } else { + $cmd2 = "$cmd2 || exit \$?"; + } + } + print "echo $mark $spkg $mark\n$cmd2\necho $mark $spkg = \$? $mark\n"; + } +} + +sub print_list2 ($$) { + my($list,$c) = @_; + my($pkg); + + foreach (@$list) { + $pkg = "$_->{name}-$_->{version}-$_->{release}"; + print "$RPM_PRIV -e $pkg\n"; + } +} + +####################################################################### + +my($config,$url,$repository,$installed,$list,$bonly); +my($pattern,%with,%exclude); + +if ($opt_a) { + $pattern = undef; +} else { + $pattern = join(' ', @ARGV); +} + +if ($opt_A) { + $pattern = '*'; +} + +%with = map { + /([^\s=]+)(?:\=(\S+))?/ + ? ($1 => (defined $2 ? $2 : 'yes')) + : () + } split(/\s+/, $opt_D); +%exclude = map { $_ => 1 } split(/\s+/, $opt_E); + +$config = get_config(); + +if (defined $opt_p) { + $config->{platform} = $opt_p; +} + +if (defined $opt_r) { + $url = $opt_r; + $url .= '/' unless $url =~ /\/$/; +} else { + $url = get_release(); +} + +# +# if we read the index from a file we can no longer deduce +# repository paths from index paths. For now lets assume +# that everything is below SRC/ to be compatible with +# existing file indexes. +# +if (defined $opt_f && !defined $opt_r) { + $url .= 'SRC/'; +} + +$installed = $opt_Z ? {} : get_installed(); +$repository = get_index($url.'00INDEX.rdf',$opt_f,\%with); + +($list,$bonly) = build_list($pattern, { + config => $config, + installed => $installed, + repository => $repository, + built => {}, + revdep => undef, + with => \%with, + exclude => \%exclude, + upgrade => ($opt_a || $opt_U), + zero => ($opt_z || $opt_Z), + quick => $opt_q, + sourceonly => ( + $opt_u || + $opt_U || + $opt_z || + $opt_Z || + scalar(%with) > 0 ) + }); + +die "FATAL: cannot find package\n" unless defined $list; + +print_list1($list,$config,$opt_a || $opt_u || $opt_U,\%with,$opt_i); +print_list2($bonly,$config); + diff --git a/openpkg-tool/openpkg-index.pl b/openpkg-tool/openpkg-index.pl new file mode 100644 index 0000000000..9563f541d4 --- /dev/null +++ b/openpkg-tool/openpkg-index.pl @@ -0,0 +1,866 @@ +## +## openpkg-index -- create index from spec files +## +## Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH +## Copyright (c) 2000-2002 The OpenPKG Project +## Copyright (c) 2000-2002 Ralf S. Engelschall +## +## Permission to use, copy, modify, and distribute this software for +## any purpose with or without fee is hereby granted, provided that +## the above copyright notice and this permission notice appear in all +## copies. +## +## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +## SUCH DAMAGE. +## + +require 5; + +use strict; + +use Getopt::Std; +getopts('r:p:C:o:ci'); +use vars qw/$opt_r $opt_p $opt_C $opt_o $opt_c $opt_i/; + +use FileHandle; +use DirHandle; + +my $RPM = 'rpm'; +my $R2C = 'rpm2cpio'; +my $BZ = 'bzip2 -9'; + +######################################################################### + +# +# escape XML special characters for output in RDF file +# +# remove trailing whitespace +# remove common leading whitespace +# +sub e ($) { + my($s) = @_; + my($i); + + $s =~ s/\n+$//sg; + $s =~ s/\s+$//mg; + + $i = undef; + while ($s =~ /^(\s+)/mg) { + $i = $1 if !defined $i || length($1) < length($i); + } + + $s =~ s/^\Q$i\E//mg if defined $i; + $s =~ s/&/&/sg; + $s =~ s//>/sg; + + return $s; +} + +sub commasep ($$) { + my($k,$v) = @_; + + if ($k =~ /^(PreReq|BuildPreReq|Provides|Conflicts)$/) { + return split(/\s*,\s*/, $v); + } + + return $v; +} + +sub vsub ($$) { + my($var,$v) = @_; + + $v =~ s/\%\{([^}]+)\}/exists $var->{$1} ? $var->{$1} : '%{'.$1.'}'/emg; + + return $v; +} + +sub upn ($) { + my($t) = @_; + my(@tok) = $t =~ /(\(|\)|\&\&|\|\||\!|\S+)/g; + my(@out,$op,$o); + my(@save); + + $op = []; + foreach (@tok) { + if ($_ eq '(') { + push @save, $op; + $op = []; + } elsif ($_ eq ')') { + die "FATAL: unresolved operators in: @tok\n" if @$op; + $op = pop @save + or die "FATAL: parenthesis stack underflow in: @tok\n"; + while ($o = pop @$op) { + push @out, $o->[0]; + last if $o->[1]; + } + } elsif ($_ eq '&&') { + push @$op, [ '+', 1 ] ; + } elsif ($_ eq '||') { + push @$op, [ '|', 1 ] ; + } elsif ($_ eq '!') { + push @$op, [ '!', 0 ]; + } elsif (/^\%\{(\S*?)\}$/) { + push @out, $1; + while ($o = pop @$op) { + push @out, $o->[0]; + last if $o->[1]; # binop + } + } + } + + return join (' ',@out); +} + +# +# deduce external variables from description +# +sub find_options ($) { + my($descr) = @_; + my(%evar); + + %evar = map { + $1 => '%{'.$1.'}' + } $descr =~ /--define\s*'(\S+)\s*\%\{\1\}'/; + + return \%evar; +} + +# +# translate default section from spec-file +# into a hash +# %if/%ifdef/%define... are translated to #if/#ifdef/#define +# +# #defines are interpolated (correct ?) +# +# #if/#ifdef/... sections are stripped +# result is the same as if all conditions evaluate false (!) +# +# all attributes are of the form key: value +# repeated attributes are coalesced into a list +# +sub package2data ($$) { + my($s,$evar) = @_; + my(%var); + my(@term, $term); + my(%attr); + my($l, $v, $cond, $d, $p); + my($re,@defs); + + # combine multilines + $s =~ s/\\\n/ /sg; + + # + # map conditional variable macros + # + $s =~ s/^#\{\!\?([^:]*):\s*%(.*?)\s*\}\s*$/#ifndef $1\n#$2\n#endif/mg; + $s =~ s/^#\{\!\?([^:]*):\s*(.*?)\s*\}\s*$/#ifndef $1\n$2\n#endif/mg; + + # + # guess more external parameters by scanning for "default" sections. + # + $re = '^\#ifndef\s+[\w\_]+\s*\n((?:\#define\s+[\w\_]+\s.*\n)+)\#endif\n'; + @defs = $s =~ /$re/gm; + foreach (@defs) { + while (/^\#define\s+([\w\_]+)\s(.*?)\s*$/mg) { + $evar->{$1} = '%{'.$1.'}'; + } + } + $s =~ s/$re//gm; + + # + # add everything looking like a with_ variable + # + $re = '%{(with\_[\w\_]+)}'; + @defs = $s =~ /$re/gm; + foreach (@defs) { + $evar->{$1} = '%{'.$1.'}'; + } + + + # + # extract all conditional sections + # + @term = (); + %var = (); + $cond = ''; + foreach $l (split(/\n/, $s)) { + $v = vsub(\%var,$l); + + if (($p) = $v =~ /^\#if\s+(.*?)\s*$/) { + # + # normalize #if expressions + # "%{variable}" == "yes" + # "%{variable}" == "no" + # operators ! && || + # + $term = ''; + while ($p =~ /(!=)|(\!|\|\||\&\&|\(|\))|"\%\{([^}]+)\}"\s*==\s*"(yes|no)"|(\S+)/g) { + if (defined $1) { + warn "WARNING: unknown token '$1':\n< $l\n> $v\n"; + } elsif (defined $5) { + warn "WARNING: unknown token '$5':\n< $l\n> $v\n"; + } elsif (defined $2) { + $term .= " $2 "; + } elsif (exists $evar->{$3}) { + $term .= ($4 eq 'no' ? '! ' : '').vsub($evar,'%{'.$3.'}'); + } else { + warn "WARNING: unknown conditional '$2':\n< $l\n> $v\n"; + } + } + + # + # join with previous conditions for this #if/#endif block + # + if ($term ne '') { + push @term, "( $term )"; + $cond = join(' && ', grep { $_ ne '' } @term).''; + } else { + push @term, ''; + } + } elsif ($v =~ /^\#else\s*$/) { + # + # reverse last condition + # + if (@term) { + $term[-1] = ' ! '.$term[-1]; + $cond = join(' && ', grep { $_ ne '' } @term).''; + } else { + die "FATAL: else without if\n"; + } + } elsif ($v =~ /^\#endif\s*$/) { + # + # unwind last #if expression + # + pop @term; + $cond = join(' && ', grep { $_ ne '' } @term).''; + + } elsif ($v =~ /^\#(?:define)\s*(\S+)\s*(.*?)\s*$/) { + + # + # define conditional variables + # truth-value becomes current condition + # + # define internal variables + # -> store for subsequent substitution + # + if (exists $evar->{$1}) { + if ($2 eq 'yes') { + $evar->{$1} = "( \%\{$1\} || ( $cond ) )"; + } elsif ($2 eq 'no') { + $evar->{$1} = "( %\{$1\} && ! ( $cond ) )"; + } else { + warn "WARNING: logic too complex for '$1':\n< $l\n> $v\n"; + } + } else { + $var{$1} = $2; + } + } elsif ($v =~ /^\s*([^\#]\S*)\s*:\s*(.*?)\s*$/) { + + # + # store attribute=value for current condition + # + push @{$attr{$1}->{$cond}}, commasep($1,$2); + } + } + + return \%attr; +} + +# +# split spec file into sections starting with a %word +# +# concatenate extended lines +# strip comment lines +# map %command to #command +# split sections +# +# return package2data from default section. +# +sub spec2data ($) { + my($s) = @_; + my(%map); + my($a,$o); + + # remove comments + $s =~ s/^\s*#.*?\n//mg; + + # map commands + $s =~ s/^%(ifdef|ifndef|if|define|else|endif|\{)/#$1/mg; + + # split sections + foreach (split(/^(?=%\w+\s*\n)/m, $s)) { + if (/^%(\w+)\s*\n/) { + $map{$1} .= $'; + } else { + $map{'*'} .= $_; + } + } + + $o = find_options($map{'description'}); + $a = package2data($map{'*'}, $o); + if (exists $map{'description'}) { + $a->{'Description'} = { '' => [ $map{'description'} ] }; + } + + return $a; +} + +########################################################################## + +# +# start of XML file +# +sub xml_head ($$) { + my($fh,$res) = @_; + print $fh < + + +EOFEOF +} + +# +# end of XML file, corresponds with start tags +# +sub xml_foot ($) { + my($fh) = @_; + print $fh < + +EOFEOF +} + +sub n($$) { + my($a,$k) = @_; + return unless $a->{$k}; + return unless $a->{$k}->{''}; + return $a->{$k}->{''}->[0]; +} + +# +# send out $a->{$k} as text-style tag +# +sub xml_text ($$$;$) { + my($i,$a,$k,$tag) = @_; + my($out); + return "" unless exists $a->{$k}; + $tag = $k unless defined $tag; + $i = ' ' x $i; + + $out = e(n($a,$k)); + + return if $out eq ''; + + return "$i<$tag>\n$out\n$i\n"; +} + +# +# send out @{$a->{$k}} as body of an XML tag +# $k is the name of the tag unless overridden by $tag +# $i denotes the depth of indentation to form nicely +# looking files. +# +# all data from the list is flattened into a single +# body, separated by LF and escaped for XML metachars. +# +sub xml_tag ($$$;$) { + my($i,$a,$k,$tag) = @_; + my($out,$cond,$upn); + return "" unless exists $a->{$k}; + $tag = $k unless defined $tag; + $out = ''; + $i = ' ' x $i; + + foreach $cond (sort keys %{$a->{$k}}) { + $upn = e(upn($cond)); + $out .= $i. + ($cond ne '' ? "<$tag cond=\"$upn\">" : "<$tag>"). + join("\n", map { e($_) } @{$a->{$k}->{$cond}}). + "\n"; + } + + return $out; +} + +# +# send out @{$a->{$k}} as a rdf:bag +# $k is the name of the outer tag unless overriden by $tag +# $i denotes the depth of indentation, inner tags are indented +# 2 or 4 more character positions. +# +sub xml_bag ($$$;$) { + my($i,$a,$k,$tag) = @_; + my($out,$cond,$upn); + return "" unless exists $a->{$k}; + $tag = $k unless defined $tag; + $out = ''; + $i = ' ' x $i; + + foreach $cond (sort keys %{$a->{$k}}) { + $upn = e(upn($cond)); + $out .= $i. + ($cond ne '' ? "<$tag cond=\"$upn\">\n" : "<$tag>\n"). + "$i \n". + join("", + map { "$i ".e($_)."\n" } + @{$a->{$k}->{$cond}}). + "$i \n". + "$i\n"; + } + + return $out; +} + +# +# send out reference to another RDF +# +sub xml_reference ($$$) { + my($fh, $res, $href) = @_; + + print $fh < +EOFEOF +} + +# +# translate attributes from %$a as generated by package2data +# into XML and write to file $fh +# +sub xml_record ($$$) { + my($fh, $a, $href) = @_; + my($maj,$min,$rel,$about); + + $about = + n($a,'Name').'-'. + n($a,'Version').'-'. + n($a,'Release'); + + unless (defined $href) { + + # guess location from Information in Specfile + + $href = "$about.src.rpm"; + ($maj,$min,$rel) = n($a,'Release') =~ /^(\d+)\.(\d+)\.(\d+)/; + + if (defined $min) { + if ($maj > 1 || ($maj == 1 && $min > 0)) { + # 1.1 or later + if (n($a,'Distribution') =~ /\[PLUS\]/) { + $href = 'PLUS/'.$href; + } + } + if ($maj > 1 || ($maj == 1 && $min >= 0)) { + # 1.0 or later + if ($rel > 0) { + $href = 'UPD/'.$href; + } + } + } else { + # current + } + + } + + print $fh < +EOFEOF + + # fake Source attribute from Source\d attribtutes + # XXX only default conditional + $a->{'Source'} = { '' => [ + map { + s/\Q%{name}\E/n($a,'Name')/esg; + s/\Q%{version}\E/n($a,'Version')/esg; + s/\Q%{release}\E/n($a,'Release')/esg; + s/.*\///; + $_; + } + map { + $a->{$_}->{''} ? @{$a->{$_}->{''}} : () + } + sort { + my($x) = $a =~ /^(\d*)$/; + my($y) = $b =~ /^(\d*)$/; + return $x <=> $y; + } + grep { + /^Source\d*$/ + } keys %$a + ]}; + delete $a->{'Source'} unless @{$a->{'Source'}->{''}}; + + print $fh + xml_tag(6, $a, 'Name'), + xml_tag(6, $a, 'Version'), + xml_tag(6, $a, 'Release'), + xml_tag(6, $a, 'Distribution'), + xml_tag(6, $a, 'Group'), + xml_tag(6, $a, 'License'), + xml_tag(6, $a, 'Packager'), + xml_tag(6, $a, 'Summary'), + xml_tag(6, $a, 'URL'), + xml_tag(6, $a, 'Vendor'), + xml_tag(6, $a, 'SourceRPM'), + xml_tag(6, $a, 'Arch'), + xml_tag(6, $a, 'Os'), + xml_tag(6, $a, 'BuildRoot'), + xml_tag(6, $a, 'BuildHost'), + xml_tag(6, $a, 'BuildSystem'), + xml_tag(6, $a, 'BuildTime'), + xml_tag(6, $a, 'Relocations'), + xml_tag(6, $a, 'Size'), + xml_tag(6, $a, 'Prefixes'), + xml_tag(6, $a, 'Platform'), + xml_tag(6, $a, 'SigSize'), + xml_tag(6, $a, 'SigMD5'), + xml_tag(6, $a, 'SigPGP'), + xml_tag(6, $a, 'SigGPG'), + xml_bag(6, $a, 'BuildPreReq'), + xml_bag(6, $a, 'PreReq'), + xml_bag(6, $a, 'Provides'), + xml_bag(6, $a, 'Conflicts'), + xml_bag(6, $a, 'Source'), + xml_bag(6, $a, 'Filenames'), + xml_text(6, $a, 'Description'); + + print $fh < +EOFEOF +} + +##################################################################### + +sub rpm2spec ($) { + my($fn) = @_; + my($pipe) = new FileHandle "$R2C '$fn' |" + or die "FATAL: cannot read '$fn' ($!)\n"; + my($buf,@hdr,$n,$m,$name,$step); + my($spec); + + while (read($pipe,$buf,110) == 110) { + @hdr = unpack('a6a8a8a8a8a8a8a8a8a8a8a8a8a8',$buf); + $n = hex($hdr[12]); # filename length + $m = int(($n+5)/4)*4-2; # filename size (padded) + last unless read($pipe,$buf,$m) == $m; + $name = substr($buf,0,$n-1); + $n = hex($hdr[7]); # file length + $m = int(($n+3)/4)*4; # file size (padded) + if ($name !~ /.spec$/) { + while ($m > 0) { + $step = $m > 8192 ? 8192 : $m; + last unless read($pipe,$buf,$step); + $m -= length($buf); + } + } else { + if (read($pipe,$buf,$n) == $n) { + $spec = $buf; + } + last; + } + } + $pipe->close; + + return $spec; +} + +##################################################################### + +sub rpm2data ($$) { + my($fn,$platform) = @_; + my($q,$pipe,%a); + my($t,$v); + + $q = <) { + if (/^(\S+)\s+(.*?)\s*$/) { + $t = $1; + $v = $2; + } elsif (/^(\s+.+?)\s*$/) { + next unless defined $t; + $v = $1; + } else { + $t = undef; + next; + } + + if (exists $a{$t}) { + $a{$t} .= "\n$v"; + } else { + $a{$t} = $v; + } + } + $pipe->close; + + %a = map { $_ => $a{$_} } + grep { $a{$_} ne '(none)' } + keys %a; + if ($a{'Relocations'} eq '(non relocatable)') { + delete $a{'Relocations'}; + } + if ($a{'SigMD5'} eq '(unknown type)') { + delete $a{'SigMD5'}; + } + $a{'Platform'} = "$a{'Arch'}-$platform-$a{'Os'}"; + $a{'PreReq'} =~ s/^rpmlib\(.*$//mg; + $a{'Description'} = [ $a{'Description'} ]; + + return { map { + $_ => { '' => (ref $a{$_} ? $a{$_} : [ split(/\n+/, $a{$_}) ]) } + } keys %a }; +} + +##################################################################### + +sub getindex ($) { + my($dir) = @_; + my(@idx) = sort { -M $a <=> -M $b; } + grep { -f $_ } + ( <$dir/00INDEX.rdf>, <$dir/00INDEX.rdf.*> ); + + return unless @idx; + return $idx[0]; +} + +sub list_specdir ($) { + my($dir) = @_; + my($dh,$d,$path); + my(@list); + + $dh = new DirHandle($dir); + while ($d = $dh->read) { + next if $d =~ /^\./; + $path = "$dir/$d/$d.spec"; + push @list, $path if -f $path; + } + + return \@list; +} + +sub list_rpmdir ($) { + my($dir) = @_; + my($dh,$d,$path); + my(@list,$idx,$sub); + + $dh = new DirHandle($dir); + while ($d = $dh->read) { + next if $d =~ /^\./; + $path = "$dir/$d"; + if (-d $path) { + $idx = getindex($path); + if (defined $idx) { + push @list, $idx; + } else { + $sub = list_rpmdir($path); + push @list, @$sub; + undef $sub; + } + } else { + next unless $d =~ /\.rpm$/ && -f $path; + push @list, $path; + } + } + + return \@list; +} + +##################################################################### + +sub readfile ($) { + my($fn) = @_; + my($fh) = new FileHandle "< $fn" + or die "FATAL: cannot read '$fn' ($!)\n"; + my(@l) = <$fh>; + $fh->close; + return join('',@l); +} + +sub relpath ($$) { + my($prefix,$path) = @_; + $path =~ s/^\Q$prefix\E\///s; + return $path; +} + +sub dirname ($) { + my($path) = @_; + $path =~ s/\/[^\/]*$//s; + return $path.'/'; +} + +sub getresource ($) { + my($fn) = @_; + my($fh, $buf); + + if ($fn =~ /\.bz2$/) { + $fh = new FileHandle "$BZ -dc $fn |" + or die "FATAL: cannot read '$fn' ($!)\n"; + } else { + $fh = new FileHandle "< $fn" + or die "FATAL: cannot read '$fn' ($!)\n"; + } + $fh->read($buf, 1024); + $fh->close; + + if ($buf =~ /{"M$_"} && + $cache->{"M$_"} == $mtime) { + $spec = $cache->{"S$_"}; + } else { + $spec = rpm2spec($_); + $cache->{"S$_"} = $spec; + $cache->{"M$_"} = $mtime; + } + } else { + $spec = rpm2spec($_); + } + $a = spec2data($spec); + } elsif (/([^\/]+\.rpm)$/) { + $h = relpath($prefix, $_); + $a = rpm2data($_, $platform); + } elsif (/([^\/]+\.rdf[^\/]*)$/) { + $h = relpath($prefix, $_); + $r = getresource($_) || $resource.dirname($h); + } + + if ($a) { + xml_record($fh, $a, $h); + } elsif ($r) { + xml_reference($fh, $r, $h); + } else { + warn "ERROR: cannot process $_\n"; + } + } +} + +##################################################################### + +my($prefix,$list,$fh,%cache,$tmpo); + +if ($#ARGV < 0) { + print "openpkg:index:USAGE: $0 [-r resource] [-p platform] [-C cache.db] [-o index.rdf] [-c] [-i] dir ...\n"; + die "\n"; +} + +if ($opt_C) { + require DB_File; + tie %cache, 'DB_File', $opt_C, O_CREAT|O_RDWR, 0666, $DB_File::DB_HASH + or die "FATAL: cannot tie cache '$opt_C' ($!)\n"; +} + +$opt_r = 'OpenPKG-CURRENT/Source/' unless defined $opt_r; +$opt_p = 'unknown' unless defined $opt_p; + +if (defined $opt_o) { + $tmpo = $opt_o . '.tmp'; + if ($opt_c) { + $fh = new FileHandle "| $BZ -c > '$tmpo'" + or die "FATAL: cannot write '$tmpo' ($!)\n"; + } else { + $fh = new FileHandle "> $tmpo" + or die "FATAL: cannot write '$tmpo' ($!)\n"; + } +} else { + if ($opt_c) { + $fh = new FileHandle "| $BZ -c" + or die "FATAL: cannot write to stdout ($!)\n"; + } else { + $fh = new FileHandle ">&=1" + or die "FATAL: cannot write to stdout ($!)\n"; + } +} + +xml_head($fh, $opt_r); +foreach $prefix (@ARGV) { + die "FATAL: $prefix is not a directory\n" unless -d $prefix; + if ($opt_i) { + $list = list_rpmdir($prefix); + } else { + $list = list_specdir($prefix); + } + write_index($fh, $prefix, $opt_r, $opt_p, $list, $opt_C ? \%cache : undef); +} +xml_foot($fh); + +$fh->close + or die "FATAL: write error on output ($!)\n"; + +if (defined $tmpo) { + rename $tmpo,$opt_o + or die "FATAL: cannot rename $tmpo to $opt_o ($!)\n"; +} + diff --git a/openpkg-tool/openpkg-tool.spec b/openpkg-tool/openpkg-tool.spec new file mode 100644 index 0000000000..a4cdb7a185 --- /dev/null +++ b/openpkg-tool/openpkg-tool.spec @@ -0,0 +1,89 @@ +## +## openpkg-tool.spec -- OpenPKG RPM Specification +## Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH +## Copyright (c) 2000-2002 The OpenPKG Project +## Copyright (c) 2000-2002 Ralf S. Engelschall +## +## Permission to use, copy, modify, and distribute this software for +## any purpose with or without fee is hereby granted, provided that +## the above copyright notice and this permission notice appear in all +## copies. +## +## THIS SOFTWARE IS PROVIDED AS IS'' AND ANY EXPRESSED OR IMPLIED +## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +## SUCH DAMAGE. +## + +# package information +Name: openpkg-tool +Summary: OpenPKG Tool +URL: http://www.openpkg.org/ +Vendor: The OpenPKG Project +Packager: The OpenPKG Project +Distribution: OpenPKG [EVAL] +Group: Bootstrapping +License: GPL +Version: 20021126 +Release: 20021126 + +# list of sources +Source0: openpkg.sh +Source1: openpkg-index.pl +Source2: openpkg-build.pl +Source3: openpkg.pod +Source4: openpkg.1 +Source5: Makefile + +# build information +Prefix: %{l_prefix} +BuildRoot: %{l_buildroot} +BuildPreReq: OpenPKG, openpkg >= 20020206 +PreReq: OpenPKG, openpkg >= 20020206 +AutoReq: no +AutoReqProv: no + +%description + The OpenPKG tool is a helper utility for managing an OpenPKG instance. + +%prep + +%build + +%install + rm -rf $RPM_BUILD_ROOT + %{l_shtool} mkdir -f -p -m 755 \ + $RPM_BUILD_ROOT%{l_prefix}/bin \ + $RPM_BUILD_ROOT%{l_prefix}/lib/openpkg \ + $RPM_BUILD_ROOT%{l_prefix}/man/man1 + %{l_shtool} install -c -m 755 \ + -e 's;@l_prefix@;%{l_prefix};g' \ + -e 's;@version@;%{version};g' \ + %{SOURCE openpkg.sh} $RPM_BUILD_ROOT%{l_prefix}/bin/openpkg + %{l_shtool} install -c -m 644 \ + -e 's;@l_prefix@;%{l_prefix};g' \ + -e 's;@version@;%{version};g' \ + %{SOURCE openpkg.1} $RPM_BUILD_ROOT%{l_prefix}/man/man1/ + %{l_shtool} install -c -m 755 \ + %{SOURCE openpkg-index.pl} \ + $RPM_BUILD_ROOT%{l_prefix}/lib/openpkg/ + %{l_shtool} install -c -m 755 \ + %{SOURCE openpkg-build.pl} \ + $RPM_BUILD_ROOT%{l_prefix}/lib/openpkg/ + %{l_rpmtool} files -v -ofiles -r$RPM_BUILD_ROOT \ + %{l_files_std} \ + '%not %dir %{l_prefix}/lib/openpkg' + +%files -f files + +%clean + rm -rf $RPM_BUILD_ROOT + diff --git a/openpkg-tool/openpkg.1 b/openpkg-tool/openpkg.1 new file mode 100644 index 0000000000..c153c63cdc --- /dev/null +++ b/openpkg-tool/openpkg.1 @@ -0,0 +1,327 @@ +.\" Automatically generated by Pod::Man v1.34, Pod::Parser v1.13 +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sh \" Subsection heading +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. | will give a +.\" real vertical bar. \*(C+ will give a nicer C++. Capital omega is used to +.\" do unbreakable dashes and therefore won't be available. \*(C` and \*(C' +.\" expand to `' in nroff, nothing in troff, for use with C<>. +.tr \(*W-|\(bv\*(Tr +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` +. ds C' +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.\" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.hy 0 +.if n .na +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "OPENPKG 1" +.TH OPENPKG 1 "2002-11-26" "openpkg-tool" "OpenPKG Maintainance" +.SH "NAME" +\&\fBopenpkg\fR \- \fBOpenPKG\fR maintainance utility +.SH "VERSION" +.IX Header "VERSION" +openpkg-tool \f(CW@version\fR@ +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +\&\fBopenpkg\fR +\&\fBindex\fR +[\fB\-r\fR \fIresource\fR] +[\fB\-p\fR \fIplatform\fR] +[\fB\-C\fR \fIcache.db\fR] +[\fB\-o\fR \fIindex.rdf\fR] +[\fB\-c\fR] +[\fB\-i\fR] +\&\fIdir\fR ... +.PP +\&\fBopenpkg\fR +\&\fBbuild\fR +[\fB\-R\fR \fIrpm\fR] +[\fB\-r\fR \fIrepository\fR] +[\fB\-f\fR \fIindex.rdf\fR] +[\fB\-u\fR] +[\fB\-U\fR] +[\fB\-z\fR] +[\fB\-Z\fR] +[\fB\-i\fR] +[\fB\-q\fR] +[\fB\-P\fR \fIpriv-cmd\fR] +[\fB\-N\fR \fInon-priv-cmd\fR] +[\fB\-p\fR \fIplatform\fR] +[\fB\-D\fR\fIvar\fR=\fIval\fR ...] +[\fB\-E\fR \fIname\fR ...] +([\fB\-a\fR] [\fB\-A\fR] | \fIpatternlist\fR) +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +\&\fBopenpkg\fR is a frontend utility for maintaining an \fBOpenPKG\fR instance. +It currenty provides indexing of \s-1RPM\s0 files (\fBopenpkg index\fR) and +automated recursive from-scratch installation and updating of existing +\&\s-1RPM\s0 packages (\fBopenpkg build\fR). +.SH "COMMANDS" +.IX Header "COMMANDS" +.Sh "\s-1OPENPKG\s0 \s-1INDEX\s0" +.IX Subsection "OPENPKG INDEX" +\&\fBopenpkg-index\fR creates an \s-1XML/RDF\s0 based resource index for \s-1RPM\s0 +\&\fI.spec\fR files in a source tree or from an \s-1RPM\s0 package repository. The +index holds enough information to support an automated build process by +\&\fBopenpkg build\fR. +.PP +The following command line options exist: +.IP "\fB\-r\fR \fIresource\fR" 4 +.IX Item "-r resource" +The name of the resource stored in the index. The default is +"\f(CW\*(C`OpenPKG\-CURRENT/Source/\*(C'\fR". +.IP "\fB\-p\fR \fIplatform\fR" 4 +.IX Item "-p platform" +\&\fBopenpkg index\fR adds a platform attribute for binary RPMs. The +attribute is built as \fI%{arch}\fR\f(CW\*(C`\-\*(C'\fR\fIplatform\fR\f(CW\*(C`\-\*(C'\fR\fI%{os}\fR where +\&\fI%{arch}\fR and \fI%{os}\fR are taken from the \s-1RPM\s0 header and \fIplatform\fR is +the value of the \fB\-p\fR option. The default value is "\f(CW\*(C`unkown\*(C'\fR". This +must be used to distinguish between platforms that support the same +Architecture and \s-1OS\s0 name like various Linux distributions. +.IP "\fB\-C\fR \fIcache.db\fR" 4 +.IX Item "-C cache.db" +Cache all \fI.spec\fR files into this Berkeley-DB file when indexing source +RPMs. The cache is refreshed automatically when the source RPMs are more +recent than the cache entry. +.IP "\fB\-o\fR \fIindex.rdf\fR" 4 +.IX Item "-o index.rdf" +Name of the output \s-1XML/RDF\s0 file, default is to write to \fIstdout\fR. +.IP "\fB\-c\fR" 4 +.IX Item "-c" +Compress output with \f(CW\*(C`bzip2\*(C'\fR. Use the \fB\-o\fR option to specify a \fI.bz2\fR +suffix. +.IP "\fB\-i\fR" 4 +.IX Item "-i" +The specified directories are \s-1RPM\s0 repositories. Build index over +all \fI.rpm\fR files in these directories and all subdirectories. +If a subdirectory already contains a \f(CW\*(C`00INDEX.rdf\*(C'\fR or \f(CW\*(C`00INDEX.rdf.*\*(C'\fR +file then skip scanning the subdirectory, instead add a reference +to the index file into the new index. +.Sp +Without this option the directories are source trees with a subdirectory +per package and a \fIpackage\fR\f(CW\*(C`.spec\*(C'\fR file inside each subdirectory. +.Sh "\s-1OPENPKG\s0 \s-1BUILD\s0" +.IX Subsection "OPENPKG BUILD" +\&\fBopenpkg build\fR writes a shell script to standard output that installs +or upgrades software packages including all dependencies. Packages that +are upgraded automatically trigger rebuilds of all packages that depend +on the upgraded package (\*(L"reverse dependencies\*(R"). The dependency +information is read from an index generated by \fBopenpkg index\fR. +.PP +The following command line options exist: +.IP "\fB\-R\fR \fIrpm\fR" 4 +.IX Item "-R rpm" +Specify a path to the installed \fBOpenPKG\fR \f(CW\*(C`rpm\*(C'\fR executable. Several +other internal paths are deduced from the \fIrpm\fR path, so this should be +something like \fI%{l_prefix}\fR\f(CW\*(C`/bin/rpm\*(C'\fR. +.IP "\fB\-r\fR \fIrepository\fR" 4 +.IX Item "-r repository" +Specify a path to an \s-1RPM\s0 repository, this can be a \s-1URL\s0 or a directory +path. The name of the package file is appended to this path. +The default is to use a \s-1URL\s0 pointing to the \fBOpenPKG\fR \s-1FTP\s0 server. +.IP "\fB\-f\fR \fIindex.rdf\fR" 4 +.IX Item "-f index.rdf" +Specify a path to the primary \s-1XML/RDF\s0 index, this can be a \s-1URL\s0 or a +file path. If the index contains references to aother indexes these are +included automatically. The default is to use a \s-1URL\s0 pointing to the +\&\fBOpenPKG\fR \s-1FTP\s0 server for the \fBOpenPKG\fR release you are using. +.IP "\fB\-u\fR" 4 +.IX Item "-u" +The generated script will ignore binary RPMs that are stored on +your system. Instead it will either fetch binary RPMs or rebuild +from source RPMs fetched from the repository. +.IP "\fB\-U\fR" 4 +.IX Item "-U" +The generated script will try to upgrade all selected packages +including their dependencies to the most recent version. +.IP "\fB\-z\fR" 4 +.IX Item "-z" +The generated script will rebuild all selected packages +including their dependencies even when the most recent version +is already installed. +.IP "\fB\-Z\fR" 4 +.IX Item "-Z" +\&\fBopenpkg build\fR ignores a installed packages, the +script will rebuild all selected packages from scratch. +Note that this doesn't work together with the \fB\-a\fR option. +.IP "\fB\-i\fR" 4 +.IX Item "-i" +The generated script will ignore errors. However, if a build +phase fails the install phase is still skipped. +.IP "\fB\-q\fR" 4 +.IX Item "-q" +Ignore all reverse dependencies. +\&\fI\s-1ATTENTION:\s0 this might break already installed packages!\fR +.IP "\fB\-P\fR \fIpriv-cmd\fR" 4 +.IX Item "-P priv-cmd" +Command prefix to use for install commands that require elevated +privileges. The most common tool for this is \fIsudo\fR\|(1). +.IP "\fB\-N\fR \fInon-priv-cmd\fR" 4 +.IX Item "-N non-priv-cmd" +Command prefix to use for install commands that do not require elevated +privileges. The most common tool for this is \fIsudo\fR\|(1). +.IP "\fB\-p\fR \fIplatform\fR" 4 +.IX Item "-p platform" +The platform string that is matched against the index for binary +packages. Default is to use the \fI%{_target_platform}\fR variable. +.IP "\fB\-D\fR\fIvar\fR=\fIval\fR" 4 +.IX Item "-Dvar=val" +Specify configuration options for all selected packages. This can be +either \fB\-D\fR\f(CW\*(C`with_\*(C'\fR\fIxxx\fR\f(CW\*(C`=\*(C'\fR\fIyyy\fR or just \fB\-D\fR\f(CW\*(C`with_\*(C'\fR\fIxxx\fR, the +latter is equivalent to a \fB\-D\fR\f(CW\*(C`with_\*(C'\fR\fIxxx\fR\f(CW\*(C`=\*(C'\fR\f(CW\*(C`yes\*(C'\fR. The parameters +are matched against selected packages that are already installed. If +they do indicate a change the package is rebuild. There can be multiple +\&\fB\-D\fR options. +.IP "\fB\-E\fR \fIname\fR" 4 +.IX Item "-E name" +Ignore a package with the specified \fIname\fR. This can be used to avoid +upgrading to a broken package in the repository. There can be multiple +\&\fB\-E\fR options. +.IP "\fB\-a\fR" 4 +.IX Item "-a" +Select all installed packages. Do not specify a pattern list together +with the \fB\-a\fR option. +.IP "\fB\-A\fR" 4 +.IX Item "-A" +Select all packages in the repository. Do not specify a pattern list together +with the \fB\-a\fR option. +.SH "CONFIGURATION" +.IX Header "CONFIGURATION" +\&\fBopenpkg build\fR reads the configuration file \fI$HOME/.openpkg/build\fR. +The file lists default options, one option per line and section tags +of the form \f(CW\*(C`[\*(C'\fR\fIprefix\fR\f(CW\*(C`]\*(C'\fR. Options following such a tag are only +evaluated if the selected \s-1RPM\s0 path matches the prefix so that you can +define default options for multiple \fBOpenPKG\fR hierarchies. +.SH "CAVEATS" +.IX Header "CAVEATS" +Parallel execution of \fBopenpkg build\fR causes undefined effects. +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\&\fIrpm\fR\|(1), \fIsudo\fR\|(1) +.SH "HISTORY" +.IX Header "HISTORY" +The \fBopenpkg index\fR and \fBopenpkg build\fR command +was invented in November 2002 by \fIMichael van Elst\fR + under contract with \fICable & Wireless +Germany\fR for use inside the \fBOpenPKG\fR +project . +.SH "AUTHORS" +.IX Header "AUTHORS" +.Vb 2 +\& Michael van Elst +\& mlelstv@dev.de.cw.net +.Ve diff --git a/openpkg-tool/openpkg.pod b/openpkg-tool/openpkg.pod new file mode 100644 index 0000000000..ac7fa59a89 --- /dev/null +++ b/openpkg-tool/openpkg.pod @@ -0,0 +1,265 @@ +## +## openpkg.pod -- OpenPKG maintainance utility (frontend manual page) +## +## Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH +## Copyright (c) 2000-2002 The OpenPKG Project +## Copyright (c) 2000-2002 Ralf S. Engelschall +## +## Permission to use, copy, modify, and distribute this software for +## any purpose with or without fee is hereby granted, provided that +## the above copyright notice and this permission notice appear in all +## copies. +## +## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +## SUCH DAMAGE. +## + +=pod + +=head1 NAME + +B - B maintainance utility + +=head1 VERSION + +openpkg-tool @version@ + +=head1 SYNOPSIS + +B +B +[B<-r> I] +[B<-p> I] +[B<-C> I] +[B<-o> I] +[B<-c>] +[B<-i>] +I ... + +B +B +[B<-R> I] +[B<-r> I] +[B<-f> I] +[B<-u>] +[B<-U>] +[B<-z>] +[B<-Z>] +[B<-i>] +[B<-q>] +[B<-P> I] +[B<-N> I] +[B<-p> I] +[B<-D>I=I ...] +[B<-E> I ...] +([B<-a>] [B<-A>] | I) + +=head1 DESCRIPTION + +B is a frontend utility for maintaining an B instance. +It currenty provides indexing of RPM files (B) and +automated recursive from-scratch installation and updating of existing +RPM packages (B). + +=head1 COMMANDS + +=head2 OPENPKG INDEX + +B creates an XML/RDF based resource index for RPM +F<.spec> files in a source tree or from an RPM package repository. The +index holds enough information to support an automated build process by +B. + +The following command line options exist: + +=over 4 + +=item B<-r> I + +The name of the resource stored in the index. The default is +"C". + +=item B<-p> I + +B adds a platform attribute for binary RPMs. The +attribute is built as I<%{arch}>C<->IC<->I<%{os}> where +I<%{arch}> and I<%{os}> are taken from the RPM header and I is +the value of the B<-p> option. The default value is "C". This +must be used to distinguish between platforms that support the same +Architecture and OS name like various Linux distributions. + +=item B<-C> I + +Cache all F<.spec> files into this Berkeley-DB file when indexing source +RPMs. The cache is refreshed automatically when the source RPMs are more +recent than the cache entry. + +=item B<-o> I + +Name of the output XML/RDF file, default is to write to F. + +=item B<-c> + +Compress output with C. Use the B<-o> option to specify a F<.bz2> +suffix. + +=item B<-i> + +The specified directories are RPM repositories. Build index over +all F<.rpm> files in these directories and all subdirectories. +If a subdirectory already contains a C<00INDEX.rdf> or C<00INDEX.rdf.*> +file then skip scanning the subdirectory, instead add a reference +to the index file into the new index. + +Without this option the directories are source trees with a subdirectory +per package and a IC<.spec> file inside each subdirectory. + +=back + +=head2 OPENPKG BUILD + +B writes a shell script to standard output that installs +or upgrades software packages including all dependencies. Packages that +are upgraded automatically trigger rebuilds of all packages that depend +on the upgraded package ("reverse dependencies"). The dependency +information is read from an index generated by B. + +The following command line options exist: + +=over 4 + +=item B<-R> I + +Specify a path to the installed B C executable. Several +other internal paths are deduced from the I path, so this should be +something like I<%{l_prefix}>C. + +=item B<-r> I + +Specify a path to an RPM repository, this can be a URL or a directory +path. The name of the package file is appended to this path. +The default is to use a URL pointing to the B FTP server. + +=item B<-f> I + +Specify a path to the primary XML/RDF index, this can be a URL or a +file path. If the index contains references to aother indexes these are +included automatically. The default is to use a URL pointing to the +B FTP server for the B release you are using. + +=item B<-u> + +The generated script will ignore binary RPMs that are stored on +your system. Instead it will either fetch binary RPMs or rebuild +from source RPMs fetched from the repository. + +=item B<-U> + +The generated script will try to upgrade all selected packages +including their dependencies to the most recent version. + +=item B<-z> + +The generated script will rebuild all selected packages +including their dependencies even when the most recent version +is already installed. + +=item B<-Z> + +B ignores a installed packages, the +script will rebuild all selected packages from scratch. +Note that this doesn't work together with the B<-a> option. + +=item B<-i> + +The generated script will ignore errors. However, if a build +phase fails the install phase is still skipped. + +=item B<-q> + +Ignore all reverse dependencies. +I + +=item B<-P> I + +Command prefix to use for install commands that require elevated +privileges. The most common tool for this is sudo(1). + +=item B<-N> I + +Command prefix to use for install commands that do not require elevated +privileges. The most common tool for this is sudo(1). + +=item B<-p> I + +The platform string that is matched against the index for binary +packages. Default is to use the I<%{_target_platform}> variable. + +=item B<-D>I=I + +Specify configuration options for all selected packages. This can be +either B<-D>CIC<=>I or just B<-D>CI, the +latter is equivalent to a B<-D>CIC<=>C. The parameters +are matched against selected packages that are already installed. If +they do indicate a change the package is rebuild. There can be multiple +B<-D> options. + +=item B<-E> I + +Ignore a package with the specified I. This can be used to avoid +upgrading to a broken package in the repository. There can be multiple +B<-E> options. + +=item B<-a> + +Select all installed packages. Do not specify a pattern list together +with the B<-a> option. + +=item B<-A> + +Select all packages in the repository. Do not specify a pattern list together +with the B<-a> option. + +=back + +=head1 CONFIGURATION + +B reads the configuration file I<$HOME/.openpkg/build>. +The file lists default options, one option per line and section tags +of the form C<[>IC<]>. Options following such a tag are only +evaluated if the selected RPM path matches the prefix so that you can +define default options for multiple B hierarchies. + +=head1 CAVEATS + +Parallel execution of B causes undefined effects. + +=head1 SEE ALSO + +rpm(1), sudo(1) + +=head1 HISTORY + +The B and B command +was invented in November 2002 by I +Emlelstv@dev.de.cw.netE under contract with I Ehttp://www.cw.com/deE for use inside the B +project Ehttp://www.openpkg.org/E. + +=head1 AUTHORS + + Michael van Elst + mlelstv@dev.de.cw.net + +=cut + diff --git a/openpkg-tool/openpkg.sh b/openpkg-tool/openpkg.sh new file mode 100644 index 0000000000..003878ec64 --- /dev/null +++ b/openpkg-tool/openpkg.sh @@ -0,0 +1,90 @@ +#!@l_prefix@/lib/openpkg/bash +## +## openpkg.sh -- OpenPKG maintainance utility (frontend) +## +## Copyright (c) 2000-2002 Cable & Wireless Deutschland GmbH +## Copyright (c) 2000-2002 The OpenPKG Project +## Copyright (c) 2000-2002 Ralf S. Engelschall +## +## Permission to use, copy, modify, and distribute this software for +## any purpose with or without fee is hereby granted, provided that +## the above copyright notice and this permission notice appear in all +## copies. +## +## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +## SUCH DAMAGE. +## + +# program information +progname="openpkg" +progvers="@version@" +l_prefix="@l_prefix@" + +# try to determine Perl interpreter +perl="" +for dir in $l_prefix/bin `echo $PATH | sed -e 's;:; ;g'` /bin /usr/bin /usr/local/bin; do + if [ -f "$dir/perl" ]; then + perl="$dir/perl" + break + fi +done + +# command line option parsing +if [ $# -eq 0 ]; then + echo "$progname:USAGE: $progname index|build [options]" + exit 0 +fi +while [ ".$1" != . ]; do + case "$1" in + -h ) + echo "$progname:USAGE: $progname index|build [options]" + exit 0 + ;; + -v ) + echo "$progname $progvers (OpenPKG instance: $l_prefix)" + exit 0 + ;; + -* ) + echo "$progname:ERROR: invalid option \"$1\"" 1>&2 + exit 1 + ;; + * ) + break + ;; + esac +done + +# command dispatching +case "$1" in + index ) + if [ ".$perl" = . ]; then + echo "$progname:ERROR: \"index\" command requires a Perl interpreter" 1>&2 + exit 1 + fi + shift + exec $perl ${l_prefix}/lib/openpkg/openpkg-index.pl ${1+"$@"} + ;; + build ) + if [ ".$perl" = . ]; then + echo "$progname:ERROR: \"build\" command requires a Perl interpreter" 1>&2 + exit 1 + fi + shift + exec $perl ${l_prefix}/lib/openpkg/openpkg-build.pl ${1+"$@"} + ;; + * ) + echo "$progname:ERROR: invalid command \"$1\"" 1>&2 + exit 1 + ;; +esac +