Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(95)

Side by Side Diff: third_party/dpkg-dev/scripts/Dpkg/Changelog.pm

Issue 2411423002: Linux build: Use sysroot when calculating dependencies (Closed)
Patch Set: Update expected_deps Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright © 2005, 2007 Frank Lichtenheld <frank@lichtenheld.de>
2 # Copyright © 2009 Raphaël Hertzog <hertzog@debian.org>
3 #
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17 =encoding utf8
18
19 =head1 NAME
20
21 Dpkg::Changelog - base class to implement a changelog parser
22
23 =head1 DESCRIPTION
24
25 Dpkg::Changelog is a class representing a changelog file
26 as an array of changelog entries (Dpkg::Changelog::Entry).
27 By deriving this object and implementing its parse method, you
28 add the ability to fill this object with changelog entries.
29
30 =head2 FUNCTIONS
31
32 =cut
33
34 package Dpkg::Changelog;
35
36 use strict;
37 use warnings;
38
39 our $VERSION = '1.00';
40
41 use Dpkg;
42 use Dpkg::Gettext;
43 use Dpkg::ErrorHandling qw(:DEFAULT report);
44 use Dpkg::Control;
45 use Dpkg::Control::Changelog;
46 use Dpkg::Control::Fields;
47 use Dpkg::Index;
48 use Dpkg::Version;
49 use Dpkg::Vendor qw(run_vendor_hook);
50
51 use parent qw(Dpkg::Interface::Storable);
52
53 use overload
54 '@{}' => sub { return $_[0]->{data} };
55
56 =over 4
57
58 =item my $c = Dpkg::Changelog->new(%options)
59
60 Creates a new changelog object.
61
62 =cut
63
64 sub new {
65 my ($this, %opts) = @_;
66 my $class = ref($this) || $this;
67 my $self = {
68 verbose => 1,
69 parse_errors => []
70 };
71 bless $self, $class;
72 $self->set_options(%opts);
73 return $self;
74 }
75
76 =item $c->load($filename)
77
78 Parse $filename as a changelog.
79
80 =cut
81
82 =item $c->set_options(%opts)
83
84 Change the value of some options. "verbose" (defaults to 1) defines
85 whether parse errors are displayed as warnings by default. "reportfile"
86 is a string to use instead of the name of the file parsed, in particular
87 in error messages. "range" defines the range of entries that we want to
88 parse, the parser will stop as soon as it has parsed enough data to
89 satisfy $c->get_range($opts{range}).
90
91 =cut
92
93 sub set_options {
94 my ($self, %opts) = @_;
95 $self->{$_} = $opts{$_} foreach keys %opts;
96 }
97
98 =item $c->reset_parse_errors()
99
100 Can be used to delete all information about errors occurred during
101 previous L<parse> runs.
102
103 =cut
104
105 sub reset_parse_errors {
106 my ($self) = @_;
107 $self->{parse_errors} = [];
108 }
109
110 =item $c->parse_error($file, $line_nr, $error, [$line])
111
112 Record a new parse error in $file at line $line_nr. The error message is
113 specified with $error and a copy of the line can be recorded in $line.
114
115 =cut
116
117 sub parse_error {
118 my ($self, $file, $line_nr, $error, $line) = @_;
119 shift;
120
121 push @{$self->{parse_errors}}, [ @_ ];
122
123 if ($self->{verbose}) {
124 if ($line) {
125 warning("%20s(l$line_nr): $error\nLINE: $line", $file);
126 } else {
127 warning("%20s(l$line_nr): $error", $file);
128 }
129 }
130 }
131
132 =item $c->get_parse_errors()
133
134 Returns all error messages from the last L<parse> run.
135 If called in scalar context returns a human readable
136 string representation. If called in list context returns
137 an array of arrays. Each of these arrays contains
138
139 =over 4
140
141 =item 1.
142
143 a string describing the origin of the data (a filename usually). If the
144 reportfile configuration option was given, its value will be used instead.
145
146 =item 2.
147
148 the line number where the error occurred
149
150 =item 3.
151
152 an error description
153
154 =item 4.
155
156 the original line
157
158 =back
159
160 =cut
161
162 sub get_parse_errors {
163 my ($self) = @_;
164
165 if (wantarray) {
166 return @{$self->{parse_errors}};
167 } else {
168 my $res = '';
169 foreach my $e (@{$self->{parse_errors}}) {
170 if ($e->[3]) {
171 $res .= report(_g('warning'),_g("%s(l%s): %s\nLINE: %s"), @$e );
172 } else {
173 $res .= report(_g('warning'), _g('%s(l%s): %s'), @$e);
174 }
175 }
176 return $res;
177 }
178 }
179
180 =item $c->set_unparsed_tail($tail)
181
182 Add a string representing unparsed lines after the changelog entries.
183 Use undef as $tail to remove the unparsed lines currently set.
184
185 =item $c->get_unparsed_tail()
186
187 Return a string representing the unparsed lines after the changelog
188 entries. Returns undef if there's no such thing.
189
190 =cut
191
192 sub set_unparsed_tail {
193 my ($self, $tail) = @_;
194 $self->{unparsed_tail} = $tail;
195 }
196
197 sub get_unparsed_tail {
198 my ($self) = @_;
199 return $self->{unparsed_tail};
200 }
201
202 =item @{$c}
203
204 Returns all the Dpkg::Changelog::Entry objects contained in this changelog
205 in the order in which they have been parsed.
206
207 =item $c->get_range($range)
208
209 Returns an array (if called in list context) or a reference to an array of
210 Dpkg::Changelog::Entry objects which each represent one entry of the
211 changelog. $range is a hash reference describing the range of entries
212 to return. See section L<"RANGE SELECTION">.
213
214 =cut
215
216 sub __sanity_check_range {
217 my ($self, $r) = @_;
218 my $data = $self->{data};
219
220 if (defined($r->{offset}) and not defined($r->{count})) {
221 warning(_g("'offset' without 'count' has no effect")) if $self->{verbose };
222 delete $r->{offset};
223 }
224
225 ## no critic (ControlStructures::ProhibitUntilBlocks)
226 if ((defined($r->{count}) || defined($r->{offset})) &&
227 (defined($r->{from}) || defined($r->{since}) ||
228 defined($r->{to}) || defined($r->{until})))
229 {
230 warning(_g("you can't combine 'count' or 'offset' with any other " .
231 'range option')) if $self->{verbose};
232 delete $r->{from};
233 delete $r->{since};
234 delete $r->{to};
235 delete $r->{until};
236 }
237 if (defined($r->{from}) && defined($r->{since})) {
238 warning(_g("you can only specify one of 'from' and 'since', using " .
239 "'since'")) if $self->{verbose};
240 delete $r->{from};
241 }
242 if (defined($r->{to}) && defined($r->{until})) {
243 warning(_g("you can only specify one of 'to' and 'until', using " .
244 "'until'")) if $self->{verbose};
245 delete $r->{to};
246 }
247
248 # Handle non-existing versions
249 my (%versions, @versions);
250 foreach my $entry (@{$data}) {
251 $versions{$entry->get_version()->as_string()} = 1;
252 push @versions, $entry->get_version()->as_string();
253 }
254 if ((defined($r->{since}) and not exists $versions{$r->{since}})) {
255 warning(_g("'%s' option specifies non-existing version"), 'since');
256 warning(_g('use newest entry that is earlier than the one specified'));
257 foreach my $v (@versions) {
258 if (version_compare_relation($v, REL_LT, $r->{since})) {
259 $r->{since} = $v;
260 last;
261 }
262 }
263 if (not exists $versions{$r->{since}}) {
264 # No version was earlier, include all
265 warning(_g('none found, starting from the oldest entry'));
266 delete $r->{since};
267 $r->{from} = $versions[-1];
268 }
269 }
270 if ((defined($r->{from}) and not exists $versions{$r->{from}})) {
271 warning(_g("'%s' option specifies non-existing version"), 'from');
272 warning(_g('use oldest entry that is later than the one specified'));
273 my $oldest;
274 foreach my $v (@versions) {
275 if (version_compare_relation($v, REL_GT, $r->{from})) {
276 $oldest = $v;
277 }
278 }
279 if (defined($oldest)) {
280 $r->{from} = $oldest;
281 } else {
282 warning(_g("no such entry found, ignoring '%s' parameter"), 'from');
283 delete $r->{from}; # No version was oldest
284 }
285 }
286 if (defined($r->{until}) and not exists $versions{$r->{until}}) {
287 warning(_g("'%s' option specifies non-existing version"), 'until');
288 warning(_g('use oldest entry that is later than the one specified'));
289 my $oldest;
290 foreach my $v (@versions) {
291 if (version_compare_relation($v, REL_GT, $r->{until})) {
292 $oldest = $v;
293 }
294 }
295 if (defined($oldest)) {
296 $r->{until} = $oldest;
297 } else {
298 warning(_g("no such entry found, ignoring '%s' parameter"), 'until') ;
299 delete $r->{until}; # No version was oldest
300 }
301 }
302 if (defined($r->{to}) and not exists $versions{$r->{to}}) {
303 warning(_g("'%s' option specifies non-existing version"), 'to');
304 warning(_g('use newest entry that is earlier than the one specified'));
305 foreach my $v (@versions) {
306 if (version_compare_relation($v, REL_LT, $r->{to})) {
307 $r->{to} = $v;
308 last;
309 }
310 }
311 if (not exists $versions{$r->{to}}) {
312 # No version was earlier
313 warning(_g("no such entry found, ignoring '%s' parameter"), 'to');
314 delete $r->{to};
315 }
316 }
317
318 if (defined($r->{since}) and $data->[0]->get_version() eq $r->{since}) {
319 warning(_g("'since' option specifies most recent version, ignoring"));
320 delete $r->{since};
321 }
322 if (defined($r->{until}) and $data->[-1]->get_version() eq $r->{until}) {
323 warning(_g("'until' option specifies oldest version, ignoring"));
324 delete $r->{until};
325 }
326 ## use critic
327 }
328
329 sub get_range {
330 my ($self, $range) = @_;
331 $range //= {};
332 my $res = $self->_data_range($range);
333 if (defined $res) {
334 return @$res if wantarray;
335 return $res;
336 } else {
337 return;
338 }
339 }
340
341 sub _is_full_range {
342 my ($self, $range) = @_;
343
344 return 1 if $range->{all};
345
346 # If no range delimiter is specified, we want everything.
347 foreach (qw(since until from to count offset)) {
348 return 0 if exists $range->{$_};
349 }
350
351 return 1;
352 }
353
354 sub _data_range {
355 my ($self, $range) = @_;
356
357 my $data = $self->{data} or return;
358
359 return [ @$data ] if $self->_is_full_range($range);
360
361 $self->__sanity_check_range($range);
362
363 my ($start, $end);
364 if (defined($range->{count})) {
365 my $offset = $range->{offset} || 0;
366 my $count = $range->{count};
367 # Convert count/offset in start/end
368 if ($offset > 0) {
369 $offset -= ($count < 0);
370 } elsif ($offset < 0) {
371 $offset = $#$data + ($count > 0) + $offset;
372 } else {
373 $offset = $#$data if $count < 0;
374 }
375 $start = $end = $offset;
376 $start += $count+1 if $count < 0;
377 $end += $count-1 if $count > 0;
378 # Check limits
379 $start = 0 if $start < 0;
380 return if $start > $#$data;
381 $end = $#$data if $end > $#$data;
382 return if $end < 0;
383 $end = $start if $end < $start;
384 return [ @{$data}[$start .. $end] ];
385 }
386
387 ## no critic (ControlStructures::ProhibitUntilBlocks)
388 my @result;
389 my $include = 1;
390 $include = 0 if defined($range->{to}) or defined($range->{until});
391 foreach (@$data) {
392 my $v = $_->get_version();
393 $include = 1 if defined($range->{to}) and $v eq $range->{to};
394 last if defined($range->{since}) and $v eq $range->{since};
395
396 push @result, $_ if $include;
397
398 $include = 1 if defined($range->{until}) and $v eq $range->{until};
399 last if defined($range->{from}) and $v eq $range->{from};
400 }
401 ## use critic
402
403 return \@result if scalar(@result);
404 return;
405 }
406
407 =item $c->abort_early()
408
409 Returns true if enough data have been parsed to be able to return all
410 entries selected by the range set at creation (or with set_options).
411
412 =cut
413
414 sub abort_early {
415 my ($self) = @_;
416
417 my $data = $self->{data} or return;
418 my $r = $self->{range} or return;
419 my $count = $r->{count} || 0;
420 my $offset = $r->{offset} || 0;
421
422 return if $self->_is_full_range($r);
423 return if $offset < 0 or $count < 0;
424 if (defined($r->{count})) {
425 if ($offset > 0) {
426 $offset -= ($count < 0);
427 }
428 my $start = my $end = $offset;
429 $end += $count-1 if $count > 0;
430 return ($start < @$data and $end < @$data);
431 }
432
433 return unless defined($r->{since}) or defined($r->{from});
434 foreach (@$data) {
435 my $v = $_->get_version();
436 return 1 if defined($r->{since}) and $v eq $r->{since};
437 return 1 if defined($r->{from}) and $v eq $r->{from};
438 }
439
440 return;
441 }
442
443 =item $c->save($filename)
444
445 Save the changelog in the given file.
446
447 =item $c->output()
448
449 =item "$c"
450
451 Returns a string representation of the changelog (it's a concatenation of
452 the string representation of the individual changelog entries).
453
454 =item $c->output($fh)
455
456 Output the changelog to the given filehandle.
457
458 =cut
459
460 sub output {
461 my ($self, $fh) = @_;
462 my $str = '';
463 foreach my $entry (@{$self}) {
464 my $text = $entry->output();
465 print { $fh } $text if defined $fh;
466 $str .= $text if defined wantarray;
467 }
468 my $text = $self->get_unparsed_tail();
469 if (defined $text) {
470 print { $fh } $text if defined $fh;
471 $str .= $text if defined wantarray;
472 }
473 return $str;
474 }
475
476 =item my $control = $c->dpkg($range)
477
478 Returns a Dpkg::Control::Changelog object representing the entries selected
479 by the optional range specifier (see L<"RANGE SELECTION"> for details).
480 Returns undef in no entries are matched.
481
482 The following fields are contained in the object:
483
484 =over 4
485
486 =item Source
487
488 package name (in the first entry)
489
490 =item Version
491
492 packages' version (from first entry)
493
494 =item Distribution
495
496 target distribution (from first entry)
497
498 =item Urgency
499
500 urgency (highest of all printed entries)
501
502 =item Maintainer
503
504 person that created the (first) entry
505
506 =item Date
507
508 date of the (first) entry
509
510 =item Closes
511
512 bugs closed by the entry/entries, sorted by bug number
513
514 =item Changes
515
516 content of the the entry/entries
517
518 =back
519
520 =cut
521
522 our ( @URGENCIES, %URGENCIES );
523 BEGIN {
524 @URGENCIES = qw(low medium high critical emergency);
525 my $i = 1;
526 %URGENCIES = map { $_ => $i++ } @URGENCIES;
527 }
528
529 sub dpkg {
530 my ($self, $range) = @_;
531
532 my @data = $self->get_range($range) or return;
533 my $src = shift @data;
534
535 my $f = Dpkg::Control::Changelog->new();
536 $f->{Urgency} = $src->get_urgency() || 'unknown';
537 $f->{Source} = $src->get_source() || 'unknown';
538 $f->{Version} = $src->get_version() // 'unknown';
539 $f->{Distribution} = join(' ', $src->get_distributions());
540 $f->{Maintainer} = $src->get_maintainer() || '';
541 $f->{Date} = $src->get_timestamp() || '';
542 $f->{Changes} = $src->get_dpkg_changes();
543
544 # handle optional fields
545 my $opts = $src->get_optional_fields();
546 my %closes;
547 foreach (keys %$opts) {
548 if (/^Urgency$/i) { # Already dealt
549 } elsif (/^Closes$/i) {
550 $closes{$_} = 1 foreach (split(/\s+/, $opts->{Closes}));
551 } else {
552 field_transfer_single($opts, $f);
553 }
554 }
555
556 foreach my $bin (@data) {
557 my $oldurg = $f->{Urgency} || '';
558 my $oldurgn = $URGENCIES{$f->{Urgency}} || -1;
559 my $newurg = $bin->get_urgency() || '';
560 my $newurgn = $URGENCIES{$newurg} || -1;
561 $f->{Urgency} = ($newurgn > $oldurgn) ? $newurg : $oldurg;
562 $f->{Changes} .= "\n" . $bin->get_dpkg_changes();
563
564 # handle optional fields
565 $opts = $bin->get_optional_fields();
566 foreach (keys %$opts) {
567 if (/^Closes$/i) {
568 $closes{$_} = 1 foreach (split(/\s+/, $opts->{Closes}));
569 } elsif (not exists $f->{$_}) { # Don't overwrite an existing field
570 field_transfer_single($opts, $f);
571 }
572 }
573 }
574
575 if (scalar keys %closes) {
576 $f->{Closes} = join ' ', sort { $a <=> $b } keys %closes;
577 }
578 run_vendor_hook('post-process-changelog-entry', $f);
579
580 return $f;
581 }
582
583 =item my @controls = $c->rfc822($range)
584
585 Returns a Dpkg::Index containing Dpkg::Control::Changelog objects where
586 each object represents one entry in the changelog that is part of the
587 range requested (see L<"RANGE SELECTION"> for details). For the format of
588 such an object see the description of the L<"dpkg"> method (while ignoring
589 the remarks about which values are taken from the first entry).
590
591 =cut
592
593 sub rfc822 {
594 my ($self, $range) = @_;
595
596 my @data = $self->get_range($range) or return;
597 my $index = Dpkg::Index->new(type => CTRL_CHANGELOG);
598
599 foreach my $entry (@data) {
600 my $f = Dpkg::Control::Changelog->new();
601 $f->{Urgency} = $entry->get_urgency() || 'unknown';
602 $f->{Source} = $entry->get_source() || 'unknown';
603 $f->{Version} = $entry->get_version() // 'unknown';
604 $f->{Distribution} = join(' ', $entry->get_distributions());
605 $f->{Maintainer} = $entry->get_maintainer() || '';
606 $f->{Date} = $entry->get_timestamp() || '';
607 $f->{Changes} = $entry->get_dpkg_changes();
608
609 # handle optional fields
610 my $opts = $entry->get_optional_fields();
611 foreach (keys %$opts) {
612 field_transfer_single($opts, $f) unless exists $f->{$_};
613 }
614
615 run_vendor_hook('post-process-changelog-entry', $f);
616
617 $index->add($f);
618 }
619 return $index;
620 }
621
622 =back
623
624 =head1 RANGE SELECTION
625
626 A range selection is described by a hash reference where
627 the allowed keys and values are described below.
628
629 The following options take a version number as value.
630
631 =over 4
632
633 =item since
634
635 Causes changelog information from all versions strictly
636 later than B<version> to be used.
637
638 =item until
639
640 Causes changelog information from all versions strictly
641 earlier than B<version> to be used.
642
643 =item from
644
645 Similar to C<since> but also includes the information for the
646 specified B<version> itself.
647
648 =item to
649
650 Similar to C<until> but also includes the information for the
651 specified B<version> itself.
652
653 =back
654
655 The following options don't take version numbers as values:
656
657 =over 4
658
659 =item all
660
661 If set to a true value, all entries of the changelog are returned,
662 this overrides all other options.
663
664 =item count
665
666 Expects a signed integer as value. Returns C<value> entries from the
667 top of the changelog if set to a positive integer, and C<abs(value)>
668 entries from the tail if set to a negative integer.
669
670 =item offset
671
672 Expects a signed integer as value. Changes the starting point for
673 C<count>, either counted from the top (positive integer) or from
674 the tail (negative integer). C<offset> has no effect if C<count>
675 wasn't given as well.
676
677 =back
678
679 Some examples for the above options. Imagine an example changelog with
680 entries for the versions 1.2, 1.3, 2.0, 2.1, 2.2, 3.0 and 3.1.
681
682 Range Included entries
683 C<{ since =E<gt> '2.0' }> 3.1, 3.0, 2.2
684 C<{ until =E<gt> '2.0' }> 1.3, 1.2
685 C<{ from =E<gt> '2.0' }> 3.1, 3.0, 2.2, 2.1, 2.0
686 C<{ to =E<gt> '2.0' }> 2.0, 1.3, 1.2
687 C<{ count =E<gt> 2 }> 3.1, 3.0
688 C<{ count =E<gt> -2 }> 1.3, 1.2
689 C<{ count =E<gt> 3, offset=E<gt> 2 }> 2.2, 2.1, 2.0
690 C<{ count =E<gt> 2, offset=E<gt> -3 }> 2.0, 1.3
691 C<{ count =E<gt> -2, offset=E<gt> 3 }> 3.0, 2.2
692 C<{ count =E<gt> -2, offset=E<gt> -3 }> 2.2, 2.1
693
694 Any combination of one option of C<since> and C<from> and one of
695 C<until> and C<to> returns the intersection of the two results
696 with only one of the options specified.
697
698 =head1 AUTHOR
699
700 Frank Lichtenheld, E<lt>frank@lichtenheld.deE<gt>
701 Raphaël Hertzog, E<lt>hertzog@debian.orgE<gt>
702
703 =cut
704 1;
OLDNEW
« no previous file with comments | « third_party/dpkg-dev/scripts/Dpkg/BuildProfiles.pm ('k') | third_party/dpkg-dev/scripts/Dpkg/Changelog/Debian.pm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698