~shnatsel/+junk/pkg-kde-tools-backport

« back to all changes in this revision

Viewing changes to pkgkde-symbolshelper

  • Committer: Sergey "Shnatsel" Davidoff
  • Date: 2016-04-18 20:56:33 UTC
  • Revision ID: shnatsel@gmail.com-20160418205633-i7sh6o3o6yzm410a
Initial import of version 0.15.16ubuntu2 from vivid

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/perl -w
 
2
 
 
3
# Copyright (C) 2008-2010 Modestas Vainius <modax@debian.org>
 
4
#
 
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.
 
9
#
 
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.
 
14
#
 
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/>
 
17
 
 
18
use strict;
 
19
use warnings;
 
20
 
 
21
use Debian::PkgKde qw(setup_datalibdir);
 
22
BEGIN {
 
23
    setup_datalibdir("Dpkg/Shlibs/SymbolFile.pm");
 
24
}
 
25
 
 
26
use File::Spec;
 
27
use File::Basename qw();
 
28
use File::Copy qw();
 
29
use Getopt::Long qw(:config noignore_case);
 
30
use Dpkg::ErrorHandling;
 
31
use Dpkg::Arch qw(get_host_arch get_valid_arches);
 
32
use Dpkg::Version;
 
33
use Dpkg::IPC;
 
34
 
 
35
use Debian::PkgKde::SymbolsHelper::SymbolFile;
 
36
use Debian::PkgKde::SymbolsHelper::SymbolFileCollection;
 
37
use Debian::PkgKde::SymbolsHelper::Patching;
 
38
 
 
39
######## Option processing ##################
 
40
my $opt_out;
 
41
my $opt_backup;
 
42
my $opt_in;
 
43
my $opt_package;
 
44
my $opt_arch = get_host_arch();
 
45
my $opt_version;
 
46
my $opt_verbose;
 
47
 
 
48
sub verify_opt_arch {
 
49
    my ($opt, $arch) = @_;
 
50
    error("unknown architecture: $arch")
 
51
        unless grep /^\Q$arch\E$/, get_valid_arches();
 
52
    $opt_arch = $arch;
 
53
}
 
54
 
 
55
sub verify_opt_in {
 
56
    my ($opt, $input) = @_;
 
57
 
 
58
    error("input file ($input) does not exit") unless (-f $input);
 
59
    $opt_in = $input;
 
60
}
 
61
 
 
62
sub get_common_options {
 
63
    my $args = shift;
 
64
    my (%args, %res);
 
65
 
 
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});
 
74
 
 
75
    return %res;
 
76
}
 
77
 
 
78
sub check_mandatory_options {
 
79
    my ($args, $msg, @opts) = @_;
 
80
    my %args;
 
81
 
 
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});
 
90
 
 
91
    while (@opts) {
 
92
        my $val = shift @opts;
 
93
        my $msg = shift @opts;
 
94
        error($msg) if (!$val);
 
95
    }
 
96
    return 1;
 
97
}
 
98
 
 
99
sub sanitize_version {
 
100
    my ($ver, $default, $prompt) = @_;
 
101
    my %choices = ( v => $ver );
 
102
    my $answer;
 
103
    if ($ver =~ m/^(.+)-(.*[^~])$/) {
 
104
        # Non-native debian package
 
105
        $choices{u} = $1;
 
106
    }
 
107
    $choices{'d'} = $choices{v}.'~';
 
108
 
 
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>);
 
116
        chop $answer;
 
117
        $answer = $choices{$answer} if exists $choices{$answer};
 
118
    }
 
119
    if (! $answer) {
 
120
        $answer = (defined $default) ? $choices{$default} : $default;
 
121
    }
 
122
 
 
123
    return (wantarray) ? (\%choices, $answer) : $answer;
 
124
}
 
125
 
 
126
############### Common subroutines #################
 
127
sub regular_print {
 
128
    my $msg = shift;
 
129
    print STDERR sprintf($msg, @_), "\n";
 
130
}
 
131
 
 
132
sub verbose_print {
 
133
    regular_print(@_) if $opt_verbose;
 
134
}
 
135
 
 
136
sub find_package_symbolfile_path {
 
137
    my ($package, $arch) = @_;
 
138
    my @PATHS = (
 
139
        "debian/$package.symbols.$arch",
 
140
        "debian/symbols.$arch",
 
141
        "debian/$package.symbols",
 
142
        "debian/symbols"
 
143
    );
 
144
    for my $path (@PATHS) {
 
145
        return $path if (-f $path);
 
146
    }
 
147
    return undef;
 
148
}
 
