3 ################################################################
5 # Copyright (c) 1995-2014 SUSE Linux Products GmbH
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License version 2 or 3 as
9 # published by the Free Software Foundation.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program (see the file COPYING); if not, write to the
18 # Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 ################################################################
25 use File::Temp qw/tempfile tempdir/;
27 # See: http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html#S3-RPM-FILE-FORMAT-HEADER-TAG-LISTING
28 # cf http://search.cpan.org/~davecross/Parse-RPM-Spec-0.01/lib/Parse/RPM/Spec.pm
36 "DESCRIPTION" => 1005,
39 "INSTALLTIME" => 1008,
41 "DISTRIBUTION" => 1010,
58 "OLDFILENAMES" => 1027,
65 "FILELINKTOS" => 1036,
67 "FILEUSERNAME" => 1039,
68 "FILEGROUPNAME" => 1040,
71 "FILEVERIFYFLAGS" => 1045,
72 "ARCHIVESIZE" => 1046,
73 "PROVIDENAME" => 1047,
75 "REQUIREFLAGS" => 1048,
76 "REQUIRENAME" => 1049,
77 "REQUIREVERSION" => 1050,
80 "CONFLICTFLAGS" => 1053,
81 "CONFLICTNAME" => 1054,
82 "CONFLICTVERSION" => 1055,
83 "EXCLUDEARCH" => 1059,
85 "EXCLUSIVEARCH" => 1061,
86 "EXCLUSIVEOS" => 1062,
88 "TRIGGERSCRIPTS" => 1065,
89 "TRIGGERNAME" => 1066,
90 "TRIGGERVERSION" => 1067,
91 "TRIGGERFLAGS" => 1068,
92 "TRIGGERINDEX" => 1069,
93 "VERIFYSCRIPT" => 1079,
94 "CHANGELOGTIME" => 1080,
95 "CHANGELOGNAME" => 1081,
96 "CHANGELOGTEXT" => 1082,
100 "POSTUNPROG" => 1088,
101 "BUILDARCHS" => 1089,
102 "OBSOLETENAME" => 1090,
104 "VERIFYSCRIPTPROG" => 1091,
105 "TRIGGERSCRIPTPROG" => 1092,
107 "FILEDEVICES" => 1095,
108 "FILEINODES" => 1096,
111 "INSTPREFIXES" => 1099,
112 "SOURCEPACKAGE" => 1106,
113 "PROVIDEFLAGS" => 1112,
114 "PROVIDEVERSION" => 1113,
115 "OBSOLETEFLAGS" => 1114,
116 "OBSOLETEVERSION" => 1115,
117 "DIRINDEXES" => 1116,
122 "PAYLOADFORMAT" => 1124,
123 "PAYLOADCOMPRESSOR" => 1125,
124 "PAYLOADFLAGS" => 1126,
125 "INSTALLCOLOR" => 1127,
126 "INSTALLTID" => 1128,
128 "RHNPLATFORM" => 1131,
130 "PATCHESNAME" => 1133,
131 "PATCHESFLAGS" => 1134,
132 "PATCHESVERSION" => 1135,
133 "CACHECTIME" => 1136,
134 "CACHEPKGPATH" => 1137,
135 "CACHEPKGSIZE" => 1138,
136 "CACHEPKGMTIME" => 1139,
137 "FILECOLORS" => 1140,
140 "FILEDEPENDSX" => 1143,
141 "FILEDEPENDSN" => 1144,
142 "DEPENDSDICT" => 1145,
143 "SOURCEPKGID" => 1146,
146 "PRETRANSPROG" => 1153,
147 "POSTTRANSPROG" => 1154,
149 "SUGGESTSNAME" => 1156,
150 "SUGGESTSVERSION" => 1157,
151 "SUGGESTSFLAGS" => 1158,
152 "ENHANCESNAME" => 1159,
153 "ENHANCESVERSION" => 1160,
154 "ENHANCESFLAGS" => 1161,
159 # do not mix numeric tags with symbolic tags.
160 # special symbolic tag 'FILENAME' exists.
162 # This function seems to take a set of tags and populates a global
163 # hash-table (%res) with data obtained by doing a binary unpack() on
165 # http://www.rpm.org/max-rpm/s1-rpm-file-format-rpm-file-format.html
171 my $need_filenames = grep { $_ eq 'FILENAMES' } @stags;
172 push @stags, 'BASENAMES', 'DIRNAMES', 'DIRINDEXES', 'OLDFILENAMES' if $need_filenames;
173 @stags = grep { $_ ne 'FILENAMES' } @stags if $need_filenames;
174 my %stags = map {0+($STAG{$_} or $_) => $_} @stags;
176 my ($magic, $sigtype, $headmagic, $cnt, $cntdata, $lead, $head, $index, $data, $tag, $type, $offset, $count);
179 if (ref($rpm) eq 'ARRAY') {
180 ($headmagic, $cnt, $cntdata) = unpack('N@8NN', $rpm->[0]);
181 if ($headmagic != 0x8eade801) {
185 if (length($rpm->[0]) < 16 + $cnt * 16 + $cntdata) {
189 $index = substr($rpm->[0], 16, $cnt * 16);
190 $data = substr($rpm->[0], 16 + $cnt * 16, $cntdata);
192 return () unless open(RPM, "<$rpm");
193 if (read(RPM, $lead, 96) != 96) {
194 warn("Bad rpm $rpm\n");
198 ($magic, $sigtype) = unpack('N@78n', $lead);
199 if ($magic != 0xedabeedb || $sigtype != 5) {
200 warn("Bad rpm $rpm\n");
204 if (read(RPM, $head, 16) != 16) {
205 warn("Bad rpm $rpm\n");
209 ($headmagic, $cnt, $cntdata) = unpack('N@8NN', $head);
210 if ($headmagic != 0x8eade801) {
211 warn("Bad rpm $rpm\n");
215 if (read(RPM, $index, $cnt * 16) != $cnt * 16) {
216 warn("Bad rpm $rpm\n");
220 $cntdata = ($cntdata + 7) & ~7;
221 if (read(RPM, $data, $cntdata) != $cntdata) {
222 warn("Bad rpm $rpm\n");
230 if (ref($rpm) eq 'ARRAY' && @stags && @$rpm > 1) {
231 my %res2 = &rpmq_many([ $rpm->[1] ], @stags);
232 %res = (%res, %res2);
236 if (ref($rpm) ne 'ARRAY' && @stags) {
237 if (read(RPM, $head, 16) != 16) {
238 warn("Bad rpm $rpm\n");
242 ($headmagic, $cnt, $cntdata) = unpack('N@8NN', $head);
243 if ($headmagic != 0x8eade801) {
244 warn("Bad rpm $rpm\n");
248 if (read(RPM, $index, $cnt * 16) != $cnt * 16) {
249 warn("Bad rpm $rpm\n");
253 if (read(RPM, $data, $cntdata) != $cntdata) {
254 warn("Bad rpm $rpm\n");
259 close RPM if ref($rpm) ne 'ARRAY';
261 return %res unless @stags; # nothing to do
264 ($tag, $type, $offset, $count, $index) = unpack('N4a*', $index);
268 my $otag = $stags{$tag};
270 $res{$otag} = [ '' ];
271 } elsif ($type == 1) {
272 $res{$otag} = [ unpack("\@${offset}c$count", $data) ];
273 } elsif ($type == 2) {
274 $res{$otag} = [ unpack("\@${offset}c$count", $data) ];
275 } elsif ($type == 3) {
276 $res{$otag} = [ unpack("\@${offset}n$count", $data) ];
277 } elsif ($type == 4) {
278 $res{$otag} = [ unpack("\@${offset}N$count", $data) ];
279 } elsif ($type == 5) {
280 $res{$otag} = [ undef ];
281 } elsif ($type == 6) {
282 $res{$otag} = [ unpack("\@${offset}Z*", $data) ];
283 } elsif ($type == 7) {
284 $res{$otag} = [ unpack("\@${offset}a$count", $data) ];
285 } elsif ($type == 8 || $type == 9) {
286 my $d = unpack("\@${offset}a*", $data);
287 my @res = split("\0", $d, $count + 1);
288 $res{$otag} = [ splice @res, 0, $count ];
290 $res{$otag} = [ undef ];
294 warn("Bad rpm $rpm: $@\n");
300 if ($need_filenames) {
301 if ($res{'OLDFILENAMES'}) {
302 $res{'FILENAMES'} = [ @{$res{'OLDFILENAMES'}} ];
305 $res{'FILENAMES'} = [ map {"$res{'DIRNAMES'}->[$res{'DIRINDEXES'}->[$i++]]$_"} @{$res{'BASENAMES'}} ];
311 sub rpmq_add_flagsvers {
318 my @flags = @{$res->{$flags} || []};
319 my @vers = @{$res->{$vers} || []};
320 for (@{$res->{$name}}) {
321 if (@flags && ($flags[0] & 0xe) && @vers) {
323 $_ .= '<' if $flags[0] & 2;
324 $_ .= '>' if $flags[0] & 4;
325 $_ .= '=' if $flags[0] & 8;
334 Name Version Release Epoch Summary Copyright License Distribution
335 Disturl Vendor Group Packager Url Icon Prefixes
373 # Used for each package by
387 ($targetarch, $targettype) = split(':', $target, 2);
405 for (split("\n", $config)) {
408 next if $_ eq '' || $_ =~ /^#/;
410 s/\<targettype\>/$targettype/g;
411 s/\<targetarch\>/$targetarch/g;
412 s/\<name\>/$pkgname/g;
413 s/\<version\>/$pkgver/g;
414 s/\<prefix\>/$prefix/g;
415 s/\<extension\>/$extension/g;
416 s/\<configdir\>/$configdir/g;
417 s/\<match1\>/$match1/g;
420 next unless s/^arch\s+\Q$arch\E\s+//;
422 next if /^targets\s+/;
423 if (/\s+package\s+[-+_a-zA-Z0-9]+$/) {
424 $pkgmatches = 0; # XXX: hack
426 if (/\s+package\s+\/[-+_a-zA-Z0-9]+\/$/) {
427 $pkgmatches = 0; # XXX: hack
429 if (/^targettype\s+/) {
430 next unless s/^targettype\s+\Q$targettype\E\s+//;
432 if (/^targetarch\s+/) {
433 next unless s/^targetarch\s+\Q$targetarch\E\s+//;
435 if (/^prefix\s+(.*?)$/) { $prefix = $1; next; }
436 if (/^legacyversion\s+(.*?)$/) { $legacyversion = $1; next; }
437 if (/^extension\s+(.*?)$/) { $extension = $1; next; }
438 if (/^configdir\s+(.*?)$/) { $configdir= $1; next; }
439 if (/^targetname\s+(.*?)$/) { $targetname = $1; next; }
441 $_ = "baselib $_" if /^[\+\-\"]/;
442 $_ = "package $_" if /^[-+_a-zA-Z0-9]+$/;
443 if (/^package\s+\/(.*?)\/$/) {
445 $pkgmatches = $pkgname =~ /$pm/;
446 $match1 = $1 if defined $1;
447 $pkghasmatched |= $pkgmatches if $pkgname =~ /-debuginfo$/ && $target_matched{$target};
450 if (/^package\s+(.*?)$/) {
451 $pkgmatches = $1 eq $pkgname;
452 $pkghasmatched |= $pkgmatches;
455 next unless $pkgmatches;
456 return 0 if $_ eq 'block!';
457 if (/^provides\s+(.*?)$/) { push @provides, $1; next; }
458 if (/^requires\s+(.*?)$/) { push @requires, $1; next; }
459 if (/^recommends\s+(.*?)$/) { push @recommends, $1; next; }
460 if (/^supplements\s+(.*?)$/) { push @supplements, $1; next; }
461 if (/^suggests\s+(.*?)$/) { push @suggests, $1; next; }
462 if (/^prereq\s+(.*?)$/) { push @prerequires, $1; next; }
463 if (/^obsoletes\s+(.*?)$/) { push @obsoletes, $1; next; }
464 if (/^conflicts\s+(.*?)$/) { push @conflicts, $1; next; }
465 if (/^baselib\s+(.*?)$/) { push @baselib, $1; next; }
466 if (/^config\s+(.*?)$/) { push @config, $1; next; }
467 if (/^pre(in)?\s+(.*?)$/) { push @prein, $2; next; }
468 if (/^post(in)?\s+(.*?)$/) { push @postin, $2; next; }
469 if (/^preun\s+(.*?)$/) { push @preun, $1; next; }
470 if (/^postun\s+(.*?)$/) { push @preun, $1; next; }
471 if (/^autoreqprov\s+(.*?)$/) {$autoreqprov = $1; next; }
472 die("bad line: $_\n");
474 return $pkghasmatched;
480 open(F, "<$cfname") || die("$cfname: $!\n");
483 $config .= join('', @cf);
484 $config .= "\npackage __does_not_match__\n";
488 my $architecture = shift;
491 for (split("\n", $conf)) {
493 next unless s/^arch\s+\Q$architecture\E\s+//;
495 if (/^targets\s+(.*?)$/) {
496 $targets{$_} = 1 for split(' ', $1);
499 my @targets = sort keys %targets;
503 # Packages listed in config file
506 for (split("\n", $config)) {
507 if (/^(.*\s+)?package\s+([-+_a-zA-Z0-9]+)\s*$/) { # eg : arch ppc package libnuma-devel
509 } elsif (/^\s*([-+_a-zA-Z0-9]+)\s*$/) { # eg: readline-devel
513 return sort keys %rpms;
516 # Packages listed in config file - debian variant (can have "." in package names)
517 sub get_debpkgnames {
519 for (split("\n", $config)) {
520 if (/^(.*\s+)?package\s+([-+_a-zA-Z0-9.]+)\s*$/) { # eg : arch ppc package libnuma-devel
522 } elsif (/^\s*([-+_a-zA-Z0-9.]+)\s*$/) { # eg: readline-devel
526 return sort keys %debs;
532 my @stags = map {uc($_)} @preamble;
533 push @stags, 'DESCRIPTION';
534 push @stags, 'FILENAMES', 'FILEMODES', 'FILEUSERNAME', 'FILEGROUPNAME', 'FILEFLAGS', 'FILEVERIFYFLAGS';
535 push @stags, 'CHANGELOGTIME', 'CHANGELOGNAME', 'CHANGELOGTEXT';
536 push @stags, 'ARCH', 'SOURCERPM', 'RPMVERSION';
537 push @stags, 'BUILDTIME';
538 my %res = rpmq_many($rpm, @stags);
539 die("$rpm: bad rpm\n") unless $res{'NAME'};
541 my $rname = $res{'NAME'}->[0];
542 my $sname = $res{'SOURCERPM'}->[0];
543 die("$rpm is a sourcerpm\n") unless $sname;
544 die("bad sourcerpm: $sname\n") unless $sname =~ /^(.*)-([^-]+)-([^-]+)\.(no)?src\.rpm$/;
549 $arch = $res{'ARCH'}->[0];
550 my @targets = get_targets($arch, $config);
552 print "no targets for arch $arch, skipping $rname\n";
555 for my $target (@targets) {
557 next unless parse_config($target, $res{'NAME'}->[0], $res{'VERSION'}->[0]);
558 die("targetname not set\n") unless $targetname;
559 $target_matched{$target} = 1;
562 my @rpmfiles = @{$res{'FILENAMES'}};
563 my @ff = @{$res{'FILEFLAGS'}};
565 $ghosts{$_} = 1 if $ff[0] & (1 << 6);
572 for my $r (@baselib) {
573 my $rr = substr($r, 1);
574 if (substr($r, 0, 1) eq '+') {
575 if ($rr =~ /^(.*?)\s*->\s*(.*?)$/) {
576 if (grep {$_ eq $1} @rpmfiles) {
581 for (grep {/$rr/} @rpmfiles) {
586 } elsif (substr($r, 0, 1) eq '-') {
587 delete $files{$_} for grep {/$rr/} keys %files;
588 } elsif (substr($r, 0, 1) eq '"') {
590 if ($rr =~ /^(.*?)\s*->\s*(.*?)$/) {
593 die("bad baselib string rule: $r\n");
596 die("bad baselib rule: $r\n");
600 for my $r (@config) {
601 my $rr = substr($r, 1);
602 if (substr($r, 0, 1) eq '+') {
603 $cfiles{$_} = 1 for grep {/$rr/} grep {!$ghosts{$_}} @rpmfiles;
604 } elsif (substr($r, 0, 1) eq '-') {
605 delete $cfiles{$_} for grep {/$rr/} keys %cfiles;
607 die("bad config rule: $r\n");
611 $files{$_} = 1 for keys %cfiles;
614 print "$rname($target): empty filelist, skipping rpm\n";
619 for (@{$res{'FILENAMES'}}) {
620 $files{$_} = $i if $files{$_};
626 next if $cfiles{$_} || $moves{$_};
628 next unless $fn =~ s/\/[^\/]+$//;
638 next unless $fn =~ s/\/[^\/]+$//;
641 next unless $fn =~ s/\/[^\/]+$//;
642 $alldirs{"$prefix$fn"} = 1;
645 $alldirs{$_} = 1 for keys %symlinks;
646 $alldirs{$configdir} = 1 if %cfiles;
648 for $ad (keys %alldirs) {
649 $alldirs{$ad} = 1 while $ad =~ s/\/[^\/]+$//;
655 delete $alldirs{$moves{$fn}};
657 delete $alldirs{"$prefix$fn"};
660 delete $alldirs{$_} for keys %symlinks;
662 delete $alldirs{$ad};
663 delete $alldirs{$ad} while $ad =~ s/\/[^\/]+$//;
664 delete $alldirs{$_} for @filesystem;
666 print "$rname($target): writing specfile...\n";
667 my ($fh, $specfile) = tempfile(SUFFIX => ".spec");
668 open(SPEC, ">&=", $fh) || die("open: $!\n");
669 for my $p (@preamble) {
671 next unless $res{$pt};
672 my $d = $res{$pt}->[0];
675 print SPEC "Name: $sname\n";
678 if ($p eq 'Version') {
679 print SPEC "Version: $sversion\n";
682 if ($p eq 'Release') {
683 print SPEC "Release: $srelease\n";
686 if ($p eq 'Disturl') {
687 print SPEC "%define disturl $d\n";
690 print SPEC "$p: $d\n";
692 print SPEC "Source: $rpm\n";
693 print SPEC "NoSource: 0\n" if $res{'SOURCERPM'}->[0] =~ /\.nosrc\.rpm$/;
694 print SPEC "BuildRoot: %{_tmppath}/baselibs-%{name}-%{version}-build\n";
695 print SPEC "%define _target_cpu $targetarch\n";
696 print SPEC "%define __os_install_post %{nil}\n";
697 print SPEC "%description\nUnneeded main package. Ignore.\n\n";
698 print SPEC "%package -n $targetname\n";
699 for my $p (@preamble) {
700 next if $p eq 'Name' || $p eq 'Disturl';
702 next unless $res{$pt};
703 my $d = $res{$pt}->[0];
705 if ($pt eq 'VERSION' && $legacyversion) {
707 } elsif ($pt eq 'RELEASE' && $legacyversion) {
708 my @bt = localtime($res{'BUILDTIME'}->[0]);
711 $d = sprintf("%04d%02d%02d%02d%02d\n", @bt[5,4,3,2,1]);
713 print SPEC "$p: $d\n";
715 print SPEC "Autoreqprov: $autoreqprov\n";
717 for my $ar ([\@provides, 'provides'],
718 [\@prerequires, 'prereq'],
719 [\@requires, 'requires'],
720 [\@recommends, 'recommends'],
721 [\@supplements, 'supplements'],
722 [\@obsoletes, 'obsoletes'],
723 [\@conflicts, 'conflicts']) {
727 if (substr($_, 0, 1) eq '"') {
728 die("bad $ar->[1] rule: $_\n") unless /^\"(.*)\"$/;
730 } elsif (substr($_, 0, 1) eq '-') {
731 my $ra = substr($_, 1);
732 @na = grep {!/$ra/} @na;
734 die("bad $ar->[1] rule: $_\n");
737 print SPEC ucfirst($ar->[1]).": $_\n" for @na;
740 $cpiopre = './' if $res{'RPMVERSION'}->[0] !~ /^3/;
741 my $d = $res{'DESCRIPTION'}->[0];
743 if ($legacyversion) {
744 $d = "This rpm was re-packaged from $res{'NAME'}->[0]-$res{'VERSION'}->[0]-$res{'RELEASE'}->[0]\n\n$d";
746 print SPEC "\n%description -n $targetname\n";
748 print SPEC "%prep\n";
749 print SPEC "%build\n";
750 print SPEC "%install\n";
751 print SPEC "rm -rf \$RPM_BUILD_ROOT\n";
752 print SPEC "mkdir \$RPM_BUILD_ROOT\n";
753 print SPEC "cd \$RPM_BUILD_ROOT\n";
754 my @cfl = grep {!$cfiles{$_} && !$moves{$_}} sort keys %files;
757 print SPEC "mkdir -p \$RPM_BUILD_ROOT$prefix\n";
758 print SPEC "pushd \$RPM_BUILD_ROOT$prefix\n";
760 print SPEC "cat <<EOFL >.filelist\n";
761 print SPEC "$_\n" for map {$cpiopre.substr($_, 1)} @cfl;
763 print SPEC "mkdir -p \$RPM_BUILD_ROOT$prefix$_\n" for sort keys %cpiodirs;
764 print SPEC "rpm2cpio $rpm | cpio -i -d -v -E .filelist\n";
765 print SPEC "rm .filelist\n";
767 for my $fn (grep {$ghosts{$_}} @cfl) {
769 $fnm = '.' unless $fnm =~ s/\/[^\/]+$//;
770 print SPEC "mkdir -p \$RPM_BUILD_ROOT$prefix$fnm\n";
771 print SPEC "touch \$RPM_BUILD_ROOT$prefix$fn\n";
778 if (%cfiles || %moves) {
779 print SPEC "mkdir -p .cfiles\n";
780 print SPEC "pushd .cfiles\n";
781 print SPEC "cat <<EOFL >.filelist\n";
782 print SPEC "$_\n" for map {$cpiopre.substr($_, 1)} grep {$cfiles{$_} || $moves{$_}} sort keys %files;
784 print SPEC "rpm2cpio $rpm | cpio -i -d -v -E .filelist\n";
787 print SPEC "mkdir -p \$RPM_BUILD_ROOT$configdir\n";
788 print SPEC "mv .cfiles$_ \$RPM_BUILD_ROOT$configdir\n" for sort keys %cfiles;
790 for my $fn (sort keys %moves) {
791 my $fnm = $moves{$fn};
792 $fnm = '.' unless $fnm =~ s/\/[^\/]+$//;
793 print SPEC "mkdir -p \$RPM_BUILD_ROOT$fnm\n";
794 print SPEC "mv .cfiles$fn \$RPM_BUILD_ROOT$moves{$fn}\n";
796 print SPEC "rm -rf .cfiles\n";
798 for my $fn (sort keys %symlinks) {
800 $fnm = '.' unless $fnm =~ s/\/[^\/]+$//;
801 print SPEC "mkdir -p \$RPM_BUILD_ROOT$fnm\n";
802 print SPEC "ln -s $symlinks{$fn} \$RPM_BUILD_ROOT$fn\n";
804 if ($prefix ne '' && grep {/\.so.*$/} @cfl) {
805 @postin = () if @postin == 1 && $postin[0] =~ /^\"-p.*ldconfig/;
806 unshift @postin, "\"/sbin/ldconfig -r $prefix\"";
810 print SPEC "%pre -n $targetname";
811 print SPEC $prein[0] =~ /^\"-p/ ? " " : "\n";
813 die("bad prein rule: $_\n") unless /^\"(.*)\"$/;
818 print SPEC "%post -n $targetname";
819 print SPEC $postin[0] =~ /^\"-p/ ? " " : "\n";
821 die("bad postin rule: $_\n") unless /^\"(.*)\"$/;
826 print SPEC "%preun -n $targetname";
827 print SPEC $preun[0] =~ /^\"-p/ ? " " : "\n";
829 die("bad preun rule: $_\n") unless /^\"(.*)\"$/;
834 print SPEC "%postun -n $targetname";
835 print SPEC $postun[0] =~ /^\"-p/ ? " " : "\n";
837 die("bad postun rule: $_\n") unless /^\"(.*)\"$/;
842 print SPEC "\n%clean\n";
843 print SPEC "\nrm -rf \$RPM_BUILD_ROOT\n\n";
844 print SPEC "%files -n $targetname\n";
845 for my $file (sort keys %alldirs) {
846 print SPEC "%dir %attr(0755,root,root) $file\n";
848 for my $file (keys %files) {
849 my $fi = $files{$file};
850 my $fm = $res{'FILEMODES'}->[$fi];
851 my $fv = $res{'FILEVERIFYFLAGS'}->[$fi];
852 my $ff = $res{'FILEFLAGS'}->[$fi];
853 if (POSIX::S_ISDIR($fm)) {
856 if ($ff & ((1 << 3) | (1 << 4))) {
857 print SPEC "%config(missingok noreplace) ";
858 } elsif ($ff & (1 << 3)) {
859 print SPEC "%config(missingok) ";
860 } elsif ($ff & (1 << 4)) {
861 print SPEC "%config(noreplace) ";
862 } elsif ($ff & (1 << 0)) {
863 print SPEC "%config ";
865 print SPEC "%doc " if $ff & (1 << 1);
866 print SPEC "%ghost " if $ff & (1 << 6);
867 print SPEC "%license " if $ff & (1 << 7);
868 print SPEC "%readme " if $ff & (1 << 8);
869 if ($fv != 4294967295) {
870 print SPEC "%verify(";
871 if ($fv & 2147483648) {
875 print SPEC "md5 " if $fv & (1 << 0);
876 print SPEC "size " if $fv & (1 << 1);
877 print SPEC "link " if $fv & (1 << 2);
878 print SPEC "user " if $fv & (1 << 3);
879 print SPEC "group " if $fv & (1 << 4);
880 print SPEC "mtime " if $fv & (1 << 5);
881 print SPEC "mode " if $fv & (1 << 6);
882 print SPEC "rdev " if $fv & (1 << 7);
885 #sigh, no POSIX::S_ISLNK ...
886 if (($fm & 0170000) == 0120000) {
887 printf SPEC "%%attr(-,%s,%s) ", $res{'FILEUSERNAME'}->[$fi], $res{'FILEGROUPNAME'}->[$fi];
889 printf SPEC "%%attr(%03o,%s,%s) ", $fm & 07777, $res{'FILEUSERNAME'}->[$fi], $res{'FILEGROUPNAME'}->[$fi];
891 if ($cfiles{$file}) {
894 print SPEC "$configdir/$fn\n";
897 print SPEC "$moves{$file}\n";
899 print SPEC "$prefix$file\n";
903 for (keys %symlinks) {
904 printf SPEC "%%attr(-,root,root) $_\n";
907 if ($res{'CHANGELOGTEXT'}) {
908 print SPEC "\n%changelog -n $targetname\n";
909 my @ct = @{$res{'CHANGELOGTIME'}};
910 my @cn = @{$res{'CHANGELOGNAME'}};
911 my @wdays = qw{Sun Mon Tue Wed Thu Fri Sat};
912 my @months = qw{Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec};
913 for my $cc (@{$res{'CHANGELOGTEXT'}}) {
914 my @lt = localtime($ct[0]);
919 printf SPEC "* %s %s %02d %04d %s\n%s\n", $wdays[$lt[6]], $months[$lt[4]], $lt[3], 1900 + $lt[5], $cn2, $cc2;
925 close(SPEC) || die("$specfile: $!\n");
926 print "$rname($target): running build...\n";
927 if (system("rpmbuild -bb $specfile".($verbose ? '' : '>/dev/null 2>&1'))) {
928 print "rpmbuild failed: $?\n";
929 print "re-running in verbose mode:\n";
930 system("rpmbuild -bb $specfile 2>&1");
938 ################################################################
943 require Parse::DebControl;
946 print "mkbaselibs needs the perl module Parse::DebControl\n".
947 "Error. baselibs-deb.conf specified but mkbaselibs can't run\n".
948 "Please ensure that 'osc meta prjconf' contains the following line:\n".
949 " Support: libparse-debcontrol-perl\n";
955 # look in the config file to see if we should be doing anything
957 # Unpack the deb control data using dpkg-deb
959 # Unpack the deb control data *and* file data using dpkg-deb
960 # process the config file for this package modifying control and moving files
961 # repackage the target deb
964 # http://www.debian.org/doc/debian-policy/ch-controlfields.html#s-binarycontrolfiles
965 # unpack the outer loop control file - this gives us eg: the arch
966 my $base = tempdir() || die("tempdir: $!\n");
967 system "dpkg -e $deb ${base}/DEBIAN" || die "dpkg -e failed on $deb";
968 my $controlParser = new Parse::DebControl;
969 $controlParser->DEBUG();
970 my $keys = $controlParser->parse_file("${base}/DEBIAN/control");
971 # print Dumper($keys);
972 # DebControl supports multiple paragraphs of control data but
973 # debian/control in a .deb only has one (whereas a debian/control
974 # in a build root contains many)
975 # So extract the ref to the first one.
976 my %control = %{@{$keys}[0]};
978 # Validate this is a binary deb and get the control data
979 my $d_name = $control{'Package'};
980 my $d_version = $control{'Version'};
982 $arch = $control{'Architecture'}; # set global $arch
985 # arch <arch> targets <target_arch>[:<target_type>] [<target_arch>[:<target_type>]...]
986 # line and get a list of target_arch-es
987 my @targets = get_targets($arch, $config);
989 print "no targets for arch $arch, skipping $d_name\n";
990 next; # there may be more debs to handle
993 for my $target (@targets) {
994 next unless parse_config($target, $d_name, $d_version);
995 die("targetname not set\n") unless $targetname; # set in the global_conf
996 $target_matched{$target} = 1;
998 my $baseTarget = "${base}/$target";
999 # Unpack a .deb to work on. We have to do this each time as we
1000 # manipulate the unpacked files.
1001 system "mkdir ${base}/$target";
1002 system "dpkg -e $deb ${baseTarget}/DEBIAN" || die "dpkg -e failed on $deb";
1003 # Note that extracting to $prefix does the clever move to /lib-x86/ or whatever
1004 system "dpkg -x $deb ${baseTarget}/$prefix" || die "dpkg -x failed on $deb";
1006 # Reset the control data
1007 $keys = $controlParser->parse_file("${baseTarget}/DEBIAN/control");
1008 %control = %{@{$keys}[0]};
1010 # Force the architecture
1011 $control{'Architecture'} = $targetarch;
1013 # Currently this script does not manipulate any files
1014 # If needed they are all unpacked in ${baseTarget}
1016 # we don't need a dsc/spec file.. all done by just moving files around
1017 # and running dpkg -b ${base} $NEW_DEB
1019 # my $dscfile = "/usr/src/packages/DSCS/mkbaselibs$$.dsc";
1021 print "$d_name($target): writing dscfile...\n";
1022 # We can Use Parse::DebControl write_file to create the new control file
1023 # just modify tags in there
1025 # We'll use requires -> Depends:
1026 map s/^"(.*)"$/$1/, @requires; # remove leading/trailing "s
1027 $control{"Depends"} = @requires ? join(", ", @requires) : ""; # join array if exists or reset it to ""
1029 map s/^"(.*)"$/$1/, @prerequires;
1030 $control{"Pre-Depends"} = @prerequires ? join(", ", @prerequires) : "";
1032 map s/^"(.*)"$/$1/, @provides;
1033 $control{"Provides"} = @provides ? join(", ", @provides) : "";
1035 map s/^"(.*)"$/$1/, @recommends;
1036 $control{"Recommends"} = @recommends ? join(", ", @recommends) : "";
1038 map s/^"(.*)"$/$1/, @suggests;
1039 $control{"Suggests"} = @suggests ? join(", ", @suggests) : "";
1041 map s/^"(.*)"$/$1/, @obsoletes;
1042 $control{"Replaces"} = @obsoletes ? join(", ", @obsoletes) : "";
1044 map s/^"(.*)"$/$1/, @conflicts;
1045 $control{"Conflicts"} = @conflicts ? join(", ", @conflicts) : "";
1047 map s/^"(.*)"$/$1/, @supplements;
1048 $control{"Enhances"} = @supplements ? join(", ", @supplements) : "";
1051 # Tidy up the various control files.
1052 # the md5sums are regenerated by dpkg-deb when building
1053 foreach my $c_file ( qw(conffiles postins postrm preinst prerm) ) {
1054 unlink "${baseTarget}/DEBIAN/$c_file";
1056 # Create them if needed
1058 map s/^"(.*)"$/$1/, @prein; # remove leading/trailing "s
1059 open(my $SCRIPT, ">${baseTarget}/DEBIAN/preinst");
1060 print $SCRIPT join("\n", @prein) ;
1061 chmod(0755, $SCRIPT);
1065 map s/^"(.*)"$/$1/, @postin;
1066 open(my $SCRIPT, ">${baseTarget}/DEBIAN/postinst");
1067 print $SCRIPT join("\n", @postin) ;
1068 chmod(0755, $SCRIPT);
1072 map s/^"(.*)"$/$1/, @preun;
1073 open(my $SCRIPT, ">${baseTarget}/DEBIAN/prerm");
1074 print $SCRIPT join("\n", @preun) ;
1075 chmod(0755, $SCRIPT);
1079 map s/^"(.*)"$/$1/, @postun;
1080 open(my $SCRIPT, ">${baseTarget}/DEBIAN/postrm");
1081 print $SCRIPT join("\n", @postun) ;
1082 chmod(0755, $SCRIPT);
1086 # Don't forget to rename the package - or it will replace/uninstall the /-based one
1087 $control{"Package"} = "${d_name}-${targettype}";
1089 $controlParser->write_file("${baseTarget}/DEBIAN/control", \%control, {clobberFile => 1, addNewline=>1 } );
1090 system "dpkg -b ${baseTarget} /usr/src/packages/DEBS/${d_name}-${targettype}_${d_version}_${targetarch}.deb" || die "dpkg -b failed on $deb";
1091 system "rm -rf ${baseTarget}";
1093 system "rm -rf ${base}";
1097 # args is a list of full pathnames to rpm/deb files
1098 die("Usage: mkbaselibs <rpms>\n") unless @ARGV;
1100 if ($ARGV[0] eq '-v') {
1104 while ($ARGV[0] eq '-c') {
1106 read_config($ARGV[0]);
1110 my %goodpkgs = map {$_ => 1} get_pkgnames(); # These are packages named in the config file
1114 for my $rpm (@pkgs) {
1117 warn ("$rpm does not exist, skipping\n");
1120 my @rpmfiles = `rpm -qp --queryformat "[%{FILENAMES}\n]" $rpm`;
1122 warn ("$rpm is empty, skipping\n");
1125 next if $rpm =~ /\.(no)?src\.rpm$/; # ignore source rpms
1126 next if $rpm =~ /\.spm$/;
1127 $rpmn =~ s/.*\///; # Remove leading path info
1128 $rpmn =~ s/-[^-]+-[^-]+\.[^\.]+\.rpm$/\.rpm/; # remove all version info
1129 $rpmn =~ s/\.rpm$//; # remove extension
1130 push @rpms, $rpm if $goodpkgs{$rpmn};
1131 if ($rpmn =~ s/-debuginfo$//) {
1132 push @debugrpms, $rpm if $goodpkgs{$rpmn};
1136 die("$_: need absolute path to package\n") unless /^\//;
1139 my %debs_to_process = map {$_ => 1} get_debpkgnames(); # These are packages named in the config file
1141 for my $deb (@pkgs) {
1143 next unless $debn =~ /\.deb$/;
1144 my @debfiles = `dpkg --contents $deb`;
1146 warn ("$deb is empty, skipping\n");
1149 $debn =~ s/.*\///; # Remove leading path info
1150 $debn =~ s/_[^_]+_[^_]+\.deb$//; # remove all version info and extension
1151 push @debs, $deb if $debs_to_process{$debn};
1152 print "ignoring $deb as $debn not in baselibs.conf\n" if !$debs_to_process{$debn};
1155 die("$_: need absolute path to package\n") unless /^\//;
1158 exit 0 unless @rpms or @debs;
1161 @filesystem = split("\n", `rpm -ql filesystem 2>/dev/null`);
1162 die("filesystem rpm is not installed\n") unless @filesystem;
1165 handle_rpms(@debugrpms);