OLD | NEW |
| (Empty) |
1 # Copyright © 2008-2011 Raphaël Hertzog <hertzog@debian.org> | |
2 # | |
3 # This program is free software; you can redistribute it and/or modify | |
4 # it under the terms of the GNU General Public License as published by | |
5 # the Free Software Foundation; either version 2 of the License, or | |
6 # (at your option) any later version. | |
7 # | |
8 # This program is distributed in the hope that it will be useful, | |
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 # GNU General Public License for more details. | |
12 # | |
13 # You should have received a copy of the GNU General Public License | |
14 # along with this program. If not, see <https://www.gnu.org/licenses/>. | |
15 | |
16 package Dpkg::Source::Package; | |
17 | |
18 =encoding utf8 | |
19 | |
20 =head1 NAME | |
21 | |
22 Dpkg::Source::Package - manipulate Debian source packages | |
23 | |
24 =head1 DESCRIPTION | |
25 | |
26 This module provides an object that can manipulate Debian source | |
27 packages. While it supports both the extraction and the creation | |
28 of source packages, the only API that is officially supported | |
29 is the one that supports the extraction of the source package. | |
30 | |
31 =head1 FUNCTIONS | |
32 | |
33 =cut | |
34 | |
35 use strict; | |
36 use warnings; | |
37 | |
38 our $VERSION = '1.01'; | |
39 | |
40 use Dpkg::Gettext; | |
41 use Dpkg::ErrorHandling; | |
42 use Dpkg::Control; | |
43 use Dpkg::Checksums; | |
44 use Dpkg::Version; | |
45 use Dpkg::Compression; | |
46 use Dpkg::Exit qw(run_exit_handlers); | |
47 use Dpkg::Path qw(check_files_are_the_same find_command); | |
48 use Dpkg::IPC; | |
49 use Dpkg::Vendor qw(run_vendor_hook); | |
50 | |
51 use Carp; | |
52 use POSIX qw(:errno_h :sys_wait_h); | |
53 use File::Basename; | |
54 | |
55 use Exporter qw(import); | |
56 our @EXPORT_OK = qw(get_default_diff_ignore_regex | |
57 set_default_diff_ignore_regex | |
58 get_default_tar_ignore_pattern); | |
59 | |
60 my $diff_ignore_default_regex = ' | |
61 # Ignore general backup files | |
62 (?:^|/).*~$| | |
63 # Ignore emacs recovery files | |
64 (?:^|/)\.#.*$| | |
65 # Ignore vi swap files | |
66 (?:^|/)\..*\.sw.$| | |
67 # Ignore baz-style junk files or directories | |
68 (?:^|/),,.*(?:$|/.*$)| | |
69 # File-names that should be ignored (never directories) | |
70 (?:^|/)(?:DEADJOE|\.arch-inventory|\.(?:bzr|cvs|hg|git)ignore)$| | |
71 # File or directory names that should be ignored | |
72 (?:^|/)(?:CVS|RCS|\.deps|\{arch\}|\.arch-ids|\.svn| | |
73 \.hg(?:tags|sigs)?|_darcs|\.git(?:attributes|modules)?| | |
74 \.shelf|_MTN|\.be|\.bzr(?:\.backup|tags)?)(?:$|/.*$) | |
75 '; | |
76 # Take out comments and newlines | |
77 $diff_ignore_default_regex =~ s/^#.*$//mg; | |
78 $diff_ignore_default_regex =~ s/\n//sg; | |
79 | |
80 # Public variables | |
81 # XXX: Backwards compatibility, stop exporting on VERSION 2.00. | |
82 ## no critic (Variables::ProhibitPackageVars) | |
83 our $diff_ignore_default_regexp; | |
84 *diff_ignore_default_regexp = \$diff_ignore_default_regex; | |
85 | |
86 no warnings 'qw'; ## no critic (TestingAndDebugging::ProhibitNoWarnings) | |
87 our @tar_ignore_default_pattern = qw( | |
88 *.a | |
89 *.la | |
90 *.o | |
91 *.so | |
92 .*.sw? | |
93 */*~ | |
94 ,,* | |
95 .[#~]* | |
96 .arch-ids | |
97 .arch-inventory | |
98 .be | |
99 .bzr | |
100 .bzr.backup | |
101 .bzr.tags | |
102 .bzrignore | |
103 .cvsignore | |
104 .deps | |
105 .git | |
106 .gitattributes | |
107 .gitignore | |
108 .gitmodules | |
109 .hg | |
110 .hgignore | |
111 .hgsigs | |
112 .hgtags | |
113 .shelf | |
114 .svn | |
115 CVS | |
116 DEADJOE | |
117 RCS | |
118 _MTN | |
119 _darcs | |
120 {arch} | |
121 ); | |
122 ## use critic | |
123 | |
124 =over 4 | |
125 | |
126 =item my $string = get_default_diff_ignore_regex() | |
127 | |
128 Returns the default diff ignore regex. | |
129 | |
130 =cut | |
131 | |
132 sub get_default_diff_ignore_regex { | |
133 return $diff_ignore_default_regex; | |
134 } | |
135 | |
136 =item set_default_diff_ignore_regex($string) | |
137 | |
138 Set a regex as the new default diff ignore regex. | |
139 | |
140 =cut | |
141 | |
142 sub set_default_diff_ignore_regex { | |
143 my ($regex) = @_; | |
144 | |
145 $diff_ignore_default_regex = $regex; | |
146 } | |
147 | |
148 =item my @array = get_default_tar_ignore_pattern() | |
149 | |
150 Returns the default tar ignore pattern, as an array. | |
151 | |
152 =cut | |
153 | |
154 sub get_default_tar_ignore_pattern { | |
155 return @tar_ignore_default_pattern; | |
156 } | |
157 | |
158 =item $p = Dpkg::Source::Package->new(filename => $dscfile, options => {}) | |
159 | |
160 Creates a new object corresponding to the source package described | |
161 by the file $dscfile. | |
162 | |
163 The options hash supports the following options: | |
164 | |
165 =over 8 | |
166 | |
167 =item skip_debianization | |
168 | |
169 If set to 1, do not apply Debian changes on the extracted source package. | |
170 | |
171 =item skip_patches | |
172 | |
173 If set to 1, do not apply Debian-specific patches. This options is | |
174 specific for source packages using format "2.0" and "3.0 (quilt)". | |
175 | |
176 =item require_valid_signature | |
177 | |
178 If set to 1, the check_signature() method will be stricter and will error | |
179 out if the signature can't be verified. | |
180 | |
181 =item copy_orig_tarballs | |
182 | |
183 If set to 1, the extraction will copy the upstream tarballs next the | |
184 target directory. This is useful if you want to be able to rebuild the | |
185 source package after its extraction. | |
186 | |
187 =back | |
188 | |
189 =cut | |
190 | |
191 # Object methods | |
192 sub new { | |
193 my ($this, %args) = @_; | |
194 my $class = ref($this) || $this; | |
195 my $self = { | |
196 fields => Dpkg::Control->new(type => CTRL_PKG_SRC), | |
197 options => {}, | |
198 checksums => Dpkg::Checksums->new(), | |
199 }; | |
200 bless $self, $class; | |
201 if (exists $args{options}) { | |
202 $self->{options} = $args{options}; | |
203 } | |
204 if (exists $args{filename}) { | |
205 $self->initialize($args{filename}); | |
206 $self->init_options(); | |
207 } | |
208 return $self; | |
209 } | |
210 | |
211 sub init_options { | |
212 my ($self) = @_; | |
213 # Use full ignore list by default | |
214 # note: this function is not called by V1 packages | |
215 $self->{options}{diff_ignore_regex} ||= $diff_ignore_default_regex; | |
216 $self->{options}{diff_ignore_regex} .= '|(?:^|/)debian/source/local-.*$'; | |
217 if (defined $self->{options}{tar_ignore}) { | |
218 $self->{options}{tar_ignore} = [ @tar_ignore_default_pattern ] | |
219 unless @{$self->{options}{tar_ignore}}; | |
220 } else { | |
221 $self->{options}{tar_ignore} = [ @tar_ignore_default_pattern ]; | |
222 } | |
223 push @{$self->{options}{tar_ignore}}, 'debian/source/local-options', | |
224 'debian/source/local-patch-header'; | |
225 # Skip debianization while specific to some formats has an impact | |
226 # on code common to all formats | |
227 $self->{options}{skip_debianization} ||= 0; | |
228 } | |
229 | |
230 sub initialize { | |
231 my ($self, $filename) = @_; | |
232 my ($fn, $dir) = fileparse($filename); | |
233 error(_g('%s is not the name of a file'), $filename) unless $fn; | |
234 $self->{basedir} = $dir || './'; | |
235 $self->{filename} = $fn; | |
236 | |
237 # Read the fields | |
238 my $fields = Dpkg::Control->new(type => CTRL_PKG_SRC); | |
239 $fields->load($filename); | |
240 $self->{fields} = $fields; | |
241 $self->{is_signed} = $fields->get_option('is_pgp_signed'); | |
242 | |
243 foreach my $f (qw(Source Version Files)) { | |
244 unless (defined($fields->{$f})) { | |
245 error(_g('missing critical source control field %s'), $f); | |
246 } | |
247 } | |
248 | |
249 $self->{checksums}->add_from_control($fields, use_files_for_md5 => 1); | |
250 | |
251 $self->upgrade_object_type(0); | |
252 } | |
253 | |
254 sub upgrade_object_type { | |
255 my ($self, $update_format) = @_; | |
256 $update_format //= 1; | |
257 $self->{fields}{'Format'} = '1.0' | |
258 unless exists $self->{fields}{'Format'}; | |
259 my $format = $self->{fields}{'Format'}; | |
260 | |
261 if ($format =~ /^([\d\.]+)(?:\s+\((.*)\))?$/) { | |
262 my ($version, $variant, $major, $minor) = ($1, $2, $1, undef); | |
263 | |
264 if (defined $variant and $variant ne lc $variant) { | |
265 error(_g("source package format '%s' is not supported: %s"), | |
266 $format, _g('format variant must be in lowercase')); | |
267 } | |
268 | |
269 $major =~ s/\.[\d\.]+$//; | |
270 my $module = "Dpkg::Source::Package::V$major"; | |
271 $module .= '::' . ucfirst $variant if defined $variant; | |
272 eval "require $module; \$minor = \$${module}::CURRENT_MINOR_VERSION;"; | |
273 $minor //= 0; | |
274 if ($update_format) { | |
275 $self->{fields}{'Format'} = "$major.$minor"; | |
276 $self->{fields}{'Format'} .= " ($variant)" if defined $variant; | |
277 } | |
278 if ($@) { | |
279 error(_g("source package format '%s' is not supported: %s"), | |
280 $format, $@); | |
281 } | |
282 bless $self, $module; | |
283 } else { | |
284 error(_g("invalid Format field `%s'"), $format); | |
285 } | |
286 } | |
287 | |
288 =item $p->get_filename() | |
289 | |
290 Returns the filename of the DSC file. | |
291 | |
292 =cut | |
293 | |
294 sub get_filename { | |
295 my ($self) = @_; | |
296 return $self->{basedir} . $self->{filename}; | |
297 } | |
298 | |
299 =item $p->get_files() | |
300 | |
301 Returns the list of files referenced by the source package. The filenames | |
302 usually do not have any path information. | |
303 | |
304 =cut | |
305 | |
306 sub get_files { | |
307 my ($self) = @_; | |
308 return $self->{checksums}->get_files(); | |
309 } | |
310 | |
311 =item $p->check_checksums() | |
312 | |
313 Verify the checksums embedded in the DSC file. It requires the presence of | |
314 the other files constituting the source package. If any inconsistency is | |
315 discovered, it immediately errors out. | |
316 | |
317 =cut | |
318 | |
319 sub check_checksums { | |
320 my ($self) = @_; | |
321 my $checksums = $self->{checksums}; | |
322 # add_from_file verify the checksums if they are already existing | |
323 foreach my $file ($checksums->get_files()) { | |
324 $checksums->add_from_file($self->{basedir} . $file, key => $file); | |
325 } | |
326 } | |
327 | |
328 sub get_basename { | |
329 my ($self, $with_revision) = @_; | |
330 my $f = $self->{fields}; | |
331 unless (exists $f->{'Source'} and exists $f->{'Version'}) { | |
332 error(_g('source and version are required to compute the source basename
')); | |
333 } | |
334 my $v = Dpkg::Version->new($f->{'Version'}); | |
335 my $vs = $v->as_string(omit_epoch => 1, omit_revision => !$with_revision); | |
336 return $f->{'Source'} . '_' . $vs; | |
337 } | |
338 | |
339 sub find_original_tarballs { | |
340 my ($self, %opts) = @_; | |
341 $opts{extension} = compression_get_file_extension_regex() | |
342 unless exists $opts{extension}; | |
343 $opts{include_main} = 1 unless exists $opts{include_main}; | |
344 $opts{include_supplementary} = 1 unless exists $opts{include_supplementary}; | |
345 my $basename = $self->get_basename(); | |
346 my @tar; | |
347 foreach my $dir ('.', $self->{basedir}, $self->{options}{origtardir}) { | |
348 next unless defined($dir) and -d $dir; | |
349 opendir(my $dir_dh, $dir) or syserr(_g('cannot opendir %s'), $dir); | |
350 push @tar, map { "$dir/$_" } grep { | |
351 ($opts{include_main} and | |
352 /^\Q$basename\E\.orig\.tar\.$opts{extension}$/) or | |
353 ($opts{include_supplementary} and | |
354 /^\Q$basename\E\.orig-[[:alnum:]-]+\.tar\.$opts{extension}$/) | |
355 } readdir($dir_dh); | |
356 closedir($dir_dh); | |
357 } | |
358 return @tar; | |
359 } | |
360 | |
361 =item $bool = $p->is_signed() | |
362 | |
363 Returns 1 if the DSC files contains an embedded OpenPGP signature. | |
364 Otherwise returns 0. | |
365 | |
366 =cut | |
367 | |
368 sub is_signed { | |
369 my $self = shift; | |
370 return $self->{is_signed}; | |
371 } | |
372 | |
373 =item $p->check_signature() | |
374 | |
375 Implement the same OpenPGP signature check that dpkg-source does. | |
376 In case of problems, it prints a warning or errors out. | |
377 | |
378 If the object has been created with the "require_valid_signature" option, | |
379 then any problem will result in a fatal error. | |
380 | |
381 =cut | |
382 | |
383 sub check_signature { | |
384 my ($self) = @_; | |
385 my $dsc = $self->get_filename(); | |
386 my @exec; | |
387 | |
388 if (find_command('gpgv2')) { | |
389 push @exec, 'gpgv2'; | |
390 } elsif (find_command('gpgv')) { | |
391 push @exec, 'gpgv'; | |
392 } elsif (find_command('gpg2')) { | |
393 push @exec, 'gpg2', '--no-default-keyring', '-q', '--verify'; | |
394 } elsif (find_command('gpg')) { | |
395 push @exec, 'gpg', '--no-default-keyring', '-q', '--verify'; | |
396 } | |
397 if (scalar(@exec)) { | |
398 if (defined $ENV{HOME} and -r "$ENV{HOME}/.gnupg/trustedkeys.gpg") { | |
399 push @exec, '--keyring', "$ENV{HOME}/.gnupg/trustedkeys.gpg"; | |
400 } | |
401 foreach my $vendor_keyring (run_vendor_hook('keyrings')) { | |
402 if (-r $vendor_keyring) { | |
403 push @exec, '--keyring', $vendor_keyring; | |
404 } | |
405 } | |
406 push @exec, $dsc; | |
407 | |
408 my ($stdout, $stderr); | |
409 spawn(exec => \@exec, wait_child => 1, nocheck => 1, | |
410 to_string => \$stdout, error_to_string => \$stderr, | |
411 timeout => 10); | |
412 if (WIFEXITED($?)) { | |
413 my $gpg_status = WEXITSTATUS($?); | |
414 print { *STDERR } "$stdout$stderr" if $gpg_status; | |
415 if ($gpg_status == 1 or ($gpg_status && | |
416 $self->{options}{require_valid_signature})) | |
417 { | |
418 error(_g('failed to verify signature on %s'), $dsc); | |
419 } elsif ($gpg_status) { | |
420 warning(_g('failed to verify signature on %s'), $dsc); | |
421 } | |
422 } else { | |
423 subprocerr("@exec"); | |
424 } | |
425 } else { | |
426 if ($self->{options}{require_valid_signature}) { | |
427 error(_g("could not verify signature on %s since gpg isn't installed
"), $dsc); | |
428 } else { | |
429 warning(_g("could not verify signature on %s since gpg isn't install
ed"), $dsc); | |
430 } | |
431 } | |
432 } | |
433 | |
434 sub parse_cmdline_options { | |
435 my ($self, @opts) = @_; | |
436 foreach (@opts) { | |
437 if (not $self->parse_cmdline_option($_)) { | |
438 warning(_g('%s is not a valid option for %s'), $_, ref($self)); | |
439 } | |
440 } | |
441 } | |
442 | |
443 sub parse_cmdline_option { | |
444 return 0; | |
445 } | |
446 | |
447 =item $p->extract($targetdir) | |
448 | |
449 Extracts the source package in the target directory $targetdir. Beware | |
450 that if $targetdir already exists, it will be erased. | |
451 | |
452 =cut | |
453 | |
454 sub extract { | |
455 my $self = shift; | |
456 my $newdirectory = $_[0]; | |
457 | |
458 my ($ok, $error) = version_check($self->{fields}{'Version'}); | |
459 error($error) unless $ok; | |
460 | |
461 # Copy orig tarballs | |
462 if ($self->{options}{copy_orig_tarballs}) { | |
463 my $basename = $self->get_basename(); | |
464 my ($dirname, $destdir) = fileparse($newdirectory); | |
465 $destdir ||= './'; | |
466 my $ext = compression_get_file_extension_regex(); | |
467 foreach my $orig (grep { /^\Q$basename\E\.orig(-[[:alnum:]-]+)?\.tar\.$e
xt$/ } | |
468 $self->get_files()) | |
469 { | |
470 my $src = File::Spec->catfile($self->{basedir}, $orig); | |
471 my $dst = File::Spec->catfile($destdir, $orig); | |
472 if (not check_files_are_the_same($src, $dst, 1)) { | |
473 system('cp', '--', $src, $dst); | |
474 subprocerr("cp $src to $dst") if $?; | |
475 } | |
476 } | |
477 } | |
478 | |
479 # Try extract | |
480 eval { $self->do_extract(@_) }; | |
481 if ($@) { | |
482 run_exit_handlers(); | |
483 die $@; | |
484 } | |
485 | |
486 # Store format if non-standard so that next build keeps the same format | |
487 if ($self->{fields}{'Format'} ne '1.0' and | |
488 not $self->{options}{skip_debianization}) | |
489 { | |
490 my $srcdir = File::Spec->catdir($newdirectory, 'debian', 'source'); | |
491 my $format_file = File::Spec->catfile($srcdir, 'format'); | |
492 unless (-e $format_file) { | |
493 mkdir($srcdir) unless -e $srcdir; | |
494 open(my $format_fh, '>', $format_file) | |
495 or syserr(_g('cannot write %s'), $format_file); | |
496 print { $format_fh } $self->{fields}{'Format'} . "\n"; | |
497 close($format_fh); | |
498 } | |
499 } | |
500 | |
501 # Make sure debian/rules is executable | |
502 my $rules = File::Spec->catfile($newdirectory, 'debian', 'rules'); | |
503 my @s = lstat($rules); | |
504 if (not scalar(@s)) { | |
505 unless ($! == ENOENT) { | |
506 syserr(_g('cannot stat %s'), $rules); | |
507 } | |
508 warning(_g('%s does not exist'), $rules) | |
509 unless $self->{options}{skip_debianization}; | |
510 } elsif (-f _) { | |
511 chmod($s[2] | 0111, $rules) | |
512 or syserr(_g('cannot make %s executable'), $rules); | |
513 } else { | |
514 warning(_g('%s is not a plain file'), $rules); | |
515 } | |
516 } | |
517 | |
518 sub do_extract { | |
519 croak 'Dpkg::Source::Package does not know how to unpack a ' . | |
520 'source package; use one of the subclasses'; | |
521 } | |
522 | |
523 # Function used specifically during creation of a source package | |
524 | |
525 sub before_build { | |
526 my ($self, $dir) = @_; | |
527 } | |
528 | |
529 sub build { | |
530 my $self = shift; | |
531 eval { $self->do_build(@_) }; | |
532 if ($@) { | |
533 run_exit_handlers(); | |
534 die $@; | |
535 } | |
536 } | |
537 | |
538 sub after_build { | |
539 my ($self, $dir) = @_; | |
540 } | |
541 | |
542 sub do_build { | |
543 croak 'Dpkg::Source::Package does not know how to build a ' . | |
544 'source package; use one of the subclasses'; | |
545 } | |
546 | |
547 sub can_build { | |
548 my ($self, $dir) = @_; | |
549 return (0, 'can_build() has not been overriden'); | |
550 } | |
551 | |
552 sub add_file { | |
553 my ($self, $filename) = @_; | |
554 my ($fn, $dir) = fileparse($filename); | |
555 if ($self->{checksums}->has_file($fn)) { | |
556 croak "tried to add file '$fn' twice"; | |
557 } | |
558 $self->{checksums}->add_from_file($filename, key => $fn); | |
559 $self->{checksums}->export_to_control($self->{fields}, | |
560 use_files_for_md5 => 1); | |
561 } | |
562 | |
563 sub commit { | |
564 my $self = shift; | |
565 eval { $self->do_commit(@_) }; | |
566 if ($@) { | |
567 run_exit_handlers(); | |
568 die $@; | |
569 } | |
570 } | |
571 | |
572 sub do_commit { | |
573 my ($self, $dir) = @_; | |
574 info(_g("'%s' is not supported by the source format '%s'"), | |
575 'dpkg-source --commit', $self->{fields}{'Format'}); | |
576 } | |
577 | |
578 sub write_dsc { | |
579 my ($self, %opts) = @_; | |
580 my $fields = $self->{fields}; | |
581 | |
582 foreach my $f (keys %{$opts{override}}) { | |
583 $fields->{$f} = $opts{override}{$f}; | |
584 } | |
585 | |
586 unless($opts{nocheck}) { | |
587 foreach my $f (qw(Source Version)) { | |
588 unless (defined($fields->{$f})) { | |
589 error(_g('missing information for critical output field %s'), $f
); | |
590 } | |
591 } | |
592 foreach my $f (qw(Maintainer Architecture Standards-Version)) { | |
593 unless (defined($fields->{$f})) { | |
594 warning(_g('missing information for output field %s'), $f); | |
595 } | |
596 } | |
597 } | |
598 | |
599 foreach my $f (keys %{$opts{remove}}) { | |
600 delete $fields->{$f}; | |
601 } | |
602 | |
603 my $filename = $opts{filename}; | |
604 unless (defined $filename) { | |
605 $filename = $self->get_basename(1) . '.dsc'; | |
606 } | |
607 open(my $dsc_fh, '>', $filename) | |
608 or syserr(_g('cannot write %s'), $filename); | |
609 $fields->apply_substvars($opts{substvars}); | |
610 $fields->output($dsc_fh); | |
611 close($dsc_fh); | |
612 } | |
613 | |
614 =back | |
615 | |
616 =head1 CHANGES | |
617 | |
618 =head2 Version 1.01 | |
619 | |
620 New functions: get_default_diff_ignore_regex(), set_default_diff_ignore_regex(), | |
621 get_default_tar_ignore_pattern() | |
622 | |
623 Deprecated variables: $diff_ignore_default_regexp, @tar_ignore_default_pattern | |
624 | |
625 =head1 AUTHOR | |
626 | |
627 Raphaël Hertzog, E<lt>hertzog@debian.orgE<gt> | |
628 | |
629 =cut | |
630 | |
631 1; | |
OLD | NEW |