149
 
 
150
sub out_symfile {
 
151
    my ($symfile, %opts) = @_;
 
152
    return 1 unless defined $symfile;
 
153
 
 
154
    my $out_fh;
 
155
    my $out_file = $opt_out;
 
156
    my $backup = $opt_backup;
 
157
 
 
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;
 
162
    } else {
 
163
        # If -o is not specified, save to the input file.
 
164
        $out_file = $opt_in;
 
165
    }
 
166
 
 
167
    if ($out_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);
 
170
    } elsif ($out_fh) {
 
171
        $symfile->output($out_fh, template_mode => 1, with_deprecated => 1, %opts);
 
172
    } else {
 
173
        error("output file could not be determined");
 
174
    }
 
175
    return 0;
 
176
}
 
177
 
 
178
sub tweak_symbol {
 
179
    my ($sym, $ver) = @_;
 
180
    $sym->handle_min_version($ver);
 
181
}
 
182
 
 
183
sub get_all_packages {
 
184
    open(CONTROL, "<", "debian/control") or syserr("unable to open debian/control");
 
185
    my @packages;
 
186
    while (<CONTROL>) {
 
187
        chop;
 
188
        if (/^Package:\s+(\S+)/) {
 
189
            push @packages, $1;
 
190
        }
 
191
    }
 
192
    close(CONTROL);
 
193
    return @packages;
 
194
}
 
195
 
 
196
sub kill_dupes {
 
197
    my $prev;
 
198
    my @res;
 
199
    foreach my $a (sort @_) {
 
200
        if (!defined $prev || $prev ne $a) {
 
201
            push @res, $a;
 
202
        }
 
203
        $prev = $a;
 
204
    }
 
205
    return @res;
 
206
}
 
207
 
 
208
sub print_changes_list {
 
209
    my ($changes, $header, $print_header_sub, $print_sub) = @_;
 
210
 
 
211
    if (@$changes) {
 
212
        &$print_header_sub($header);
 
213
        foreach my $info (@$changes) {
 
214
            if ($info =~ /^\s/) {
 
215
                    # Symbol
 
216
                    &$print_sub(" %s", $info);
 
217
            } else {
 
218
                # Soname
 
219
                &$print_sub(" SONAME: %s", $info);
 
220
            }
 
221
        }
 
222
    }
 
223
}
 
224
 
 
225
# Load patches from supplied files
 
226
sub load_patches {
 
227
    my @files=@_;
 
228
    my @patches;
 
229
 
 
230
    sub _grep_patches {
 
231
        my @patches;
 
232
        foreach my $patch (@_) {
 
233
            if (!$patch->has_info()) {
 
234
                verbose_print("* patch '%s' discarded due to absence of the info header",
 
235
                    $patch->get_name());
 
236
                next;
 
237
            }
 
238
            push @patches, $patch;
 
239
        }
 
240
        return @patches;
 
241
    };
 
242
 
 
243
    if (@files) {
 
244
        regular_print "Looking for patches and reading them ....";
 
245
    } else {
 
246
        return ();
 
247
    }
 
248
 
 
249
    for my $f (@files) {
 
250
        if (-d $f) {
 
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));
 
256
            }
 
257
            closedir DIR;
 
258
        } elsif (-r $f) {
 
259
            push @patches, _grep_patches(parse_patches_from_file($f));
 
260
        } elsif ($f eq "-") {
 
261
            push @patches, _grep_patches(parse_patches_from_handle(\*STDIN));
 
262
        }
 
263
    }
 
264
 
 
265
    return @patches;
 
266
}
 
