kernel-source/mkspec

426 lines
9.7 KiB
Perl

#!/usr/bin/perl
use strict;
use warnings;
use File::Copy;
use Getopt::Long;
my $dir = ".";
my $rpmrelease = 0;
my $patches="";
GetOptions(
"patches=s" => \$patches,
"release=s" => \$rpmrelease
) or die "Usage: $0 [--release <release>] [--patches <dir>]\n";
# flavor -> [supported archs]
my %flavor_archs = parse_config_conf();
# subset to include in kernel-syms
my %syms_flavor_archs = parse_config_conf("syms");
# template name -> template body
my %templates = read_spec_templates();
# config.sh variables
my %vars = parse_config_sh();
my ($srcversion, $variant, $vanilla_only) =
($vars{'SRCVERSION'}, $vars{'VARIANT'}, $vars{'VANILLA_ONLY'});
$vanilla_only ||= "0";
# package name -> [summary, description]
my %binary_descriptions = parse_descriptions();
# arch -> flavor -> [obsoleted packages]
my %obsolete_packages = parse_old_flavors();
$patches="--patches $patches" if $patches;
my $patchversion = `$dir/compute-PATCHVERSION.sh $patches`;
chomp $patchversion;
my $rpmversion = $patchversion;
# stuff the -rcX tag into the rpm version if possible;
$rpmversion =~ s/\.0-rc/.rc/;
$rpmversion =~ s/-rc\d+//;
$rpmversion =~ s/-/./g;
$rpmrelease =~ s/-/./g;
my $sources = join("\n", $templates{source} =~ /^Source\d+:[^\n]*/msg);
# Find all SourceN: foo.tar.(bz2|xz) lines and generate the NoSource:
# lines and the %setup line
my @tarballs = ($sources =~ /^Source(\d+):[^\n]*\.tar\.(?:bz2|xz)/msg);
my $nosource = $sources;
$nosource =~ s/^Source(\d+):.*?$/NoSource: $1/mg;
# Source0 (the linux tarball) is unpacked manually
@tarballs = grep { $_ > 0 } @tarballs;
my $unpack_patches = join(" ", map { "-a $_" } @tarballs);
# List of scripts to automatically chmod +x before build
my $scripts = join(",", grep { is_script($_) }
($sources =~ /\nSource\d+:\s*([^\s]*)/mg));
my $tarball_url;
if ($srcversion =~ /^(\d+)(?:\.\d+)*(-rc\d+)?$/) {
$tarball_url = "http://www.kernel.org/pub/linux/kernel/v$1.x/";
$tarball_url .= "testing/" if $2;
} else {
# kernel.org has no tarballs for linux-next or vanilla snapshots
$tarball_url = "";
}
my $commit = get_commit();
my %macros = (
VARIANT => $variant,
VANILLA_ONLY => $vanilla_only,
SRCVERSION => $srcversion,
PATCHVERSION => $patchversion,
RPMVERSION => $rpmversion,
TARBALL_URL => $tarball_url,
RELEASE => $rpmrelease,
COMMIT => $commit,
SOURCES => $sources,
NOSOURCE => $nosource,
UNPACK_PATCHES => $unpack_patches,
SCRIPTS => $scripts,
YEAR => (localtime time)[5] + 1900,
);
# binary spec files
for my $flavor (sort keys(%flavor_archs)) {
my ($summary, $description);
if (!exists($binary_descriptions{"kernel-$flavor"})) {
print STDERR "warning: no description for kernel-$flavor found\n";
$summary = "The Linux Kernel";
$description = "The Linux Kernel.";
} else {
$summary = $binary_descriptions{"kernel-$flavor"}->[0];
$description = $binary_descriptions{"kernel-$flavor"}->[1];
}
my %obsolete_macros;
for my $subpac ("", "-base", "-extra", "-devel", "-hmac") {
(my $macro = "PROVIDES_OBSOLETES" . uc($subpac)) =~ s/-/_/;
$obsolete_macros{$macro} =
provides_obsoletes($flavor, $subpac, @{$flavor_archs{$flavor}});
}
do_spec('binary', "kernel-$flavor.spec", %macros,
FLAVOR => $flavor,
SUMMARY => $summary,
DESCRIPTION => $description,
ARCHS => join(" ", arch2rpm(@{$flavor_archs{$flavor}})),
%obsolete_macros
);
}
# kernel-source.spec
do_spec('source', "kernel-source$variant.spec", %macros);
# kernel-docs.spec
do_spec('docs', "kernel-docs$variant.spec", %macros);
# kernel-syms.spec
{
my $requires = "";
my %all_archs;
my $all_archs;
for my $flavor (sort keys(%syms_flavor_archs)) {
next if $flavor eq "vanilla";
my @archs = arch2rpm(@{$syms_flavor_archs{$flavor}});
$all_archs{$_} = 1 for @archs;
$requires .= "%ifarch @archs\n";
$requires .= "Requires: kernel-$flavor-devel = \%version-\%source_rel\n";
$requires .= "%endif\n";
}
chomp $requires;
$all_archs = join(" ", sort(keys(%all_archs)));
if (keys(%all_archs)) {
do_spec('syms', "kernel-syms$variant.spec", %macros,
REQUIRES => $requires,
ARCHS => $all_archs);
}
}
# kernel-obs-*.spec
{
my @default_archs = arch2rpm(@{$flavor_archs{default}});
# No kernel-obs-* for 32bit ppc
@default_archs = grep { $_ ne "ppc" } @default_archs;
my $default_archs = join(" ", @default_archs);
my $xen_archs = join(" ", arch2rpm(@{$flavor_archs{xen} || []}));
if (!$variant) {
do_spec('obs-build', "kernel-obs-build.spec", %macros,
ARCHS => $default_archs);
do_spec('obs-qa', "kernel-obs-qa.spec", %macros,
ARCHS => $default_archs,
NAME => "kernel-obs-qa",
FLAVOR => "default");
}
if ($xen_archs) {
do_spec('obs-qa', "kernel-obs-qa-xen.spec", %macros,
ARCHS => $xen_archs,
NAME => "kernel-obs-qa-xen",
FLAVOR => "xen");
}
}
copy_changes();
# _constraints
{
my @packages = map { "<package>kernel-$_</package>" } sort keys(%flavor_archs);
my $packages = join("\n", @packages);
do_spec('constraints', "_constraints",
BINARY_PACKAGES_XML => $packages);
}
exit 0;
sub parse_config_conf {
my @symbols = @_;
my $symbols = join(' ', @symbols);
my %res;
for my $arch (split(/\s+/, `$dir/arch-symbols --list`)) {
my @flavors = `$dir/guards $arch $symbols < $dir/config.conf`;
next if @flavors == 0;
chomp @flavors;
@flavors = map { s/.*\///; $_ } @flavors;
for my $flavor (@flavors) {
$res{$flavor} ||= [];
push(@{$res{$flavor}}, $arch);
}
}
for my $flavor (keys(%res)) {
$res{$flavor} = [sort @{$res{$flavor}}];
}
return %res;
}
sub read_spec_templates {
my %res;
for my $template (qw(binary source syms docs obs-build obs-qa)) {
xopen(my $fh, '<', "$dir/kernel-$template.spec.in");
local $/ = undef;
$res{$template} = <$fh>;
close($fh);
}
{
xopen(my $fh, '<', "$dir/constraints.in");
local $/ = undef;
$res{constraints} = <$fh>;
close($fh);
}
return %res;
}
# return a hash of config.sh variables
sub parse_config_sh {
my %res;
xopen(my $fh, '<', "$dir/config.sh");
while (<$fh>) {
chomp;
if (/^\s*([A-Z_]+)=(.*)/) {
$res{$1} = $2;
}
}
close($fh);
return %res;
}
sub parse_descriptions {
my %res;
my $current;
my $blank = "";
# 0 - expect summary, 1 - eating blank lines, 2 - reading description
my $state = 0;
xopen(my $fh, '<', "$dir/package-descriptions");
while (<$fh>) {
next if /^\s*#/;
if (/^==+\s+([^\s]+)\s+==+\s*$/) {
my $package = $1;
if ($current) {
chomp $current->[1];
}
$current = ["", ""];
$res{$package} = $current;
$state = 0;
next;
}
if (/^$/) {
if ($state == 2) {
$blank .= $_;
}
next;
}
# non-blank line and not === package ===
if ($state == 0) {
chomp;
$current->[0] = $_;
$state = 1;
} elsif ($state == 1) {
$current->[1] = $_;
$blank = "";
$state = 2;
} else {
$current->[1] .= $blank;
$blank = "";
$current->[1] .= $_;
}
}
if ($current) {
chomp $current->[1];
}
close($fh);
return %res;
}
sub parse_old_flavors{
my %res;
xopen(my $fh, '<', "$dir/old-flavors");
while (<$fh>) {
chomp;
next if /^\s*(#|$)/;
if (!m:^\s*(\w+)/([\w-]+)\s+([\w-]+)\s+([\w.-]+)\s*$:) {
print STDERR "$dir/old-flavors:$.: expected arch/flavor <old flavor> <old version>\n";
next;
}
my ($arch, $flavor, $old_flavor, $old_version) = ($1, $2, $3, $4);
$res{$arch} ||= {};
$res{$arch}{$flavor} ||= [];
push(@{$res{$arch}{$flavor}},
["kernel-$old_flavor", $old_version]);
}
close($fh);
return %res;
}
sub is_script {
my $script = shift;
return undef if $script =~ /\.(tar\.(gz|bz2)|in|conf)$/;
return undef if $script =~ /^README/;
return 1 if $script =~ /\.pl$/;
open(my $fh, '<', $script) or return undef;
sysread($fh, my $shebang, 2);
close($fh);
return 1 if $shebang eq "#!";
return undef;
}
sub arch2rpm {
if (wantarray) {
return map { _arch2rpm($_) } @_;
}
return _arch2rpm($_[0]);
}
sub _arch2rpm {
my $arch = shift;
return "\%ix86" if $arch eq "i386";
return "aarch64" if $arch eq "arm64";
return $arch;
}
sub provides_obsoletes {
my $flavor = shift;
my $subpac = shift;
my @archs = @_;
my $res = "";
for my $arch (@archs) {
my @packs = @{$obsolete_packages{$arch}{$flavor} || []};
my $printed;
next if (!@packs);
my $rpmarch = arch2rpm($arch);
chomp $rpmarch;
for my $pack (@packs) {
my $name = $pack->[0] . $subpac;
my $version = $pack->[1];
if ($subpac) {
# The -base and -extra split has been
# introduced in SLE11 (2.6.27)
my ($v1, $v2, $v3) = split(/\./, $version);
next if ($v1 <= 2 && $v2 <= 6 && $v3 < 27);
}
if (!$printed) {
$res .= "\%ifarch $rpmarch\n";
$printed = 1;
}
$res .= "Provides: $name = $version\n";
$res .= "Obsoletes: $name <= $version\n";
}
$res .= "\%endif\n" if $printed;
}
chomp $res;
return $res;
}
sub get_commit {
my ($commit, $fh);
if (!open($fh, '<', "source-timestamp")) {
print STDERR "warning: source-timestamp: $!\n";
print STDERR "warning: Cannot determine commit id\n";
return "0000000";
}
while (<$fh>) {
if (/^GIT Revision: ([0-9a-f]{7})/) {
$commit = $1;
}
}
close($fh);
if (!$commit) {
print STDERR "warning: Commit id missing in source-timestamp file\n";
return "0000000";
}
return $commit;
}
sub do_spec {
my $template = shift;
my $specfile = shift;
my %macros = @_;
my $text = $templates{$template};
my $prev_text;
do {
$prev_text = $text;
for my $m (keys %macros) {
$text =~ s/\@$m\@/$macros{$m}/g;
}
} while ($prev_text ne $text);
print "$specfile\n";
xopen(my $fh, '>', "$dir/$specfile");
print $fh $text;
close($fh);
}
sub copy_changes {
opendir(my $dh, $dir) or die "$dir: $!\n";
while (my $name = readdir $dh) {
next unless $name =~ /\.spec$/;
next if $name eq "kernel-source$variant.spec";
$name =~ s/\.spec$/.changes/;
copy("$dir/kernel-source$variant.changes", "$dir/$name");
}
closedir($dh);
}
sub xopen {
open($_[0], $_[1], $_[2]) or die "$_[2]: $!\n";
}