1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/openpkg/makeproxy.pl Tue Jul 31 12:23:42 2012 +0200 1.3 @@ -0,0 +1,411 @@ 1.4 +## 1.5 +## makeproxy.pl -- OpenPKG Tool Chain 1.6 +## Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/> 1.7 +## 1.8 +## This software is property of the OpenPKG GmbH, DE MUC HRB 160208. 1.9 +## All rights reserved. Licenses which grant limited permission to use, 1.10 +## copy, modify and distribute this software are available from the 1.11 +## OpenPKG GmbH. 1.12 +## 1.13 +## THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED 1.14 +## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1.15 +## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1.16 +## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR 1.17 +## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.18 +## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.19 +## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 1.20 +## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 1.21 +## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 1.22 +## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 1.23 +## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 1.24 +## SUCH DAMAGE. 1.25 +## 1.26 + 1.27 +require 5.003; 1.28 + 1.29 +# OpenPKG instance prefix and RPM 1.30 +my $my_prefix = $ENV{'OPENPKG_PREFIX'}; 1.31 +my $my_rpm = "$my_prefix/bin/openpkg rpm"; 1.32 +delete $ENV{'OPENPKG_PREFIX'}; 1.33 + 1.34 +# program identification 1.35 +my $progname = "makeproxy"; 1.36 +my $progvers = "2.1.0"; 1.37 + 1.38 +# parameters (defaults) 1.39 +my $help = 0; 1.40 +my $version = 0; 1.41 +my $verbose = 0; 1.42 +my $debug = 0; 1.43 +my $slave_prefix = $my_prefix; 1.44 +my $master_prefix = ''; 1.45 +my $tmpdir = ($ENV{TMPDIR} || "/tmp"); 1.46 +my $output = '.'; 1.47 +my $input = '-'; 1.48 + 1.49 +# cleanup support 1.50 +my @cleanup = (); 1.51 +sub cleanup_remember { 1.52 + my ($cmd) = @_; 1.53 + push(@cleanup, $cmd); 1.54 +} 1.55 +sub cleanup_perform { 1.56 + foreach my $cmd (reverse @cleanup) { 1.57 + runcmd($cmd); 1.58 + } 1.59 +} 1.60 + 1.61 +# exception handling support 1.62 +$SIG{__DIE__} = sub { 1.63 + my ($err) = @_; 1.64 + $err =~ s|\s+at\s+.*||s if (not $verbose); 1.65 + $err =~ s/\n+$//s; 1.66 + print STDERR "openpkg:$progname:ERROR: $err\n"; 1.67 + cleanup_perform() if (not $verbose); 1.68 + exit(1); 1.69 +}; 1.70 + 1.71 +# verbose message printing 1.72 +sub verbose { 1.73 + my ($msg) = @_; 1.74 + print STDERR "$msg\n" if ($verbose); 1.75 +} 1.76 + 1.77 +# execution of external commands 1.78 +sub runcmd { 1.79 + my ($cmd) = @_; 1.80 + if ($cmd =~ m/^(.+)\|$/s) { 1.81 + print STDERR "\$ $1\n" if ($debug); 1.82 + return `$1`; 1.83 + } 1.84 + else { 1.85 + print STDERR "\$ $cmd\n" if ($debug); 1.86 + $cmd = "($cmd) >/dev/null 2>&1" if (not $debug); 1.87 + return (system($cmd) == 0); 1.88 + } 1.89 +} 1.90 + 1.91 +# expand into a full filesystem path 1.92 +sub fullpath { 1.93 + my ($prog) = @_; 1.94 + my $fullprog = ''; 1.95 + foreach my $path (split(/:/, $ENV{PATH})) { 1.96 + if (-x "$path/$prog") { 1.97 + $fullprog = "$path/$prog"; 1.98 + last; 1.99 + } 1.100 + } 1.101 + return $fullprog; 1.102 +} 1.103 + 1.104 +# convert a subdirectory (a/b/c/) 1.105 +# into a corresponding reverse path (../../../) 1.106 +sub sub2rev { 1.107 + my ($sub) = @_; 1.108 + my $rev = ''; 1.109 + $sub =~ s|^/+||s; 1.110 + $sub =~ s|/+$||s; 1.111 + while ($sub =~ s|/[^/]+||) { 1.112 + $rev .= "../"; 1.113 + } 1.114 + if ($sub ne '') { 1.115 + $rev .= "../"; 1.116 + } 1.117 + $rev =~ s|/$||s; 1.118 + return $rev; 1.119 +} 1.120 + 1.121 +# create a directory (plus its missing parent dirs) 1.122 +sub mkdirp { 1.123 + my ($dir) = @_; 1.124 + my $pdir = $dir; 1.125 + $pdir =~ s|/[^/]*$||s; 1.126 + if (not -d $pdir) { 1.127 + mkdirp($pdir); 1.128 + } 1.129 + if (not -d $dir) { 1.130 + runcmd("mkdir $dir"); 1.131 + } 1.132 +} 1.133 + 1.134 +# home-brewn getopt(3) style option parser 1.135 +sub getopts ($) { 1.136 + my ($opts) = @_; 1.137 + my (%optf) = map { m/(\w)/; $1 => $_ } $opts =~ m/(\w:|\w)/g; 1.138 + my (%opts, @argv, $optarg); 1.139 + 1.140 + foreach (@ARGV) { 1.141 + if (@argv) { 1.142 + push @argv, $_; 1.143 + } elsif (defined $optarg) { 1.144 + if (exists $opts{$optarg}) { 1.145 + $opts{$optarg} .= " $_"; 1.146 + } else { 1.147 + $opts{$optarg} = $_; 1.148 + } 1.149 + $optarg = undef; 1.150 + } elsif (!/^[-]/) { 1.151 + push @argv, $_; 1.152 + } else { 1.153 + while (/^\-(\w)(.*)/) { 1.154 + if (exists $optf{$1}) { 1.155 + if (length($optf{$1}) > 1) { 1.156 + if ($2 ne '') { 1.157 + if (exists $opts{$1}) { 1.158 + $opts{$1} .= " $2"; 1.159 + } else { 1.160 + $opts{$1} = $2; 1.161 + } 1.162 + } else { 1.163 + $optarg = $1; 1.164 + } 1.165 + last; 1.166 + } else { 1.167 + $opts{$1} = 1; 1.168 + } 1.169 + } else { 1.170 + warn "openpkg:makeproxy:WARNING: unknown option $_\n"; 1.171 + } 1.172 + $_ = "-$2"; 1.173 + } 1.174 + } 1.175 + } 1.176 + if (defined $optarg) { 1.177 + warn "openpkg:makeproxy:WARNING: option $optarg requires an argument\n"; 1.178 + } 1.179 + foreach (keys %opts) { 1.180 + eval '$opt_'.$_.' = "'.quotemeta($opts{$_}).'";'; 1.181 + } 1.182 + @ARGV = @argv; 1.183 +} 1.184 + 1.185 +# command line parsing 1.186 +getopts("Vhdvm:s:t:o:"); 1.187 +$version = $opt_V if (defined $opt_V); 1.188 +$help = $opt_h if (defined $opt_h); 1.189 +$debug = $opt_d if (defined $opt_d); 1.190 +$verbose = $opt_v if (defined $opt_v); 1.191 +$master_prefix = $opt_m if (defined $opt_m); 1.192 +$slave_prefix = $opt_s if (defined $opt_s); 1.193 +$tmpdir = $opt_t if (defined $opt_t); 1.194 +$output = $opt_o if (defined $opt_o); 1.195 +if ($help) { 1.196 + print "Usage: openpkg $progname [options] master-package\n" . 1.197 + " -h,--help print this usage page\n" . 1.198 + " -V,--version print version\n" . 1.199 + " -v,--verbose print verbose run-time information\n" . 1.200 + " -d,--debug print debug information\n" . 1.201 + " -m,--master=DIR filesystem path to master OpenPKG instance\n" . 1.202 + " -s,--slave=DIR filesystem path to slave OpenPKG instance\n" . 1.203 + " -t,--tmpdir=DIR filesystem path to temporary directory\n" . 1.204 + " -o,--output=FILE filesystem path to output proxy OpenPKG RPM package\n"; 1.205 + exit(0); 1.206 +} 1.207 +if ($version) { 1.208 + print "OpenPKG $progname $progvers\n"; 1.209 + exit(0); 1.210 +} 1.211 +if ($#ARGV == 0) { 1.212 + $input = shift(@ARGV); 1.213 +} 1.214 +if ($#ARGV != -1) { 1.215 + die "invalid number of command line arguments"; 1.216 +} 1.217 + 1.218 +# prepare temporary location 1.219 +verbose("++ prepare temporary directory"); 1.220 +$tmpdir = "$tmpdir/$progname.$$"; 1.221 +if (not -d $tmpdir) { 1.222 + runcmd("umask 077 && mkdir $tmpdir") 1.223 + or die "cannot create temporary directory \"$tmpdir\""; 1.224 + cleanup_remember("rm -rf $tmpdir"); 1.225 +} 1.226 +verbose("-- temporary directory: $tmpdir"); 1.227 + 1.228 +# determine input and output RPM 1.229 +verbose("++ determining OpenPKG RPM package files"); 1.230 +verbose("-- input/master/regular RPM package: $input"); 1.231 +verbose("-- output/slave/proxy RPM package: $output"); 1.232 + 1.233 +# ensure input RPM package is available on the filesystem 1.234 +if ($input eq '-') { 1.235 + $input = "$tmpdir/input.rpm"; 1.236 + runcmd("cat >$input"); 1.237 +} 1.238 + 1.239 +# determine master and slave OpenPKG instance and RPM commands 1.240 +if ($master_prefix eq '') { 1.241 + if (not -f $input) { 1.242 + die "input/master/regular RPM package does not exist: \"$input\""; 1.243 + } 1.244 + $master_prefix = runcmd("($my_rpm -qp --qf '%{PREFIXES}' $input 2>/dev/null || true) |"); 1.245 + $master_prefix =~ s/\r?\n$//s; 1.246 + if ($master_prefix eq '') { 1.247 + die "unable to determine master OpenPKG instance"; 1.248 + } 1.249 +} 1.250 +if ($master_prefix eq $slave_prefix) { 1.251 + die "master and slave OpenPKG instances have to be different"; 1.252 +} 1.253 +my $slave_rpm = (-f "$slave_prefix/bin/openpkg" ? "$slave_prefix/bin/openpkg rpm" : $my_rpm); 1.254 +my $master_rpm = (-f "$master_prefix/bin/openpkg" ? "$master_prefix/bin/openpkg rpm" : $my_rpm); 1.255 +verbose("-- tool OpenPKG instance: $my_prefix"); 1.256 +verbose("-- tool OpenPKG RPM command: $my_rpm"); 1.257 +verbose("-- master OpenPKG instance: $master_prefix"); 1.258 +verbose("-- master OpenPKG RPM command: $master_rpm"); 1.259 +verbose("-- slave OpenPKG instance: $slave_prefix"); 1.260 +verbose("-- slave OpenPKG RPM command: $slave_rpm"); 1.261 + 1.262 +# helper function for parsing RPM query outputs 1.263 +sub parseresponse { 1.264 + my ($r, $o) = @_; 1.265 + sub parseline { 1.266 + my ($r, $t, $k, $v) = @_; 1.267 + $v =~ s|^\s+||s; 1.268 + $v =~ s|\s+$||s; 1.269 + if ($t eq 'S') { # single-value results 1.270 + $r->{$k} = $v; 1.271 + } 1.272 + elsif ($t eq 'M') { # multi-value results 1.273 + $r->{$k} = [] if (not defined($r->{$k})); 1.274 + push(@{$r->{$k}}, $v); 1.275 + } 1.276 + } 1.277 + $o =~ s|([SM])-([^:]+):<(.*?)>\n|parseline($r, $1, $2, $3, '')|egs; 1.278 + return $r; 1.279 +} 1.280 + 1.281 +# query master OpenPKG RPM package 1.282 +verbose("++ query information from master OpenPKG RPM package"); 1.283 +my $q = ''; 1.284 +foreach my $t (qw( 1.285 + NAME SUMMARY URL VENDOR PACKAGER DISTRIBUTION CLASS GROUP LICENSE VERSION RELEASE 1.286 + DESCRIPTION 1.287 +)) { 1.288 + $q .= "S-$t:<%{$t}>\\n"; 1.289 +} 1.290 +$q .= '[M-PREREQ:<%{REQUIRENAME} %|REQUIREFLAGS?{%{REQUIREFLAGS:depflags} %{REQUIREVERSION}}:{}|>\n]'; 1.291 +$q .= '[M-PROVIDES:<%{PROVIDENAME} %|PROVIDEFLAGS?{%{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}}:{}|>\n]'; 1.292 +$q .= '[M-PREFIXES:<%{PREFIXES}>\n]'; 1.293 +my $cmd = sprintf("%s -q %s --qf '$q' %s", $master_rpm, (-f $input ? "-p" : ""), $input); 1.294 +my $o = runcmd("$cmd|"); 1.295 +if ($o =~ m/package .* is not installed/s) { 1.296 + die "master package \"$input\" not installed in master OpenPKG instance \"$master_prefix\""; 1.297 +} 1.298 +$o =~ s|M-PREREQ:<rpmlib\(.*?\).*?>\n||gs; 1.299 +my $r = {}; 1.300 +$r = parseresponse($r, $o); 1.301 +my $BD = ''; 1.302 +my $ID = ''; 1.303 +my $PR = ''; 1.304 +foreach my $d (@{$r->{PREREQ}}) { 1.305 + if ($d =~ m|^OpenPKG|i) { 1.306 + $BD .= ", " if ($BD ne ''); 1.307 + $BD .= $d; 1.308 + } 1.309 + $ID .= ", " if ($ID ne ''); 1.310 + $ID .= $d; 1.311 +} 1.312 +foreach my $d (@{$r->{PROVIDES}}) { 1.313 + $PR .= ", " if ($PR ne ''); 1.314 + $PR .= $d; 1.315 +} 1.316 + 1.317 +# prepare build environment 1.318 +verbose("++ establishing temporary OpenPKG RPM environment"); 1.319 +verbose("-- directory: $tmpdir"); 1.320 +runcmd("rm -rf $tmpdir/src; mkdir $tmpdir/src"); 1.321 +runcmd("rm -rf $tmpdir/bld; mkdir $tmpdir/bld"); 1.322 +cleanup_remember("rm -rf $tmpdir/src"); 1.323 +cleanup_remember("rm -rf $tmpdir/tmp"); 1.324 +cleanup_remember("rm -rf $tmpdir/bld"); 1.325 +cleanup_remember("rm -rf $tmpdir/pkg"); 1.326 +runcmd("mkdir $tmpdir/src/.openpkg"); 1.327 +open(MACRO, ">$tmpdir/src/.openpkg/rpmmacros") 1.328 + or die "unable to write file \"$tmpdir/src/.openpkg/rpmmacros\": $!"; 1.329 +print MACRO "\%openpkg_layout macrosfile=\%{macrosfile} layout=local shared=no\n"; 1.330 +close(MACRO); 1.331 +$ENV{HOME} = $tmpdir; 1.332 + 1.333 +# generate OpenPKG RPM .spec file for proxy OpenPKG RPM package 1.334 +verbose("++ generating OpenPKG RPM package specification for proxy package"); 1.335 +verbose("-- file: $tmpdir/src/".$r->{NAME}.".spec"); 1.336 +my $S = ''; 1.337 +$S .= "Name: ".$r->{NAME}."\n"; 1.338 +$S .= "Summary: ".$r->{SUMMARY}."\n"; 1.339 +$S .= "URL: ".$r->{URL}."\n"; 1.340 +$S .= "Vendor: ".$r->{VENDOR}."\n"; 1.341 +$S .= "Packager: ".$r->{PACKAGER}."\n"; 1.342 +$S .= "Distribution: ".$r->{DISTRIBUTION}."\n"; 1.343 +$S .= "Class: ".$r->{CLASS}."\n"; 1.344 +$S .= "Group: ".$r->{GROUP}."\n"; 1.345 +$S .= "License: ".$r->{LICENSE}."\n"; 1.346 +$S .= "Version: ".$r->{VERSION}."\n"; 1.347 +$S .= "Release: ".$r->{RELEASE}."+PROXY\n"; 1.348 +$S .= "\n"; 1.349 +$S .= "BuildPreReq: $BD\n" if ($BD ne ''); 1.350 +$S .= "PreReq: $ID\n" if ($ID ne ''); 1.351 +$S .= "Provides: $PR\n" if ($PR ne ''); 1.352 +$S .= "\n"; 1.353 +$S .= "\%description\n"; 1.354 +$S .= " ".$r->{DESCRIPTION}."\n"; 1.355 +$S .= "\n"; 1.356 +$S .= "\%install\n"; 1.357 +$S .= " \%{l_rpmtool} files -v -ofiles -r\$RPM_BUILD_ROOT \%{l_files_std}\n"; 1.358 +$S .= "\n"; 1.359 +$S .= "\%files -f files\n"; 1.360 +$S .= "\n"; 1.361 +open(SPEC, ">$tmpdir/src/".$r->{NAME}.".spec") 1.362 + or die "unable to write file \"$tmpdir/src/".$r->{NAME}."\": $!"; 1.363 +print SPEC $S; 1.364 +close(SPEC); 1.365 + 1.366 +# creating shadow tree of original contents 1.367 +verbose("++ creating shadow tree from original package contents"); 1.368 +$q = '[%{FILEMODES:perms} %{FILENAMES}\n]'; 1.369 +$cmd = sprintf("%s -q %s --qf '$q' '%s'", $master_rpm, (-f $input ? "-p" : ""), $input); 1.370 +my @FL = runcmd("$cmd|"); 1.371 +my $FD = []; 1.372 +my $FR = []; 1.373 +foreach my $fl (@FL) { 1.374 + $fl =~ s|\n$||s; 1.375 + if ($fl =~ m|^(d\S+)\s+$master_prefix(.*)$|) { 1.376 + mkdirp("$tmpdir/bld$slave_prefix$2"); 1.377 + verbose("-- | PHYS $1 $slave_prefix$2"); 1.378 + } 1.379 + elsif ($fl =~ m|^(\S+)\s+$master_prefix(.*?)([^/\s]+)$|) { 1.380 + my ($subdir, $file) = ($2, $3); 1.381 + my $target = sub2rev($subdir)."/.prefix-".$r->{NAME}.$subdir.$file; 1.382 + mkdirp("$tmpdir/bld$slave_prefix$subdir"); 1.383 + runcmd("ln -s $target $tmpdir/bld$slave_prefix$subdir$file"); 1.384 + verbose("-- | VIRT $1 $slave_prefix$subdir$file"); 1.385 + } 1.386 +} 1.387 + 1.388 +# create master-reference symbolic link 1.389 +runcmd("ln -s $master_prefix $tmpdir/bld$slave_prefix/.prefix-".$r->{NAME}); 1.390 + 1.391 +# rolling output proxy RPM package 1.392 +verbose("++ rolling output proxy RPM package"); 1.393 +runcmd( 1.394 + "cd $tmpdir/src && " . 1.395 + "$slave_rpm -bb " . 1.396 + "--define 'buildroot $tmpdir/bld' " . 1.397 + "--define '__spec_install_pre \%{___build_pre}' " . 1.398 + "--nodeps ".$r->{NAME}.".spec" 1.399 +); 1.400 + 1.401 +# providing output 1.402 +verbose("++ providing output: $output"); 1.403 +if ($output eq '-') { 1.404 + runcmd("cat $tmpdir/src/*.rpm"); 1.405 +} 1.406 +else { 1.407 + runcmd("cp $tmpdir/src/*.rpm $output"); 1.408 +} 1.409 + 1.410 +# die gracefully... 1.411 +verbose("++ cleaning up environment"); 1.412 +cleanup_perform(); 1.413 +exit(0); 1.414 +