267
 
 
268
sub patch_symfile {
 
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;
 
274
 
 
275
    error("no valid patches found for the '%s' package", $package)
 
276
        unless (@patches);
 
277
 
 
278
    # Load input symfile
 
279
    my $symfile = Debian::PkgKde::SymbolsHelper::SymbolFile->new(
 
280
        file => $infile, arch => $opt_arch
 
281
    );
 
282
    my $curversion = $symfile->get_confirmed_version();
 
283
    unless ($curversion) {
 
284
        error("input symbol file template must have 'SymbolsHelper-Confirmed' header");
 
285
    }
 
286
 
 
287
    my $base_symfile = $symfile;
 
288
    if ($basefile ne $infile) {
 
289
        $base_symfile = Debian::PkgKde::SymbolsHelper::SymbolFile->new(
 
290
            file => $basefile, arch => $opt_arch
 
291
        );
 
292
    }
 
293
 
 
294
    # Patch the base template with our patches and pick symbol files with
 
295
    # the highest version for each architecture.
 
296
    my (%psymfiles, %pversions);
 
297
    my $latest_ver;
 
298
 
 
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.");
 
308
 
 
309
            my $psymfile = shift @psymfiles;
 
310
            if (!exists $pversions{$arch} ||
 
311
                version_compare($patch->{version}, $pversions{$arch}) > 0)
 
312
            {
 
313
                $psymfiles{$arch} = $psymfile;
 
314
                $pversions{$arch} = $patch->{version};
 
315
                if (!defined $latest_ver ||
 
316
                    version_compare($patch->{version}, $latest_ver) > 0)
 
317
                {
 
318
                    $latest_ver = $patch->{version};
 
319
                }
 
320
            }
 
321
        } else {
 
322
            warning($str . "FAILED.");
 
323
            info("the patch has been ignored. Failure output below:\n" .
 
324
                $patch->get_apply_output());
 
325
        }
 
326
    }
 
327
    unless (keys %psymfiles) {
 
328
        error("no valid symbol files could be loaded from the supplied patch files");
 
329
    }
 
330
 
 
331
    # Sanitize version
 
332
    unless (defined $version) {
 
333
        $version = sanitize_version($latest_ver, undef, 1);
 
334
    }
 
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);
 
338
    }
 
339
 
 
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};
 
344
        if ($version) {
 
345
            $psymfile->set_confirmed($version, $psymfile->get_confirmed_arches());
 
346
        }
 
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};
 
353
        }
 
354
    }
 
355
    error("no valid patched symbol files found") unless keys %psymfiles;
 
356
 
 
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()
 
362
    );
 
363
    @confirmed_arches = kill_dupes(
 
364
        grep { ! exists $psymfiles{$_} } @confirmed_arches
 
365
    );
 
366
 
 
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);
 
373
 
 
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();
 
382
        }
 
383
    }
 
384
    my $template = $symfiles->create_template()->fork();
 
385
 
 
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))
 
395
        {
 
396
            my $osym = $symfile->get_symbol_object($sym, $soname);
 
397
            if (defined $osym) {
 
398
                if ($sym->{deprecated} && ! $osym->{deprecated}) {
 
399
                    push @changes_lost, " ".$sym->get_symbolspec(1);
 
400
                } elsif (! $sym->{deprecated} && $osym->{deprecated}) {
 
401
                    # Tweak symbol
 
402
                    tweak_symbol($sym, $version);
 
403
                    push @changes_new, " ".$sym->get_symbolspec(1);
 
404
                } else {
 
405
                    my $arches = $sym->get_tag_value("arch") || '';
 
406
                    my $oarches = $osym->get_tag_value("arch") || '';
 
407
 
 
408
                    if ($arches ne $oarches) {
 
409
                        push @changes_arch,
 
410
                            " ".$sym->get_symbolspec(1)." (was arch=$oarches)";
 
411
                    }
 
412
                }
 
413
            } else {
 
414
                # Tweak symbol
 
415
                tweak_symbol($sym, $version);
 
416
                push @changes_new, " ".$sym->get_symbolspec(1);
 
417
            }
 
418
        }
 
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;
 
423
    }
 
424
 
 
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, \&regular_print);
 
431
    print_changes_list(\@changes_arch,
 
432
        "architecture set changed for the symbols below:",
 
433
        \&info, \&regular_print);
 
434
 
 
435
    # Finally adjust confirmed arches list
 
436
    $template->set_confirmed($symfiles->get_latest_version(),
 
437
        $symfiles->get_latest_arches());
 
438
 
 
439
    # Generate diff
 
440
    if ($opt_verbose) {
 
441
        my $tmpfile = File::Temp->new(TEMPLATE => "${opt_in}.newXXXXXX");
 
442
        $template->output($tmpfile,
 
443
            package => $opt_package,
 
444
            template_mode => 1,
 
445
            with_deprecated => 1,
 
446
        );
 
447
        $tmpfile->close();
 
448
        spawn(exec => ["diff", "-u", $symfile->{file}, $tmpfile->filename],
 
449
              to_handle => \*STDERR, wait_child => 1, nocheck => 1);
 
450
    }
 
451
 
 
452
    return $template;
 
453
}
 
454
 
 
455
############### Subcommands ####################
 
