|
1 ## |
|
2 ## makeproxy.pl -- OpenPKG Tool Chain |
|
3 ## Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/> |
|
4 ## |
|
5 ## This software is property of the OpenPKG GmbH, DE MUC HRB 160208. |
|
6 ## All rights reserved. Licenses which grant limited permission to use, |
|
7 ## copy, modify and distribute this software are available from the |
|
8 ## OpenPKG GmbH. |
|
9 ## |
|
10 ## THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
|
11 ## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
12 ## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
13 ## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR |
|
14 ## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
15 ## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
16 ## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
|
17 ## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
|
18 ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
19 ## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
|
20 ## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
|
21 ## SUCH DAMAGE. |
|
22 ## |
|
23 |
|
24 require 5.003; |
|
25 |
|
26 # OpenPKG instance prefix and RPM |
|
27 my $my_prefix = $ENV{'OPENPKG_PREFIX'}; |
|
28 my $my_rpm = "$my_prefix/bin/openpkg rpm"; |
|
29 delete $ENV{'OPENPKG_PREFIX'}; |
|
30 |
|
31 # program identification |
|
32 my $progname = "makeproxy"; |
|
33 my $progvers = "2.1.0"; |
|
34 |
|
35 # parameters (defaults) |
|
36 my $help = 0; |
|
37 my $version = 0; |
|
38 my $verbose = 0; |
|
39 my $debug = 0; |
|
40 my $slave_prefix = $my_prefix; |
|
41 my $master_prefix = ''; |
|
42 my $tmpdir = ($ENV{TMPDIR} || "/tmp"); |
|
43 my $output = '.'; |
|
44 my $input = '-'; |
|
45 |
|
46 # cleanup support |
|
47 my @cleanup = (); |
|
48 sub cleanup_remember { |
|
49 my ($cmd) = @_; |
|
50 push(@cleanup, $cmd); |
|
51 } |
|
52 sub cleanup_perform { |
|
53 foreach my $cmd (reverse @cleanup) { |
|
54 runcmd($cmd); |
|
55 } |
|
56 } |
|
57 |
|
58 # exception handling support |
|
59 $SIG{__DIE__} = sub { |
|
60 my ($err) = @_; |
|
61 $err =~ s|\s+at\s+.*||s if (not $verbose); |
|
62 $err =~ s/\n+$//s; |
|
63 print STDERR "openpkg:$progname:ERROR: $err\n"; |
|
64 cleanup_perform() if (not $verbose); |
|
65 exit(1); |
|
66 }; |
|
67 |
|
68 # verbose message printing |
|
69 sub verbose { |
|
70 my ($msg) = @_; |
|
71 print STDERR "$msg\n" if ($verbose); |
|
72 } |
|
73 |
|
74 # execution of external commands |
|
75 sub runcmd { |
|
76 my ($cmd) = @_; |
|
77 if ($cmd =~ m/^(.+)\|$/s) { |
|
78 print STDERR "\$ $1\n" if ($debug); |
|
79 return `$1`; |
|
80 } |
|
81 else { |
|
82 print STDERR "\$ $cmd\n" if ($debug); |
|
83 $cmd = "($cmd) >/dev/null 2>&1" if (not $debug); |
|
84 return (system($cmd) == 0); |
|
85 } |
|
86 } |
|
87 |
|
88 # expand into a full filesystem path |
|
89 sub fullpath { |
|
90 my ($prog) = @_; |
|
91 my $fullprog = ''; |
|
92 foreach my $path (split(/:/, $ENV{PATH})) { |
|
93 if (-x "$path/$prog") { |
|
94 $fullprog = "$path/$prog"; |
|
95 last; |
|
96 } |
|
97 } |
|
98 return $fullprog; |
|
99 } |
|
100 |
|
101 # convert a subdirectory (a/b/c/) |
|
102 # into a corresponding reverse path (../../../) |
|
103 sub sub2rev { |
|
104 my ($sub) = @_; |
|
105 my $rev = ''; |
|
106 $sub =~ s|^/+||s; |
|
107 $sub =~ s|/+$||s; |
|
108 while ($sub =~ s|/[^/]+||) { |
|
109 $rev .= "../"; |
|
110 } |
|
111 if ($sub ne '') { |
|
112 $rev .= "../"; |
|
113 } |
|
114 $rev =~ s|/$||s; |
|
115 return $rev; |
|
116 } |
|
117 |
|
118 # create a directory (plus its missing parent dirs) |
|
119 sub mkdirp { |
|
120 my ($dir) = @_; |
|
121 my $pdir = $dir; |
|
122 $pdir =~ s|/[^/]*$||s; |
|
123 if (not -d $pdir) { |
|
124 mkdirp($pdir); |
|
125 } |
|
126 if (not -d $dir) { |
|
127 runcmd("mkdir $dir"); |
|
128 } |
|
129 } |
|
130 |
|
131 # home-brewn getopt(3) style option parser |
|
132 sub getopts ($) { |
|
133 my ($opts) = @_; |
|
134 my (%optf) = map { m/(\w)/; $1 => $_ } $opts =~ m/(\w:|\w)/g; |
|
135 my (%opts, @argv, $optarg); |
|
136 |
|
137 foreach (@ARGV) { |
|
138 if (@argv) { |
|
139 push @argv, $_; |
|
140 } elsif (defined $optarg) { |
|
141 if (exists $opts{$optarg}) { |
|
142 $opts{$optarg} .= " $_"; |
|
143 } else { |
|
144 $opts{$optarg} = $_; |
|
145 } |
|
146 $optarg = undef; |
|
147 } elsif (!/^[-]/) { |
|
148 push @argv, $_; |
|
149 } else { |
|
150 while (/^\-(\w)(.*)/) { |
|
151 if (exists $optf{$1}) { |
|
152 if (length($optf{$1}) > 1) { |
|
153 if ($2 ne '') { |
|
154 if (exists $opts{$1}) { |
|
155 $opts{$1} .= " $2"; |
|
156 } else { |
|
157 $opts{$1} = $2; |
|
158 } |
|
159 } else { |
|
160 $optarg = $1; |
|
161 } |
|
162 last; |
|
163 } else { |
|
164 $opts{$1} = 1; |
|
165 } |
|
166 } else { |
|
167 warn "openpkg:makeproxy:WARNING: unknown option $_\n"; |
|
168 } |
|
169 $_ = "-$2"; |
|
170 } |
|
171 } |
|
172 } |
|
173 if (defined $optarg) { |
|
174 warn "openpkg:makeproxy:WARNING: option $optarg requires an argument\n"; |
|
175 } |
|
176 foreach (keys %opts) { |
|
177 eval '$opt_'.$_.' = "'.quotemeta($opts{$_}).'";'; |
|
178 } |
|
179 @ARGV = @argv; |
|
180 } |
|
181 |
|
182 # command line parsing |
|
183 getopts("Vhdvm:s:t:o:"); |
|
184 $version = $opt_V if (defined $opt_V); |
|
185 $help = $opt_h if (defined $opt_h); |
|
186 $debug = $opt_d if (defined $opt_d); |
|
187 $verbose = $opt_v if (defined $opt_v); |
|
188 $master_prefix = $opt_m if (defined $opt_m); |
|
189 $slave_prefix = $opt_s if (defined $opt_s); |
|
190 $tmpdir = $opt_t if (defined $opt_t); |
|
191 $output = $opt_o if (defined $opt_o); |
|
192 if ($help) { |
|
193 print "Usage: openpkg $progname [options] master-package\n" . |
|
194 " -h,--help print this usage page\n" . |
|
195 " -V,--version print version\n" . |
|
196 " -v,--verbose print verbose run-time information\n" . |
|
197 " -d,--debug print debug information\n" . |
|
198 " -m,--master=DIR filesystem path to master OpenPKG instance\n" . |
|
199 " -s,--slave=DIR filesystem path to slave OpenPKG instance\n" . |
|
200 " -t,--tmpdir=DIR filesystem path to temporary directory\n" . |
|
201 " -o,--output=FILE filesystem path to output proxy OpenPKG RPM package\n"; |
|
202 exit(0); |
|
203 } |
|
204 if ($version) { |
|
205 print "OpenPKG $progname $progvers\n"; |
|
206 exit(0); |
|
207 } |
|
208 if ($#ARGV == 0) { |
|
209 $input = shift(@ARGV); |
|
210 } |
|
211 if ($#ARGV != -1) { |
|
212 die "invalid number of command line arguments"; |
|
213 } |
|
214 |
|
215 # prepare temporary location |
|
216 verbose("++ prepare temporary directory"); |
|
217 $tmpdir = "$tmpdir/$progname.$$"; |
|
218 if (not -d $tmpdir) { |
|
219 runcmd("umask 077 && mkdir $tmpdir") |
|
220 or die "cannot create temporary directory \"$tmpdir\""; |
|
221 cleanup_remember("rm -rf $tmpdir"); |
|
222 } |
|
223 verbose("-- temporary directory: $tmpdir"); |
|
224 |
|
225 # determine input and output RPM |
|
226 verbose("++ determining OpenPKG RPM package files"); |
|
227 verbose("-- input/master/regular RPM package: $input"); |
|
228 verbose("-- output/slave/proxy RPM package: $output"); |
|
229 |
|
230 # ensure input RPM package is available on the filesystem |
|
231 if ($input eq '-') { |
|
232 $input = "$tmpdir/input.rpm"; |
|
233 runcmd("cat >$input"); |
|
234 } |
|
235 |
|
236 # determine master and slave OpenPKG instance and RPM commands |
|
237 if ($master_prefix eq '') { |
|
238 if (not -f $input) { |
|
239 die "input/master/regular RPM package does not exist: \"$input\""; |
|
240 } |
|
241 $master_prefix = runcmd("($my_rpm -qp --qf '%{PREFIXES}' $input 2>/dev/null || true) |"); |
|
242 $master_prefix =~ s/\r?\n$//s; |
|
243 if ($master_prefix eq '') { |
|
244 die "unable to determine master OpenPKG instance"; |
|
245 } |
|
246 } |
|
247 if ($master_prefix eq $slave_prefix) { |
|
248 die "master and slave OpenPKG instances have to be different"; |
|
249 } |
|
250 my $slave_rpm = (-f "$slave_prefix/bin/openpkg" ? "$slave_prefix/bin/openpkg rpm" : $my_rpm); |
|
251 my $master_rpm = (-f "$master_prefix/bin/openpkg" ? "$master_prefix/bin/openpkg rpm" : $my_rpm); |
|
252 verbose("-- tool OpenPKG instance: $my_prefix"); |
|
253 verbose("-- tool OpenPKG RPM command: $my_rpm"); |
|
254 verbose("-- master OpenPKG instance: $master_prefix"); |
|
255 verbose("-- master OpenPKG RPM command: $master_rpm"); |
|
256 verbose("-- slave OpenPKG instance: $slave_prefix"); |
|
257 verbose("-- slave OpenPKG RPM command: $slave_rpm"); |
|
258 |
|
259 # helper function for parsing RPM query outputs |
|
260 sub parseresponse { |
|
261 my ($r, $o) = @_; |
|
262 sub parseline { |
|
263 my ($r, $t, $k, $v) = @_; |
|
264 $v =~ s|^\s+||s; |
|
265 $v =~ s|\s+$||s; |
|
266 if ($t eq 'S') { # single-value results |
|
267 $r->{$k} = $v; |
|
268 } |
|
269 elsif ($t eq 'M') { # multi-value results |
|
270 $r->{$k} = [] if (not defined($r->{$k})); |
|
271 push(@{$r->{$k}}, $v); |
|
272 } |
|
273 } |
|
274 $o =~ s|([SM])-([^:]+):<(.*?)>\n|parseline($r, $1, $2, $3, '')|egs; |
|
275 return $r; |
|
276 } |
|
277 |
|
278 # query master OpenPKG RPM package |
|
279 verbose("++ query information from master OpenPKG RPM package"); |
|
280 my $q = ''; |
|
281 foreach my $t (qw( |
|
282 NAME SUMMARY URL VENDOR PACKAGER DISTRIBUTION CLASS GROUP LICENSE VERSION RELEASE |
|
283 DESCRIPTION |
|
284 )) { |
|
285 $q .= "S-$t:<%{$t}>\\n"; |
|
286 } |
|
287 $q .= '[M-PREREQ:<%{REQUIRENAME} %|REQUIREFLAGS?{%{REQUIREFLAGS:depflags} %{REQUIREVERSION}}:{}|>\n]'; |
|
288 $q .= '[M-PROVIDES:<%{PROVIDENAME} %|PROVIDEFLAGS?{%{PROVIDEFLAGS:depflags} %{PROVIDEVERSION}}:{}|>\n]'; |
|
289 $q .= '[M-PREFIXES:<%{PREFIXES}>\n]'; |
|
290 my $cmd = sprintf("%s -q %s --qf '$q' %s", $master_rpm, (-f $input ? "-p" : ""), $input); |
|
291 my $o = runcmd("$cmd|"); |
|
292 if ($o =~ m/package .* is not installed/s) { |
|
293 die "master package \"$input\" not installed in master OpenPKG instance \"$master_prefix\""; |
|
294 } |
|
295 $o =~ s|M-PREREQ:<rpmlib\(.*?\).*?>\n||gs; |
|
296 my $r = {}; |
|
297 $r = parseresponse($r, $o); |
|
298 my $BD = ''; |
|
299 my $ID = ''; |
|
300 my $PR = ''; |
|
301 foreach my $d (@{$r->{PREREQ}}) { |
|
302 if ($d =~ m|^OpenPKG|i) { |
|
303 $BD .= ", " if ($BD ne ''); |
|
304 $BD .= $d; |
|
305 } |
|
306 $ID .= ", " if ($ID ne ''); |
|
307 $ID .= $d; |
|
308 } |
|
309 foreach my $d (@{$r->{PROVIDES}}) { |
|
310 $PR .= ", " if ($PR ne ''); |
|
311 $PR .= $d; |
|
312 } |
|
313 |
|
314 # prepare build environment |
|
315 verbose("++ establishing temporary OpenPKG RPM environment"); |
|
316 verbose("-- directory: $tmpdir"); |
|
317 runcmd("rm -rf $tmpdir/src; mkdir $tmpdir/src"); |
|
318 runcmd("rm -rf $tmpdir/bld; mkdir $tmpdir/bld"); |
|
319 cleanup_remember("rm -rf $tmpdir/src"); |
|
320 cleanup_remember("rm -rf $tmpdir/tmp"); |
|
321 cleanup_remember("rm -rf $tmpdir/bld"); |
|
322 cleanup_remember("rm -rf $tmpdir/pkg"); |
|
323 runcmd("mkdir $tmpdir/src/.openpkg"); |
|
324 open(MACRO, ">$tmpdir/src/.openpkg/rpmmacros") |
|
325 or die "unable to write file \"$tmpdir/src/.openpkg/rpmmacros\": $!"; |
|
326 print MACRO "\%openpkg_layout macrosfile=\%{macrosfile} layout=local shared=no\n"; |
|
327 close(MACRO); |
|
328 $ENV{HOME} = $tmpdir; |
|
329 |
|
330 # generate OpenPKG RPM .spec file for proxy OpenPKG RPM package |
|
331 verbose("++ generating OpenPKG RPM package specification for proxy package"); |
|
332 verbose("-- file: $tmpdir/src/".$r->{NAME}.".spec"); |
|
333 my $S = ''; |
|
334 $S .= "Name: ".$r->{NAME}."\n"; |
|
335 $S .= "Summary: ".$r->{SUMMARY}."\n"; |
|
336 $S .= "URL: ".$r->{URL}."\n"; |
|
337 $S .= "Vendor: ".$r->{VENDOR}."\n"; |
|
338 $S .= "Packager: ".$r->{PACKAGER}."\n"; |
|
339 $S .= "Distribution: ".$r->{DISTRIBUTION}."\n"; |
|
340 $S .= "Class: ".$r->{CLASS}."\n"; |
|
341 $S .= "Group: ".$r->{GROUP}."\n"; |
|
342 $S .= "License: ".$r->{LICENSE}."\n"; |
|
343 $S .= "Version: ".$r->{VERSION}."\n"; |
|
344 $S .= "Release: ".$r->{RELEASE}."+PROXY\n"; |
|
345 $S .= "\n"; |
|
346 $S .= "BuildPreReq: $BD\n" if ($BD ne ''); |
|
347 $S .= "PreReq: $ID\n" if ($ID ne ''); |
|
348 $S .= "Provides: $PR\n" if ($PR ne ''); |
|
349 $S .= "\n"; |
|
350 $S .= "\%description\n"; |
|
351 $S .= " ".$r->{DESCRIPTION}."\n"; |
|
352 $S .= "\n"; |
|
353 $S .= "\%install\n"; |
|
354 $S .= " \%{l_rpmtool} files -v -ofiles -r\$RPM_BUILD_ROOT \%{l_files_std}\n"; |
|
355 $S .= "\n"; |
|
356 $S .= "\%files -f files\n"; |
|
357 $S .= "\n"; |
|
358 open(SPEC, ">$tmpdir/src/".$r->{NAME}.".spec") |
|
359 or die "unable to write file \"$tmpdir/src/".$r->{NAME}."\": $!"; |
|
360 print SPEC $S; |
|
361 close(SPEC); |
|
362 |
|
363 # creating shadow tree of original contents |
|
364 verbose("++ creating shadow tree from original package contents"); |
|
365 $q = '[%{FILEMODES:perms} %{FILENAMES}\n]'; |
|
366 $cmd = sprintf("%s -q %s --qf '$q' '%s'", $master_rpm, (-f $input ? "-p" : ""), $input); |
|
367 my @FL = runcmd("$cmd|"); |
|
368 my $FD = []; |
|
369 my $FR = []; |
|
370 foreach my $fl (@FL) { |
|
371 $fl =~ s|\n$||s; |
|
372 if ($fl =~ m|^(d\S+)\s+$master_prefix(.*)$|) { |
|
373 mkdirp("$tmpdir/bld$slave_prefix$2"); |
|
374 verbose("-- | PHYS $1 $slave_prefix$2"); |
|
375 } |
|
376 elsif ($fl =~ m|^(\S+)\s+$master_prefix(.*?)([^/\s]+)$|) { |
|
377 my ($subdir, $file) = ($2, $3); |
|
378 my $target = sub2rev($subdir)."/.prefix-".$r->{NAME}.$subdir.$file; |
|
379 mkdirp("$tmpdir/bld$slave_prefix$subdir"); |
|
380 runcmd("ln -s $target $tmpdir/bld$slave_prefix$subdir$file"); |
|
381 verbose("-- | VIRT $1 $slave_prefix$subdir$file"); |
|
382 } |
|
383 } |
|
384 |
|
385 # create master-reference symbolic link |
|
386 runcmd("ln -s $master_prefix $tmpdir/bld$slave_prefix/.prefix-".$r->{NAME}); |
|
387 |
|
388 # rolling output proxy RPM package |
|
389 verbose("++ rolling output proxy RPM package"); |
|
390 runcmd( |
|
391 "cd $tmpdir/src && " . |
|
392 "$slave_rpm -bb " . |
|
393 "--define 'buildroot $tmpdir/bld' " . |
|
394 "--define '__spec_install_pre \%{___build_pre}' " . |
|
395 "--nodeps ".$r->{NAME}.".spec" |
|
396 ); |
|
397 |
|
398 # providing output |
|
399 verbose("++ providing output: $output"); |
|
400 if ($output eq '-') { |
|
401 runcmd("cat $tmpdir/src/*.rpm"); |
|
402 } |
|
403 else { |
|
404 runcmd("cp $tmpdir/src/*.rpm $output"); |
|
405 } |
|
406 |
|
407 # die gracefully... |
|
408 verbose("++ cleaning up environment"); |
|
409 cleanup_perform(); |
|
410 exit(0); |
|
411 |