3
# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation, either version 3 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program. If not, see <http://www.gnu.org/licenses/>
21
use Debian::PkgKde qw(setup_datalibdir);
23
setup_datalibdir("Dpkg/Shlibs/SymbolFile.pm");
27
use File::Basename qw();
29
use Getopt::Long qw(:config noignore_case);
30
use Dpkg::ErrorHandling;
31
use Dpkg::Arch qw(get_host_arch get_valid_arches);
35
use Debian::PkgKde::SymbolsHelper::SymbolFile;
36
use Debian::PkgKde::SymbolsHelper::SymbolFileCollection;
37
use Debian::PkgKde::SymbolsHelper::Patching;
39
######## Option processing ##################
44
my $opt_arch = get_host_arch();
49
my ($opt, $arch) = @_;
50
error("unknown architecture: $arch")
51
unless grep /^\Q$arch\E$/, get_valid_arches();
56
my ($opt, $input) = @_;
58
error("input file ($input) does not exit") unless (-f $input);
62
sub get_common_options {
66
map { $args{$_} = 1 } split(//, $args);
67
$res{"output|o:s"} = \$opt_out if ($args{o});
68
$res{"backup|b!"} = \$opt_backup if ($args{b});
69
$res{"input|i=s"} = \&verify_opt_in if ($args{i});
70
$res{"package|p=s"} = \$opt_package if ($args{p});
71
$res{"architecture|a=s"} = \&verify_opt_arch if ($args{a});
72
$res{"version|v:s"} = \$opt_version if ($args{v});
73
$res{"verbose|V!"} = \$opt_verbose if ($args{V});
78
sub check_mandatory_options {
79
my ($args, $msg, @opts) = @_;
82
$msg = "" if (!defined $msg);
83
map { $args{$_} = 1 } split(//, $args);
84
error("input file option (-i) is mandatory $msg") if (!$opt_in && $args{i});
85
error("output file option (-o) is mandatory $msg") if (!$opt_out && $args{o});
86
error("backup option (-b) is mandatory $msg") if (!defined $opt_backup && $args{b});
87
error("package name option (-p) is mandatory $msg") if (!$opt_package && $args{p});
88
error("architecture option (-a) is mandatory $msg") if (!$opt_arch && $args{a});
89
error("version option (-v) is mandatory $msg") if (!$opt_version && $args{v});
92
my $val = shift @opts;
93
my $msg = shift @opts;
94
error($msg) if (!$val);
99
sub sanitize_version {
100
my ($ver, $default, $prompt) = @_;
101
my %choices = ( v => $ver );
103
if ($ver =~ m/^(.+)-(.*[^~])$/) {
104
# Non-native debian package
107
$choices{'d'} = $choices{v}.'~';
109
if ($prompt && -t STDIN) {
110
regular_print("Do you want to assign one version to all new symbols %s?",
111
($default) ? "(default is '".uc($default)."')" : "(ENTER if no)");
112
print STDERR sprintf("What version? [ %s / enter other ]: ", join(" / ",
113
(map { sprintf("%s = %s", ($_ eq ($default || '')) ? uc($_) : $_, $choices{$_}) }
114
grep { exists $choices{$_} } qw(u d v))));
115
$answer = lc(<STDIN>);
117
$answer = $choices{$answer} if exists $choices{$answer};
120
$answer = (defined $default) ? $choices{$default} : $default;
123
return (wantarray) ? (\%choices, $answer) : $answer;
126
############### Common subroutines #################
129
print STDERR sprintf($msg, @_), "\n";
133
regular_print(@_) if $opt_verbose;
136
sub find_package_symbolfile_path {
137
my ($package, $arch) = @_;
139
"debian/$package.symbols.$arch",
140
"debian/symbols.$arch",
141
"debian/$package.symbols",
144
for my $path (@PATHS) {
145
return $path if (-f $path);
151
my ($symfile, %opts) = @_;
152
return 1 unless defined $symfile;
155
my $out_file = $opt_out;
156
my $backup = $opt_backup;
158
if (defined $out_file) {
159
# If -o is specified, dump to STDOUT if it does not have an argument or
160
# save to specified file otherwise.
161
$out_fh = *STDOUT unless $out_file;
163
# If -o is not specified, save to the input file.
168
File::Copy::copy($out_file, $out_file.'~') if ($backup && -r $out_file);
169
$symfile->save($out_file, template_mode => 1, with_deprecated => 1, %opts);
171
$symfile->output($out_fh, template_mode => 1, with_deprecated => 1, %opts);
173
error("output file could not be determined");
179
my ($sym, $ver) = @_;
180
$sym->handle_min_version($ver);
183
sub get_all_packages {
184
open(CONTROL, "<", "debian/control") or syserr("unable to open debian/control");
188
if (/^Package:\s+(\S+)/) {
199
foreach my $a (sort @_) {
200
if (!defined $prev || $prev ne $a) {
208
sub print_changes_list {
209
my ($changes, $header, $print_header_sub, $print_sub) = @_;
212
&$print_header_sub($header);
213
foreach my $info (@$changes) {
214
if ($info =~ /^\s/) {
216
&$print_sub(" %s", $info);
219
&$print_sub(" SONAME: %s", $info);
225
# Load patches from supplied files
232
foreach my $patch (@_) {
233
if (!$patch->has_info()) {
234
verbose_print("* patch '%s' discarded due to absence of the info header",
238
push @patches, $patch;
244
regular_print "Looking for patches and reading them ....";
251
opendir(DIR, $f) or error("unable to open directory with patches: $f");
252
while (my $filename = readdir(DIR)) {
253
next if ($filename =~ /^.{1,2}$/);
254
my $file = File::Spec->catfile($f, $filename);
255
push @patches, _grep_patches(parse_patches_from_file($file));
259
push @patches, _grep_patches(parse_patches_from_file($f));
260
} elsif ($f eq "-") {
261
push @patches, _grep_patches(parse_patches_from_handle(\*STDIN));
269
my ($package, $version, $infile, $basefile, $patches, $confirmed_arches) = @_;
270
my @patches = @$patches;
271
my @confirmed_arches = @$confirmed_arches;
272
$basefile = $infile unless $basefile;
273
@patches = grep { $_->{package} eq $package } @patches;
275
error("no valid patches found for the '%s' package", $package)
279
my $symfile = Debian::PkgKde::SymbolsHelper::SymbolFile->new(
280
file => $infile, arch => $opt_arch
282
my $curversion = $symfile->get_confirmed_version();
283
unless ($curversion) {
284
error("input symbol file template must have 'SymbolsHelper-Confirmed' header");
287
my $base_symfile = $symfile;
288
if ($basefile ne $infile) {
289
$base_symfile = Debian::PkgKde::SymbolsHelper::SymbolFile->new(
290
file => $basefile, arch => $opt_arch
294
# Patch the base template with our patches and pick symbol files with
295
# the highest version for each architecture.
296
my (%psymfiles, %pversions);
299
regular_print("Patching symbol file '%s' with supplied patches ...",
300
$base_symfile->{file});
301
my @psymfiles = $base_symfile->patch_template(@patches);
302
foreach my $patch (@patches) {
303
my $arch = $patch->{arch};
304
my $str = sprintf("* patch '%s' for %s ... ",
305
$patch->get_name(), $arch);
306
if ($patch->is_applied() && $psymfiles[0]->get_arch() eq $arch) {
307
regular_print($str . "OK.");
309
my $psymfile = shift @psymfiles;
310
if (!exists $pversions{$arch} ||
311
version_compare($patch->{version}, $pversions{$arch}) > 0)
313
$psymfiles{$arch} = $psymfile;
314
$pversions{$arch} = $patch->{version};
315
if (!defined $latest_ver ||
316
version_compare($patch->{version}, $latest_ver) > 0)
318
$latest_ver = $patch->{version};
322
warning($str . "FAILED.");
323
info("the patch has been ignored. Failure output below:\n" .
324
$patch->get_apply_output());
327
unless (keys %psymfiles) {
328
error("no valid symbol files could be loaded from the supplied patch files");
332
unless (defined $version) {
333
$version = sanitize_version($latest_ver, undef, 1);
335
if ($curversion && version_compare($version, $curversion) < 0) {
336
error("input symbol file template version (%s) is higher than the specified one (%s)",
337
$curversion, $version);
340
# Reset version if requested and drop patched symbol files which have
341
# lower version than original one
342
foreach my $arch (keys %psymfiles) {
343
my $psymfile = $psymfiles{$arch};
345
$psymfile->set_confirmed($version, $psymfile->get_confirmed_arches());
347
my $pver = $psymfile->get_confirmed_version();
348
if (version_compare($pver, $curversion) < 0) {
349
warning("ignoring obsolete %s symbol file (its version (%s) < original (%s))",
350
$arch, $pver, $curversion);
351
delete $psymfiles{$arch};
352
delete $pversions{$arch};
355
error("no valid patched symbol files found") unless keys %psymfiles;
357
# Fork $orig symbol file for the rest (unpatched) confirmed arches.
358
my %confirmed_arches; $confirmed_arches{$_} = 1 foreach @confirmed_arches;
359
my @carches = kill_dupes(
360
grep { ! exists $psymfiles{$_} && ! exists $confirmed_arches{$_} }
361
$symfile->get_confirmed_arches()
363
@confirmed_arches = kill_dupes(
364
grep { ! exists $psymfiles{$_} } @confirmed_arches
367
# Finally create a SymbolFile collection and generate template
368
my $symfiles = new Debian::PkgKde::SymbolsHelper::SymbolFileCollection($symfile);
369
$symfiles->add_new_symfiles(values %psymfiles);
370
$symfiles->add_confirmed_arches(undef, @carches);
371
# Add assume_arches at current version
372
$symfiles->add_confirmed_arches($version, @confirmed_arches);
374
# Detect templinst symbols before substitutions and create template
375
regular_print("Confirmed arches: %s", join(", ", sort(@carches, @confirmed_arches)))
376
if @carches || @confirmed_arches;
377
regular_print("Generating symbol file template .... (this might take a while)");
378
foreach my $arch ($symfiles->get_new_arches()) {
379
my $symfile = $symfiles->get_symfile($arch);
380
foreach my $sym ($symfile->get_symbols()) {
381
$sym->mark_cpp_templinst_as_optional();
384
my $template = $symfiles->create_template()->fork();
386
# Post process template and print various information about result
387
my (@changes_new, @changes_lost, @changes_arch);
388
foreach my $soname (sort $template->get_sonames()) {
389
push @changes_new, $soname;
390
push @changes_lost, $soname;
391
push @changes_arch, $soname;
392
foreach my $sym (sort { $a->get_symboltempl() cmp $b->get_symboltempl() }
393
$template->get_symbols($soname),
394
$template->get_patterns($soname))
396
my $osym = $symfile->get_symbol_object($sym, $soname);
398
if ($sym->{deprecated} && ! $osym->{deprecated}) {
399
push @changes_lost, " ".$sym->get_symbolspec(1);
400
} elsif (! $sym->{deprecated} && $osym->{deprecated}) {
402
tweak_symbol($sym, $version);
403
push @changes_new, " ".$sym->get_symbolspec(1);
405
my $arches = $sym->get_tag_value("arch") || '';
406
my $oarches = $osym->get_tag_value("arch") || '';
408
if ($arches ne $oarches) {
410
" ".$sym->get_symbolspec(1)." (was arch=$oarches)";
415
tweak_symbol($sym, $version);
416
push @changes_new, " ".$sym->get_symbolspec(1);
419
# Pop sonames if no symbols added
420
pop @changes_new if $changes_new[$#changes_new] eq $soname;
421
pop @changes_lost if $changes_lost[$#changes_lost] eq $soname;
422
pop @changes_arch if $changes_arch[$#changes_arch] eq $soname;
425
print_changes_list(\@changes_new,
426
"there are NEW symbols (including optional):",
427
\&info, \&verbose_print) if $opt_verbose;
428
print_changes_list(\@changes_lost,
429
"there are LOST symbols (including optional):",
430
\&warning, \®ular_print);
431
print_changes_list(\@changes_arch,
432
"architecture set changed for the symbols below:",
433
\&info, \®ular_print);
435
# Finally adjust confirmed arches list
436
$template->set_confirmed($symfiles->get_latest_version(),
437
$symfiles->get_latest_arches());
441
my $tmpfile = File::Temp->new(TEMPLATE => "${opt_in}.newXXXXXX");
442
$template->output($tmpfile,
443
package => $opt_package,
445
with_deprecated => 1,
448
spawn(exec => ["diff", "-u", $symfile->{file}, $tmpfile->filename],
449
to_handle => \*STDERR, wait_child => 1, nocheck => 1);
455
############### Subcommands ####################
456
sub _create_symfile {
457
my ($file, $filename_re) = @_;
458
my $filename = File::Basename::basename($file);
460
if ($filename =~ $filename_re) {
461
regular_print("* Loading \"%s\" symbol file '%s' ...", $1, $file);
462
$symfile = Debian::PkgKde::SymbolsHelper::SymbolFile->new(
467
warning("%s is not named properly. Expected *_<arch> or *.<arch>", $file);
472
sub subcommand_create {
474
get_common_options("oav"),
476
if (GetOptions(%opts)) {
477
unless (defined $opt_out) {
478
error("output file option (-o) is mandatory");
482
my @input_files = @ARGV;
484
my $str_arches = join("|", get_valid_arches());
485
my $filename_re = qr/.*?[_.]($str_arches)$/;
487
unless (@input_files) {
488
error("please pass files/directory with arch specific symbol files");
490
for my $f (@input_files) {
492
opendir(DIR, $f) or error("unable to open directory: $f");
493
while (my $filename = readdir(DIR)) {
494
next if ($filename =~ /^.{1,2}$/);
495
my $file = File::Spec->catfile($f, $filename);
496
if (my $symfile = _create_symfile($file, $filename_re)) {
497
if (exists $symfiles{$symfile->get_arch()}) {
498
error("duplicate symbol file (%s) for arch %s", $file,
499
$symfile->get_arch());
501
$symfiles{$symfile->get_arch()} = $symfile;
506
if (my $symfile = _create_symfile($f, $filename_re)) {
507
if (exists $symfiles{$symfile->get_arch()}) {
508
error("duplicate symbol file (%s) for arch (%s)", $f,
509
$symfile->get_arch());
511
$symfiles{$symfile->get_arch()} = $symfile;
514
error("unreadale file/directory: %s", $f);
518
if (scalar(keys %symfiles) > 0) {
519
unless (exists $symfiles{$opt_arch}) {
520
error("symbol file for the specified arch (%s) could not be found/loaded. ".
521
"Please specify another arch with -a option", $opt_arch);
523
# Set confirmed version
524
unless ($opt_version) {
525
my $ver = $symfiles{$opt_arch}->get_highest_version();
526
$opt_version = sanitize_version($ver, 'd', 1) || $ver;
528
foreach my $symfile (values %symfiles) {
529
$symfile->set_confirmed($opt_version, $symfile->get_arch());
532
# Create collection and generate template
533
my $orig_symfile = $symfiles{$opt_arch}->fork();
534
delete $symfiles{$opt_arch};
535
my $symfiles = Debian::PkgKde::SymbolsHelper::SymbolFileCollection->new($orig_symfile);
536
$symfiles->add_new_symfiles(values %symfiles);
537
$symfiles->add_confirmed_arches(undef, $opt_arch);
539
# Detect templinst symbols before substitutions and create template
540
regular_print("Generating symbol file template .... (this might take a while)");
541
foreach my $symfile ($orig_symfile, $symfiles->get_symfiles()) {
542
foreach my $sym ($symfile->get_symbols()) {
543
$sym->mark_cpp_templinst_as_optional();
546
my $template = $symfiles->create_template();
548
# Set confirmed header
549
$template->set_confirmed($symfiles->get_latest_version(),
550
$symfiles->get_latest_arches());
552
foreach my $sym ($template->get_symbols(),
553
$template->get_patterns())
555
tweak_symbol($sym, $opt_version);
558
return out_symfile($template);
560
error("no properly named symbol files located");
567
sub subcommand_patch {
569
my $opt_confirmed_arches = '';
572
get_common_options("obipavV"),
573
"file-to-patch|f=s" => \$opt_file2patch,
574
"confirmed-arches|c=s" => \$opt_confirmed_arches,
576
if (GetOptions(%opts)) {
577
check_mandatory_options("p", "");
578
@input_patches = @ARGV;
580
$opt_in = find_package_symbolfile_path($opt_package, $opt_arch);
581
error("symbol template file was not found for package '$opt_package'")
582
unless (defined $opt_in && -r $opt_in);
584
push @input_patches, "-" unless @input_patches;
586
my @patches = load_patches(@input_patches);
587
my @confirmed_arches = split(/[\s,]+/, $opt_confirmed_arches);
588
error("no valid patches found.") unless @patches;
590
patch_symfile($opt_package, $opt_version, $opt_in, $opt_file2patch,
591
\@patches, \@confirmed_arches)
597
sub subcommand_batchpatch {
600
my $opt_continue_on_err;
601
my $opt_confirmed_arches = '';
602
my $failed_packages = 0;
605
get_common_options("bavV"),
606
"package|p=s" => \@opt_packages,
607
"continue-on-error!" => \$opt_continue_on_err,
608
"confirmed-arches|c=s" => \$opt_confirmed_arches,
610
if (GetOptions(%opts)) {
611
@input_patches = @ARGV;
612
push @input_patches, "-" unless @input_patches;
613
my @patches = load_patches(@input_patches);
614
my @confirmed_arches = split(/[\s,]+/, $opt_confirmed_arches);
615
my %packages = map({ $_->{package} => 1 }
616
grep { defined $_->{package} } @patches);
620
foreach my $package (@opt_packages) {
621
my @pkgs = split(/[\s\n]+/sm, $package);
622
push @packages, @pkgs;
625
@packages = grep { exists $packages{$_} } get_all_packages();
628
error("no valid patches found") unless @patches;
629
error("no packages specified or none to patch for this source")
632
foreach my $package (@packages) {
633
my $msg = sprintf("| Processing %s package |", $package);
634
regular_print("-" x length($msg));
636
regular_print("-" x length($msg));
637
if (my $infile = find_package_symbolfile_path($package, $opt_arch)) {
640
$template = patch_symfile($package, $opt_version, $infile, undef,
641
\@patches, \@confirmed_arches);
644
if ($opt_continue_on_err) {
646
info("%s patching FAILED. Continuing with subsequent package if any.", $package);
650
error("%s patching FAILED. Will NOT continue.", $package);
655
out_symfile($template);
658
regular_print("* UNABLE to find symbol file for %s", $package);
661
return $failed_packages;
666
sub subcommand_rewrite {
667
my $opt_template = 1;
670
get_common_options("boiav"),
671
"template!" => \$opt_template,
672
"convert" => \$opt_convert,
674
if (GetOptions(%opts)) {
675
check_mandatory_options("i");
678
my $symfile = Debian::PkgKde::SymbolsHelper::SymbolFile->new(file => $opt_in, arch => $opt_arch);
680
template_mode => $opt_template
683
foreach my $sym ($symfile->get_symbols()) {
684
$sym->upgrade_virtual_table_symbol($opt_arch) if ($opt_convert);
685
tweak_symbol($sym, $opt_version);
687
foreach my $pat ($symfile->get_patterns()) {
688
tweak_symbol($pat, $opt_version);
691
return out_symfile($symfile->fork(), %o);
693
error("input symbol file ($opt_in) not found");
700
# Boilerplate for the common subcommand handler
701
sub subcommand_boilerplate {
703
get_common_options("obipav"),
705
if (GetOptions(%opts)) {
706
# check_mandatory_options("o");
713
"create" => [ 1, \&subcommand_create, "create symbol file template" ],
714
"patch" => [ 2, \&subcommand_patch, "apply dpkg-gensymbols patch(es) to the symbol file template" ],
715
"batchpatch" => [ 3, \&subcommand_batchpatch, "apply dpkg-gensymbols patches to multiple packages at once" ],
716
"rewrite" => [ 4, \&subcommand_rewrite, "filter/rewrite symbol file" ],
719
report_options(info_fh => \*STDERR);
721
my $curcmd = shift @ARGV;
722
if (defined $curcmd && exists $SUBCOMMANDS{$curcmd}) {
723
my $ret = &{$SUBCOMMANDS{$curcmd}->[1]}();
727
$err = ($curcmd) ? "unrecognized subcommand '$curcmd'." : "subcommand was not specified.";
728
errormsg($err . " Valid subcommands are:");
730
for my $cmd (sort({ $SUBCOMMANDS{$a}->[0] <=> $SUBCOMMANDS{$b}->[0] }
731
keys %SUBCOMMANDS)) {
732
# Display command and its short help
733
regular_print(" %s - %s", $cmd, $SUBCOMMANDS{$cmd}->[2]);
738
# vi: noexpandtab shiftwidth=4 tabstop=8