456
sub _create_symfile {
 
457
    my ($file, $filename_re) = @_;
 
458
    my $filename = File::Basename::basename($file);
 
459
    my $symfile;
 
460
    if ($filename =~ $filename_re) {
 
461
        regular_print("* Loading \"%s\" symbol file '%s' ...", $1, $file);
 
462
        $symfile = Debian::PkgKde::SymbolsHelper::SymbolFile->new(
 
463
                file => $file,
 
464
                arch => "$1"
 
465
        );
 
466
    } else {
 
467
        warning("%s is not named properly. Expected *_<arch> or *.<arch>", $file);
 
468
    }
 
469
    return $symfile;
 
470
}
 
471
 
 
472
sub subcommand_create {
 
473
    my %opts = (
 
474
        get_common_options("oav"),
 
475
    );
 
476
    if (GetOptions(%opts)) {
 
477
        unless (defined $opt_out) {
 
478
            error("output file option (-o) is mandatory");
 
479
        }
 
480
 
 
481
        # Load symbol files
 
482
        my @input_files = @ARGV;
 
483
        my %symfiles;
 
484
        my $str_arches = join("|", get_valid_arches());
 
485
        my $filename_re = qr/.*?[_.]($str_arches)$/;
 
486
 
 
487
        unless (@input_files) {
 
488
            error("please pass files/directory with arch specific symbol files");
 
489
        }
 
490
        for my $f (@input_files) {
 
491
            if (-d $f) {
 
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());
 
500
                        }
 
501
                        $symfiles{$symfile->get_arch()} = $symfile;
 
502
                    }
 
503
                }
 
504
                closedir DIR;
 
505
            } elsif (-r $f) {
 
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());
 
510
                    }
 
511
                    $symfiles{$symfile->get_arch()} = $symfile;
 
512
                }
 
513
            } else {
 
514
                error("unreadale file/directory: %s", $f);
 
515
            }
 
516
        }
 
517
 
 
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);
 
522
            }
 
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;
 
527
            }
 
528
            foreach my $symfile (values %symfiles) {
 
529
                $symfile->set_confirmed($opt_version, $symfile->get_arch());
 
530
            }
 
531
 
 
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);
 
538
 
 
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();
 
544
                }
 
545
            }
 
546
            my $template = $symfiles->create_template();
 
547
 
 
548
            # Set confirmed header
 
549
            $template->set_confirmed($symfiles->get_latest_version(),
 
550
                $symfiles->get_latest_arches());
 
551
 
 
552
            foreach my $sym ($template->get_symbols(),
 
553
                             $template->get_patterns())
 
554
            {
 
555
                tweak_symbol($sym, $opt_version);
 
556
            }
 
557
 
 
558
            return out_symfile($template);
 
559
        } else {
 
560
            error("no properly named symbol files located");
 
561
        }
 
562
        return 0;
 
563
    }
 
564
    return 1;
 
565
}
 
566
 
 
567
sub subcommand_patch {
 
568
    my $opt_file2patch;
 
569
    my $opt_confirmed_arches = '';
 
570
    my @input_patches;
 
571
    my %opts = (
 
572
        get_common_options("obipavV"),
 
573
        "file-to-patch|f=s" => \$opt_file2patch,
 
574
        "confirmed-arches|c=s" => \$opt_confirmed_arches,
 
575
    );
 
576
    if (GetOptions(%opts)) {
 
577
        check_mandatory_options("p", "");
 
578
        @input_patches = @ARGV;
 
579
        unless ($opt_in) {
 
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);
 
583
        }
 
584
        push @input_patches, "-" unless @input_patches;
 
585
 
 
586
        my @patches = load_patches(@input_patches);
 
587
        my @confirmed_arches = split(/[\s,]+/, $opt_confirmed_arches);
 
588
        error("no valid patches found.") unless @patches;
 
589
        return out_symfile(
 
590
            patch_symfile($opt_package, $opt_version, $opt_in, $opt_file2patch,
 
591
                \@patches, \@confirmed_arches)
 
592
        );
 
593
    }
 
594
    return 1;
 
595
}
 
596
 
 
597
sub subcommand_batchpatch {
 
598
    my @opt_packages;
 
599
    my @input_patches;
 
600
    my $opt_continue_on_err;
 
601
    my $opt_confirmed_arches = '';
 
602
    my $failed_packages = 0;
 
603
 
 
604
    my %opts = (
 
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,
 
609
    );
 
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);
 
