Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
Please login to access the resource
openSUSE:Leap:42.3
pesign-obs-integration
pesign-gen-repackage-spec
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File pesign-gen-repackage-spec of Package pesign-obs-integration
#!/usr/bin/perl # Given a set of rpm packages and directory with their new content, # generate a specfile that generates new packages # # Copyright (c) 2013 SUSE Linux Products GmbH, Nuernberg, Germany. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA use strict; use warnings; my $USAGE = "Usage: $0 --directory <payload directory> rpm...\n"; use Getopt::Long; use Fcntl qw(:mode :seek); my $directory; my $output = "."; my $cert_subpackage; my $kmp_basename; my @rpms; $ENV{LC_ALL} = "en_US.UTF-8"; GetOptions( "help|h" => sub { print $USAGE; exit; }, "directory|d=s" => \$directory, "output|o=s" => \$output, "cert-subpackage|c=s" => \$cert_subpackage, ) or die $USAGE; @rpms = @ARGV; if (!@rpms) { print STDERR "$0: No packages given\n"; die $USAGE; } if (!$directory || substr($directory, 0, 1) ne '/' || ! -d $directory) { print STDERR "$0: --directory must be an absolute path\n"; die $USAGE; } sub query_array { my ($rpm, @tags) = @_; my @res; my $format = "[" . join("|", map { "\%{$_}" } @tags) . "\\n]"; open(my $fh, '-|', "rpm", "-qp", "--qf", $format, $rpm) or die "rpm: $!\n"; while (<$fh>) { chomp; my @t = split(/\|/, $_, -1); push(@res, \@t); } close($fh); return @res; } sub query_multiline_array { my ($rpm, $tag) = @_; my @res; my $delim = "|||"; # XXX - dangerous my $format = "[$delim\\n\%{$tag}\\n]"; open(my $fh, '-|', "rpm", "-qp", "--qf", $format, $rpm) or die "rpm: $!\n"; my $line = <$fh>; return unless $line; chomp($line); return if $line eq "(none)"; die "Expected \"$delim\" at beginning of rpm output, got \"$line\"" if $line ne $delim; my $cur = ""; while ($line = <$fh>) { chomp($line); if ($line eq $delim) { $cur = "" if $cur eq "\n"; push(@res, $cur); $cur = ""; } else { $cur .= $line . "\n"; } } $cur = "" if $cur eq "\n"; push(@res, $cur); close($fh); return @res; } sub query_single { my ($rpm, $tag) = @_; my $res; open(my $fh, '-|', "rpm", "-qp", "--qf", "\%{$tag}\\n", $rpm) or die "rpm: $!\n"; { local $/ = undef; $res = <$fh>; } chomp $res; if ($res eq "(none)") { $res = ""; } close($fh); return $res; } # specfile dependency => rpm tag name my %dep2tag = ( conflicts => "conflict", obsoletes => "obsolete", provides => "provide", requires => "require", suggests => "suggest", enhances => "enhance", recommends => "recommend", supplements => "supplement", ); # specfile scriptlet => rpm tag name my %script2tag = ( pre => "prein", post => "postin", preun => "preun", postun => "postun", pretrans => "pretrans", posttrans => "posttrans", verifyscript => "verifyscript", # FIXME: triggers ); # tags which are printed verbatim in the specfile my @simple_tags = qw(version release license group summary packager vendor url distribution); sub load_package { my $rpm = shift; my %res; for my $tag (qw(name arch sourcerpm description), @simple_tags) { $res{$tag} = query_single($rpm, $tag); } my @files; my @list = query_array($rpm, qw(filenames fileflags filemodes fileusername filegroupname filesizes filemtimes filelinktos fileverifyflags)); for my $file (@list) { my $new = { name => $file->[0], flags => $file->[1], mode => $file->[2], owner => $file->[3], group => $file->[4], size => $file->[5], mtime => $file->[6], target => $file->[7], verify => $file->[8], }; push(@files, $new); if ($new->{name} =~ /\.ko$/ && S_ISREG($new->{mode})) { $res{is_kmp} = 1; } } $res{files} = \@files; while (my ($dep, $tag) = each(%dep2tag)) { my @deps; my @list = query_array($rpm, "${tag}name", "${tag}flags", "${tag}version"); for my $d (@list) { next if $d->[0] eq "(none)"; push(@deps, { name => $d->[0], flags => $d->[1], version => $d->[2], }); } $res{$dep} = \@deps; } while (my ($script, $tag) = each(%script2tag)) { my $interp = query_single($rpm, "${tag}prog"); next unless $interp; my $s = query_single($rpm, $tag); $res{$script} = { interp => $interp, script => $s, }; } my @triggers = query_array($rpm, qw(triggertype triggerscriptprog triggerconds)); my @triggerscripts = query_multiline_array($rpm, "triggerscripts"); if (scalar(@triggers) != scalar(@triggerscripts)) { die "# of %%{triggertype} tags (" . scalar(@triggers) . ") != # of %%{triggerscripts} tags (" . scalar(@triggerscripts) . ")"; } for (my $i = 0; $i < scalar(@triggers); $i++) { $res{triggers} ||= []; push(@{$res{triggers}}, { type => $triggers[$i]->[0], interp => $triggers[$i]->[1], conds => $triggers[$i]->[2], script => $triggerscripts[$i], }); } open(my $fh, '-|', "rpm", "-qp", "--changelog", $rpm) or die "rpm: $!\n"; { local $/ = undef; my $changelog = <$fh>; close($fh); $res{changelog} = $changelog; } return \%res; } # quote percent signs in text sub quote { my $text = shift; $text =~ s/%/%%/g; return $text; } sub print_script { my ($file, $script) = @_; return unless $script->{script}; open(my $fh, '>', "$output/$file") or die "$output/$file: $!\n"; print $fh $script->{script}; close($fh); print SPEC " -f $file"; } sub print_package { my ($p, $is_main) = @_; if ($is_main) { print SPEC "Name: $p->{name}\n"; print SPEC "Buildroot: $directory\n"; print SPEC "\%define _use_internal_dependency_generator 0\n"; print SPEC "\%define __find_provides %{nil}\n"; print SPEC "\%define __find_requires %{nil}\n"; print SPEC "\%define __find_supplements %{nil}\n"; if ($p->{nosource}) { # We do not generate any no(src).rpm, but we want the # %{sourcerpm} tag in the binary packages to match. # So we add a dummy source and mark it as nosource. print SPEC "Source0: repackage.spec\n"; print SPEC "NoSource: 0\n"; } } else { print SPEC "\%package -n $p->{name}\n"; } for my $tag (@simple_tags) { next if $p->{$tag} eq ""; print SPEC "$tag: " . quote($p->{$tag}) . "\n"; } print SPEC "BuildArch: noarch\n" if $p->{arch} eq "noarch"; for my $dep (sort(keys(%dep2tag))) { print_deps($dep, $p->{$dep}); } if ($cert_subpackage && $p->{is_kmp}) { print SPEC "Requires: $kmp_basename-ueficert\n"; } print SPEC "\%description -n $p->{name}\n"; print SPEC quote($p->{description}) . "\n\n"; for my $script (sort(keys(%script2tag))) { next unless $p->{$script}; print SPEC "\%$script -p $p->{$script}{interp} -n $p->{name}"; print_script("$script-$p->{name}", $p->{$script}); print SPEC "\n"; } my $i = 0; for my $trigger (@{$p->{triggers}}) { print SPEC "\%trigger$trigger->{type} -p $trigger->{interp} -n $p->{name}"; print_script("trigger$i-$p->{name}", $trigger); print SPEC " -- $trigger->{conds}\n"; $i++; } if ($p->{files}) { print SPEC "\%files -n $p->{name}\n"; print_files($p->{files}); } print SPEC "\n"; } # /usr/include/rpm/rpmds.h my %deptypes = ( pre => (1 << 9), post => (1 << 10), preun => (1 << 11), postun => (1 << 12), verify => (1 << 13), ); my %depflags = ( "<" => (1 << 1), ">" => (1 << 2), "=" => (1 << 3), rpmlib => (1 << 24), ); sub print_deps { my ($depname, $list) = @_; foreach my $d (@$list) { next if ($d->{flags} & $depflags{rpmlib}); print SPEC $depname; my @deptypes; while (my ($type, $bit) = each(%deptypes)) { push(@deptypes, $type) if $d->{flags} & $bit; } print SPEC "(", join(",", @deptypes), ")" if @deptypes; print SPEC ": "; print SPEC quote($d->{name}); if ($d->{version}) { print SPEC " "; for my $op (qw(< > =)) { print SPEC $op if $d->{flags} & $depflags{$op}; } print SPEC " " . quote($d->{version}); } print SPEC "\n"; } } # /usr/include/rpm/rpmfi.h my %filetypes = ( config => (1 << 0), doc => (1 << 1), missingok => (1 << 3), noreplace => (1 << 4), ghost => (1 << 6), ); my %verifyflags = ( filedigest=> (1 << 0), size => (1 << 1), link => (1 << 2), user => (1 << 3), group => (1 << 4), mtime => (1 << 5), mode => (1 << 6), rdev => (1 << 7), caps => (1 << 8), ); sub print_files { my $files = shift; for my $f (@$files) { my $path = "$directory/$f->{name}"; my $attrs = ""; # Fix mtime of directories, which cpio -idm fails to preserve if (S_ISDIR($f->{mode})) { $attrs .= "\%dir "; utime($f->{mtime}, $f->{mtime}, $path); } $attrs .= sprintf('%%attr(%04o, %s, %s) ', ($f->{mode} & 0777), $f->{owner}, $f->{group}); if ($f->{flags} & $filetypes{config}) { $attrs .= "%config "; my @cfg_attrs; for my $attr (qw(missingok noreplace)) { next unless $f->{flags} & $filetypes{$attr}; push(@cfg_attrs, $attr); } $attrs .= "(" . join(",", @cfg_attrs) . ")" if @cfg_attrs; } $attrs .= "%doc " if $f->{flags} & $filetypes{doc}; if ($f->{flags} & $filetypes{ghost}) { $attrs .= "%ghost "; if (S_ISREG($f->{mode})) { open(my $fh, '>', $path) or die "$path: $!\n"; if ($f->{size} > 0) { sysseek($fh, $f->{size} - 1, SEEK_SET); syswrite($fh, ' ', 1); } close($fh); utime($f->{mtime}, $f->{mtime}, $path); } elsif (S_ISLNK($f->{mode})) { symlink($f->{target}, $path); } } # mtime of symlinks is also not preserved by cpio if (S_ISLNK($f->{mode})) { # perl core does not provide lutimes()/utimensat() system("touch", "-h", "-d\@$f->{mtime}", $path); } my $verify_attrs = ""; for my $flag (sort(keys(%verifyflags))) { if (!($f->{verify} & $verifyflags{$flag})) { $verify_attrs .= "$flag "; } } if ($verify_attrs) { $attrs .= "%verify(not $verify_attrs) "; } print SPEC "$attrs " . quote($f->{name}) . "\n"; if (-e "$path.sig") { print SPEC "$attrs " . quote($f->{name}) . ".sig\n"; } } } my %packages; for my $rpm (@rpms) { my $p = load_package($rpm); $packages{$p->{name}} = $p; } my $sourcerpm; for my $p (values(%packages)) { $sourcerpm = $p->{sourcerpm} unless $sourcerpm; if ($p->{sourcerpm} ne $sourcerpm) { die "Error: packages built from different source rpm: $sourcerpm vs $p->{sourcerpm}\n"; } } if ($sourcerpm !~ /^(.+)-([^-]+)-([^-]+)\.(no)?src\.rpm$/) { die "Error: malformed %{sourcerpm} tag: $sourcerpm\n"; } my ($main_name, $main_ver, $main_rel, $nosrc) = ($1, $2, $3, $4); if (!exists($packages{$main_name})) { # create an empty main package my $first = (sort(keys(%packages)))[0]; $packages{$main_name} = { name => $main_name, version => $main_ver, release => $main_rel, }; for my $tag (qw(description changelog arch), @simple_tags) { next if $packages{$main_name}->{$tag}; $packages{$main_name}->{$tag} = $packages{$first}->{$tag}; } } $packages{$main_name}->{nosource} = $nosrc ? 1 : 0; # Find out the basename of <name>-kmp-<flavor>, falling back to the # main package name for my $p (values(%packages)) { next unless $p->{is_kmp}; (my $n = $p->{name}) =~ s/-kmp-.*//; $kmp_basename = $n unless $kmp_basename; if ($n ne $kmp_basename) { $kmp_basename = undef; last; } } $kmp_basename = $main_name unless $kmp_basename; open(SPEC, '>', "$output/repackage.spec") or die "$output/repackage.spec: $!\n"; print_package($packages{$main_name}, 1); for my $name (sort(keys(%packages))) { next if $name eq $main_name; print_package($packages{$name}, 0); } if ($cert_subpackage) { my $certdir = "/etc/uefi/certs"; my $certs = ""; if (-d "$directory/$certdir") { opendir(my $dh, "$directory/$certdir") or die "$directory/$certdir"; while (my $cert = readdir($dh)) { next if $cert =~ /^\.\.?$/; if ($cert !~ /\.crt$/) { print STDERR "warning: Ignoring $directory/$certdir/$cert (no .crt suffix)\n"; next; } $certs .= " $certdir/$cert"; } } if (!$certs) { print STDERR "warning: --cert-subpackage specified, but no certs found in $directory/$certdir\n"; } local $/ = undef; open(my $fh, '<', $cert_subpackage) or die "$cert_subpackage: $!\n"; my $template = <$fh>; close($fh); $template =~ s/\%{-n\*}/$kmp_basename/g; $template =~ s/\@CERTS\@/$certs/g; print SPEC $template; } print SPEC "\%changelog\n"; print SPEC quote($packages{$main_name}->{changelog}); close(SPEC);
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor