diff -r 71503088f51b -r f880f219c566 openpkg/makeproxy.pl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openpkg/makeproxy.pl Tue Jul 31 12:23:42 2012 +0200 @@ -0,0 +1,411 @@ +## +## makeproxy.pl -- OpenPKG Tool Chain +## Copyright (c) 2000-2012 OpenPKG GmbH +## +## This software is property of the OpenPKG GmbH, DE MUC HRB 160208. +## All rights reserved. Licenses which grant limited permission to use, +## copy, modify and distribute this software are available from the +## OpenPKG GmbH. +## +## 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.003; + +# OpenPKG instance prefix and RPM +my $my_prefix = $ENV{'OPENPKG_PREFIX'}; +my $my_rpm = "$my_prefix/bin/openpkg rpm"; +delete $ENV{'OPENPKG_PREFIX'}; + +# program identification +my $progname = "makeproxy"; +my $progvers = "2.1.0"; + +# parameters (defaults) +my $help = 0; +my $version = 0; +my $verbose = 0; +my $debug = 0; +my $slave_prefix = $my_prefix; +my $master_prefix = ''; +my $tmpdir = ($ENV{TMPDIR} || "/tmp"); +my $output = '.'; +my $input = '-'; + +# cleanup support +my @cleanup = (); +sub cleanup_remember { + my ($cmd) = @_; + push(@cleanup, $cmd); +} +sub cleanup_perform { + foreach my $cmd (reverse @cleanup) { + runcmd($cmd); + } +} + +# exception handling support +$SIG{__DIE__} = sub { + my ($err) = @_; + $err =~ s|\s+at\s+.*||s if (not $verbose); + $err =~ s/\n+$//s; + print STDERR "openpkg:$progname:ERROR: $err\n"; + cleanup_perform() if (not $verbose); + exit(1); +}; + +# verbose message printing +sub verbose { + my ($msg) = @_; + print STDERR "$msg\n" if ($verbose); +} + +# execution of external commands +sub runcmd { + my ($cmd) = @_; + if ($cmd =~ m/^(.+)\|$/s) { + print STDERR "\$ $1\n" if ($debug); + return `$1`; + } + else { + print STDERR "\$ $cmd\n" if ($debug); + $cmd = "($cmd) >/dev/null 2>&1" if (not $debug); + return (system($cmd) == 0); + } +} + +# expand into a full filesystem path +sub fullpath { + my ($prog) = @_; + my $fullprog = ''; + foreach my $path (split(/:/, $ENV{PATH})) { + if (-x "$path/$prog") { + $fullprog = "$path/$prog"; + last; + } + } + return $fullprog; +} + +# convert a subdirectory (a/b/c/) +# into a corresponding reverse path (../../../) +sub sub2rev { + my ($sub) = @_; + my $rev = ''; + $sub =~ s|^/+||s; + $sub =~ s|/+$||s; + while ($sub =~ s|/[^/]+||) { + $rev .= "../"; + } + if ($sub ne '') { + $rev .= "../"; + } + $rev =~ s|/$||s; + return $rev; +} + +# create a directory (plus its missing parent dirs) +sub mkdirp { + my ($dir) = @_; + my $pdir = $dir; + $pdir =~ s|/[^/]*$||s; + if (not -d $pdir) { + mkdirp($pdir); + } + if (not -d $dir) { + runcmd("mkdir $dir"); + } +} + +# home-brewn getopt(3) style option parser +sub getopts ($) { + my ($opts) = @_; + my (%optf) = map { m/(\w)/; $1 => $_ } $opts =~ m/(\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 "openpkg:makeproxy:WARNING: unknown option $_\n"; + } + $_ = "-$2"; + } + } + } + if (defined $optarg) { + warn "openpkg:makeproxy:WARNING: option $optarg requires an argument\n"; + } + foreach (keys %opts) { + eval '$opt_'.$_.' = "'.quotemeta($opts{$_}).'";'; + } + @ARGV = @argv; +} + +# command line parsing +getopts("Vhdvm:s:t:o:"); +$version = $opt_V if (defined $opt_V); +$help = $opt_h if (defined $opt_h); +$debug = $opt_d if (defined $opt_d); +$verbose = $opt_v if (defined $opt_v); +$master_prefix = $opt_m if (defined $opt_m); +$slave_prefix = $opt_s if (defined $opt_s); +$tmpdir = $opt_t if (defined $opt_t); +$output = $opt_o if (defined $opt_o); +if ($help) { + print "Usage: openpkg $progname [options] master-package\n" . + " -h,--help print this usage page\n" . + " -V,--version print version\n" . + " -v,--verbose print verbose run-time information\n" . + " -d,--debug print debug information\n" . + " -m,--master=DIR filesystem path to master OpenPKG instance\n" . + " -s,--slave=DIR filesystem path to slave OpenPKG instance\n" . + " -t,--tmpdir=DIR filesystem path to temporary directory\n" . + " -o,--output=FILE filesystem path to output proxy OpenPKG RPM package\n"; + exit(0); +} +if ($version) { + print "OpenPKG $progname $progvers\n"; + exit(0); +} +if ($#ARGV == 0) { + $input = shift(@ARGV); +} +if ($#ARGV != -1) { + die "invalid number of command line arguments"; +} + +# prepare temporary location +verbose("++ prepare temporary directory"); +$tmpdir = "$tmpdir/$progname.$$"; +if (not -d $tmpdir) { + runcmd("umask 077 && mkdir $tmpdir") + or die "cannot create temporary directory \"$tmpdir\""; + cleanup_remember("rm -rf $tmpdir"); +} +verbose("-- temporary directory: $tmpdir"); + +# determine input and output RPM +verbose("++ determining OpenPKG RPM package files"); +verbose("-- input/master/regular RPM package: $input"); +verbose("-- output/slave/proxy RPM package: $output"); + +# ensure input RPM package is available on the filesystem +if ($input eq '-') { + $input = "$tmpdir/input.rpm"; + runcmd("cat >$input"); +} + +# determine master and slave OpenPKG instance and RPM commands +if ($master_prefix eq '') { + if (not -f $input) { + die "input/master/regular RPM package does not exist: \"$input\""; + } + $master_prefix = runcmd("($my_rpm -qp --qf '%{PREFIXES}' $input 2>/dev/null || true) |"); + $master_prefix =~ s/\r?\n$//s; + if ($master_prefix eq '') { + die "unable to determine master OpenPKG instance"; + } +} +if ($master_prefix eq $slave_prefix) { + die "master and slave OpenPKG instances have to be different"; +} +my $slave_rpm = (-f "$slave_prefix/bin/openpkg" ? "$slave_prefix/bin/openpkg rpm" : $my_rpm); +my $master_rpm = (-f "$master_prefix/bin/openpkg" ? "$master_prefix/bin/openpkg rpm" : $my_rpm); +verbose("-- tool OpenPKG instance: $my_prefix"); +verbose("-- tool OpenPKG RPM command: $my_rpm"); +verbose("-- master OpenPKG instance: $master_prefix"); +verbose("-- master OpenPKG RPM command: $master_rpm"); +verbose("-- slave OpenPKG instance: $slave_prefix"); +verbose("-- slave OpenPKG RPM command: $slave_rpm"); + +# helper function for parsing RPM query outputs +sub parseresponse { + my ($r, $o) = @_; + sub parseline { + my ($r, $t, $k, $v) = @_; + $v =~ s|^\s+||s; + $v =~ s|\s+$||s; + if ($t eq 'S') { # single-value results + $r->{$k} = $v; + } + elsif ($t eq 'M') { # multi-value results + $r->{$k} = [] if (not defined($r->{$k})); + push(@{$r->{$k}}, $v); + } + } + $o =~ s|([SM])-([^:]+):<(.*?)>\n|parseline($r, $1, $2, $3, '')|egs; + return $r; +} + +# query master OpenPKG RPM package +verbose("++ query information from master OpenPKG RPM package"); +my $q = ''; +foreach my $t (qw( + NAME SUMMARY URL VENDOR PACKAGER DISTRIBUTION CLASS GROUP LICENSE VERSION RELEASE + DESCRIPTION +)) { + $q .= "S-$t:<%{$t}>\\n"; +} +$q .= '[M-PREREQ:<%{REQUIRENAME} %|REQUIREFLAGS?{%{REQUIREFLAGS:depflags} %{REQUIREVERSION}}:{}|>\n]'; +$q .= '[M-PROVIDES:<%{PROVIDENAME} %|PROVIDEFLAGS?{%{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}}:{}|>\n]'; +$q .= '[M-PREFIXES:<%{PREFIXES}>\n]'; +my $cmd = sprintf("%s -q %s --qf '$q' %s", $master_rpm, (-f $input ? "-p" : ""), $input); +my $o = runcmd("$cmd|"); +if ($o =~ m/package .* is not installed/s) { + die "master package \"$input\" not installed in master OpenPKG instance \"$master_prefix\""; +} +$o =~ s|M-PREREQ:\n||gs; +my $r = {}; +$r = parseresponse($r, $o); +my $BD = ''; +my $ID = ''; +my $PR = ''; +foreach my $d (@{$r->{PREREQ}}) { + if ($d =~ m|^OpenPKG|i) { + $BD .= ", " if ($BD ne ''); + $BD .= $d; + } + $ID .= ", " if ($ID ne ''); + $ID .= $d; +} +foreach my $d (@{$r->{PROVIDES}}) { + $PR .= ", " if ($PR ne ''); + $PR .= $d; +} + +# prepare build environment +verbose("++ establishing temporary OpenPKG RPM environment"); +verbose("-- directory: $tmpdir"); +runcmd("rm -rf $tmpdir/src; mkdir $tmpdir/src"); +runcmd("rm -rf $tmpdir/bld; mkdir $tmpdir/bld"); +cleanup_remember("rm -rf $tmpdir/src"); +cleanup_remember("rm -rf $tmpdir/tmp"); +cleanup_remember("rm -rf $tmpdir/bld"); +cleanup_remember("rm -rf $tmpdir/pkg"); +runcmd("mkdir $tmpdir/src/.openpkg"); +open(MACRO, ">$tmpdir/src/.openpkg/rpmmacros") + or die "unable to write file \"$tmpdir/src/.openpkg/rpmmacros\": $!"; +print MACRO "\%openpkg_layout macrosfile=\%{macrosfile} layout=local shared=no\n"; +close(MACRO); +$ENV{HOME} = $tmpdir; + +# generate OpenPKG RPM .spec file for proxy OpenPKG RPM package +verbose("++ generating OpenPKG RPM package specification for proxy package"); +verbose("-- file: $tmpdir/src/".$r->{NAME}.".spec"); +my $S = ''; +$S .= "Name: ".$r->{NAME}."\n"; +$S .= "Summary: ".$r->{SUMMARY}."\n"; +$S .= "URL: ".$r->{URL}."\n"; +$S .= "Vendor: ".$r->{VENDOR}."\n"; +$S .= "Packager: ".$r->{PACKAGER}."\n"; +$S .= "Distribution: ".$r->{DISTRIBUTION}."\n"; +$S .= "Class: ".$r->{CLASS}."\n"; +$S .= "Group: ".$r->{GROUP}."\n"; +$S .= "License: ".$r->{LICENSE}."\n"; +$S .= "Version: ".$r->{VERSION}."\n"; +$S .= "Release: ".$r->{RELEASE}."+PROXY\n"; +$S .= "\n"; +$S .= "BuildPreReq: $BD\n" if ($BD ne ''); +$S .= "PreReq: $ID\n" if ($ID ne ''); +$S .= "Provides: $PR\n" if ($PR ne ''); +$S .= "\n"; +$S .= "\%description\n"; +$S .= " ".$r->{DESCRIPTION}."\n"; +$S .= "\n"; +$S .= "\%install\n"; +$S .= " \%{l_rpmtool} files -v -ofiles -r\$RPM_BUILD_ROOT \%{l_files_std}\n"; +$S .= "\n"; +$S .= "\%files -f files\n"; +$S .= "\n"; +open(SPEC, ">$tmpdir/src/".$r->{NAME}.".spec") + or die "unable to write file \"$tmpdir/src/".$r->{NAME}."\": $!"; +print SPEC $S; +close(SPEC); + +# creating shadow tree of original contents +verbose("++ creating shadow tree from original package contents"); +$q = '[%{FILEMODES:perms} %{FILENAMES}\n]'; +$cmd = sprintf("%s -q %s --qf '$q' '%s'", $master_rpm, (-f $input ? "-p" : ""), $input); +my @FL = runcmd("$cmd|"); +my $FD = []; +my $FR = []; +foreach my $fl (@FL) { + $fl =~ s|\n$||s; + if ($fl =~ m|^(d\S+)\s+$master_prefix(.*)$|) { + mkdirp("$tmpdir/bld$slave_prefix$2"); + verbose("-- | PHYS $1 $slave_prefix$2"); + } + elsif ($fl =~ m|^(\S+)\s+$master_prefix(.*?)([^/\s]+)$|) { + my ($subdir, $file) = ($2, $3); + my $target = sub2rev($subdir)."/.prefix-".$r->{NAME}.$subdir.$file; + mkdirp("$tmpdir/bld$slave_prefix$subdir"); + runcmd("ln -s $target $tmpdir/bld$slave_prefix$subdir$file"); + verbose("-- | VIRT $1 $slave_prefix$subdir$file"); + } +} + +# create master-reference symbolic link +runcmd("ln -s $master_prefix $tmpdir/bld$slave_prefix/.prefix-".$r->{NAME}); + +# rolling output proxy RPM package +verbose("++ rolling output proxy RPM package"); +runcmd( + "cd $tmpdir/src && " . + "$slave_rpm -bb " . + "--define 'buildroot $tmpdir/bld' " . + "--define '__spec_install_pre \%{___build_pre}' " . + "--nodeps ".$r->{NAME}.".spec" +); + +# providing output +verbose("++ providing output: $output"); +if ($output eq '-') { + runcmd("cat $tmpdir/src/*.rpm"); +} +else { + runcmd("cp $tmpdir/src/*.rpm $output"); +} + +# die gracefully... +verbose("++ cleaning up environment"); +cleanup_perform(); +exit(0); +