617
 
 
618
        my @packages;
 
619
        if (@opt_packages) {
 
620
            foreach my $package (@opt_packages) {
 
621
                my @pkgs = split(/[\s\n]+/sm, $package);
 
622
                push @packages, @pkgs;
 
623
            }
 
624
        } else {
 
625
            @packages = grep { exists $packages{$_} } get_all_packages();
 
626
        }
 
627
 
 
628
        error("no valid patches found") unless @patches;
 
629
        error("no packages specified or none to patch for this source")
 
630
            unless @packages;
 
631
 
 
632
        foreach my $package (@packages) {
 
633
            my $msg = sprintf("| Processing %s package |", $package);
 
634
            regular_print("-" x length($msg));
 
635
            regular_print($msg);
 
636
            regular_print("-" x length($msg));
 
637
            if (my $infile = find_package_symbolfile_path($package, $opt_arch)) {
 
638
                my $template;
 
639
                eval {
 
640
                    $template = patch_symfile($package, $opt_version, $infile, undef,
 
641
                        \@patches, \@confirmed_arches);
 
642
                };
 
643
                if ($@) {
 
644
                    if ($opt_continue_on_err) {
 
645
                        print STDERR $@;
 
646
                        info("%s patching FAILED. Continuing with subsequent package if any.", $package);
 
647
                        $failed_packages++;
 
648
                    } else {
 
649
                        print STDERR $@;
 
650
                        error("%s patching FAILED. Will NOT continue.", $package);
 
651
                        die $@;
 
652
                    }
 
653
                } else {
 
654
                    $opt_in = $infile;
 
655
                    out_symfile($template);
 
656
                }
 
657
            } else {
 
658
                regular_print("* UNABLE to find symbol file for %s", $package);
 
659
            }
 
660
        }
 
661
        return $failed_packages;
 
662
    }
 
663
    return 1;
 
664
}
 
665
 
 
666
sub subcommand_rewrite {
 
667
    my $opt_template = 1;
 
668
    my $opt_convert;
 
669
    my %opts = (
 
670
        get_common_options("boiav"),
 
671
        "template!" => \$opt_template,
 
672
        "convert" => \$opt_convert,
 
673
    );
 
674
    if (GetOptions(%opts)) {
 
675
        check_mandatory_options("i");
 
676
 
 
677
        if (-f $opt_in) {
 
678
            my $symfile = Debian::PkgKde::SymbolsHelper::SymbolFile->new(file => $opt_in, arch => $opt_arch);
 
679
            my %o = (
 
680
                template_mode => $opt_template
 
681
            );
 
682
 
 
683
            foreach my $sym ($symfile->get_symbols()) {
 
684
                $sym->upgrade_virtual_table_symbol($opt_arch) if ($opt_convert);
 
685
                tweak_symbol($sym, $opt_version);
 
686
            }
 
687
            foreach my $pat ($symfile->get_patterns()) {
 
688
                tweak_symbol($pat, $opt_version);
 
689
            }
 
690
 
 
691
            return out_symfile($symfile->fork(), %o);
 
692
        } else {
 
693
            error("input symbol file ($opt_in) not found");
 
694
            return 1;
 
695
        }
 
696
    }
 
697
    return 1;
 
698
}
 
699
 
 
700
# Boilerplate for the common subcommand handler
 
701
sub subcommand_boilerplate {
 
702
    my %opts = (
 
703
        get_common_options("obipav"),
 
704
    );
 
705
    if (GetOptions(%opts)) {
 
706
#        check_mandatory_options("o");
 
707
        return 0;
 
708
    }
 
709
    return 1;
 
710
}
 
711
 
 
712
my %SUBCOMMANDS = (
 
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" ],
 
717
);
 
718
 
 
719
report_options(info_fh => \*STDERR);
 
720
 
 
721
my $curcmd = shift @ARGV;
 
722
if (defined $curcmd && exists $SUBCOMMANDS{$curcmd}) {
 
723
    my $ret = &{$SUBCOMMANDS{$curcmd}->[1]}();
 
724
    exit($ret);
 
725
} else {
 
726
    my $err;
 
727
    $err = ($curcmd) ? "unrecognized subcommand '$curcmd'." : "subcommand was not specified.";
 
728
    errormsg($err . " Valid subcommands are:");
 
729
 
 
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]);
 
734
    }
 
735
    exit(2);
 
736
}
 
737
 
 
738
# vi: noexpandtab shiftwidth=4 tabstop=8