Package: dpkg Version: 1.18.2 Severity: wishlist Hi,
it would be great if one could access Debian packaging information from source packages of all formats in a unified way. My main motivation is sbuild which could then just extract debian/changelog from a source package to check in which chroot to build the package without having first to extract the whole source package (an operation which might take considerable time and disk space for large packages). Attached you find a partly working proposal for how such a functionality could look like. It introduces the new --debian-tarfile option to dpkg-source which will work similarly to `dpkg-deb --ctrl-tarfile` in that it would write a tarball representation of the Debian packaging directory to standard output independent of the source package format. With the attached patch this already nearly works for the trivial case (format 2.0 and format 3.0 (quilt)). It does not *fully* work because dpkg info messages are *also* printed to standard output. Hence, the resulting combined output on standard output is not a proper tar file as it has dpkg-source messages in the beginning (but removing it in an editor will recover a working tarball). Maybe dpkg info and warning messages could be written to standard error instead? In ./scripts/dpkg-source.pl I found that the new option could share some code with the --extract option, so I did. In scripts/Dpkg/Source/Package/V2.pm there also seems to be lots of opportunity for sharing the sanity checks of the do_extract method with the new do_debian_tarfile method. If the --debian-tarfile option should do the same checks as the --extract option (it probably should) then I could move these sanity checks into a third function that would then be used by do_extract as well as by do_debian_tarfile. Now some thoughts about the interface I chose. Here is an excerpt from man/dpkg-source.1: > .TP > .RI "\fB\-\-debian-tarfile\fP \fIfilename\fP.dsc [\fIpath\fP]" > Extracts the \fB./debian\fP packaging directory of a given source package and > sends it to standard output in > .B tar > format. The \fB./debian\fP directory component will be part of all files in > the > output. Together with > .BR tar (1) > this can be used to extract a particular packaging file from the source > package > without unpacking the whole source package first. > For source formats >= 2.0, this action never requires the upstream source. > For source format 1.0, this action always requires the upstream source. 1. Since the output of --debian-tarfile always contains the content of the ./debian directory one might think that it would make sense to strip off the ./debian directory prefix and have the content of the ./debian directory in the root of the created tarball. I decided against this for two reasons: - the *.debian.tar.gz file of source formats >= 2.0 also contains the debian directory, so I think one would expect the same layout of the tarball produced by --debian-tarfile - it is easier to strip off a leading directory with tar than to add one if one so desires 2. For source format 1.0, the upstream tarball is *always* required. Even if no hunks in the diff.gz patch an existing file, the upstream tarball might still contain a file ./debian/foo but we can only know this if we have the upstream tarball as well. Without the upstream tarball, the output of this option would otherwise be unreliable. 3. I thought about the option of an optional wildcard path so that by selecting only a subset of files in the ./debian directory, the upstream tarball would only be required if the requested file is patching an existing one. But this also fails for the same reason as the last point: the wildcard path might match a file in the upstream tarball and we can only check for this if the upstream tarball is present. 4. If an option were added that is not a wildcard but a precise path, then it would be possible to not require the upstream tarball if that path in the diff is not patching an existing file. The resulting created tarball would then only carry a single file in it (which also looks strange). I thought this option to be very limited and since source format 1.0 packages are in the minority and since I currently do not have a good use case where the upstream tarball would not be present as well, I decided to not make this kind of feature part of the initial implementation of this proposal. Should the use for it arise, it can always be added later without breaking the interface. What do you think? Thanks! cheers, josch
>From 5f12e7d29e7741d67443379db0ea50307abc00bd Mon Sep 17 00:00:00 2001 From: Johannes 'josch' Schauer <jo...@mister-muffin.de> Date: Thu, 3 Sep 2015 12:15:38 +0200 Subject: [PATCH] proposal for dpkg-source --debian-tarfile --- man/dpkg-source.1 | 13 ++++++++++ scripts/Dpkg/Source/Package.pm | 33 ++++++++++++++++++++++++ scripts/Dpkg/Source/Package/V1.pm | 4 +++ scripts/Dpkg/Source/Package/V2.pm | 34 +++++++++++++++++++++++++ scripts/Dpkg/Source/Package/V3/Bzr.pm | 4 +++ scripts/Dpkg/Source/Package/V3/Custom.pm | 4 +++ scripts/Dpkg/Source/Package/V3/Git.pm | 4 +++ scripts/Dpkg/Source/Package/V3/Native.pm | 4 +++ scripts/dpkg-source.pl | 43 +++++++++++++++++++++----------- 9 files changed, 128 insertions(+), 15 deletions(-) diff --git a/man/dpkg-source.1 b/man/dpkg-source.1 index 1276753..08a4eac 100644 --- a/man/dpkg-source.1 +++ b/man/dpkg-source.1 @@ -121,6 +121,19 @@ This command can take supplementary parameters depending on the source format. It will error out for formats where this operation doesn't mean anything. .TP +.RI "\fB\-\-debian-tarfile\fP \fIfilename\fP.dsc [\fIpath\fP]" +Extracts the \fB./debian\fP packaging directory of a given source package and +sends it to standard output in +.B tar +format. The \fB./debian\fP directory component will be part of all files in the +output. Together with +.BR tar (1) +this can be used to extract a particular packaging file from the source package +without unpacking the whole source package first. +For source formats >= 2.0, this action never requires the upstream source. +For source format 1.0, this action always requires the upstream source. + +.TP .BR \-? ", " \-\-help Show the usage message and exit. .TP diff --git a/scripts/Dpkg/Source/Package.pm b/scripts/Dpkg/Source/Package.pm index 60ba3e7..91369bc 100644 --- a/scripts/Dpkg/Source/Package.pm +++ b/scripts/Dpkg/Source/Package.pm @@ -540,6 +540,39 @@ sub do_extract { 'source package; use one of the subclasses'; } +=item $p->debian_tarfile() + +Extracts the debian directory of the source package as a tar file to +standard output. + +=cut + +sub debian_tarfile { + my $self = shift; + + my ($ok, $error) = version_check($self->{fields}{'Version'}); + if (not $ok) { + if ($self->{options}{ignore_bad_version}) { + warning($error); + } else { + error($error); + } + } + + # Try extract + eval { $self->do_debian_tarfile() }; + if ($@) { + run_exit_handlers(); + die $@; + } + +} + +sub do_debian_tarfile { + croak 'Dpkg::Source::Package does not know how to extract a ' . + 'debian directory; use one of the subclasses'; +} + # Function used specifically during creation of a source package sub before_build { diff --git a/scripts/Dpkg/Source/Package/V1.pm b/scripts/Dpkg/Source/Package/V1.pm index db81962..412c532 100644 --- a/scripts/Dpkg/Source/Package/V1.pm +++ b/scripts/Dpkg/Source/Package/V1.pm @@ -173,6 +173,10 @@ sub do_extract { } } +sub do_debian_tarfile { + error(g_('cannot extract debian directory from source format 1.0')); +} + sub can_build { my ($self, $dir) = @_; diff --git a/scripts/Dpkg/Source/Package/V2.pm b/scripts/Dpkg/Source/Package/V2.pm index 99fab9c..15d586e 100644 --- a/scripts/Dpkg/Source/Package/V2.pm +++ b/scripts/Dpkg/Source/Package/V2.pm @@ -195,6 +195,40 @@ sub do_extract { unless $self->{options}{skip_patches}; } +sub do_debian_tarfile { + my $self = shift; + + my ($debianfile, %seen); + my $re_ext = compression_get_file_extension_regex(); + my $basename = $self->get_basename(); + my $basenamerev = $self->get_basename(1); + foreach my $file ($self->get_files()) { + my $uncompressed = $file; + $uncompressed =~ s/\.$re_ext$/.*/; + $uncompressed =~ s/\.$re_ext\.asc$/.*.asc/; + error(g_('duplicate files in %s source package: %s'), 'v2.0', + $uncompressed) if $seen{$uncompressed}; + $seen{$uncompressed} = 1; + if ($file =~ /^\Q$basename\E\.orig\.tar\.$re_ext$/) { + } elsif ($file =~ /^\Q$basename\E\.orig\.tar\.$re_ext\.asc$/) { + } elsif ($file =~ /^\Q$basename\E\.orig-([[:alnum:]-]+)\.tar\.$re_ext$/) { + } elsif ($file =~ /^\Q$basename\E\.orig-([[:alnum:]-]+)\.tar\.$re_ext\.asc$/) { + } elsif ($file =~ /^\Q$basenamerev\E\.debian\.tar\.$re_ext$/) { + $debianfile = $file; + } else { + error(g_('unrecognized file for a %s source package: %s'), + 'v2.0', $file); + } + } + info(g_('unpacking %s'), $debianfile); + my $dscdir = $self->{basedir}; + my $fh = Dpkg::Compression::FileHandle->new(filename => "$dscdir$debianfile"); + while(<$fh>) { + print; + } + close $fh; +} + sub get_autopatch_name { return 'zz_debian-diff-auto'; } diff --git a/scripts/Dpkg/Source/Package/V3/Bzr.pm b/scripts/Dpkg/Source/Package/V3/Bzr.pm index 6cf8fae..790e98f 100644 --- a/scripts/Dpkg/Source/Package/V3/Bzr.pm +++ b/scripts/Dpkg/Source/Package/V3/Bzr.pm @@ -208,4 +208,8 @@ sub do_extract { chdir $old_cwd or syserr(g_("unable to chdir to '%s'"), $old_cwd); } +sub do_debian_tarfile { + error(g_('cannot extract debian directory from source format 3.0 (bzr)')); +} + 1; diff --git a/scripts/Dpkg/Source/Package/V3/Custom.pm b/scripts/Dpkg/Source/Package/V3/Custom.pm index 4895897..8dd8de1 100644 --- a/scripts/Dpkg/Source/Package/V3/Custom.pm +++ b/scripts/Dpkg/Source/Package/V3/Custom.pm @@ -39,6 +39,10 @@ sub do_extract { error(g_("Format '3.0 (custom)' is only used to create source packages")); } +sub do_debian_tarfile { + error(g_("Format '3.0 (custom)' is only used to create source packages")); +} + sub can_build { my ($self, $dir) = @_; diff --git a/scripts/Dpkg/Source/Package/V3/Git.pm b/scripts/Dpkg/Source/Package/V3/Git.pm index 97b7aff..21b1b98 100644 --- a/scripts/Dpkg/Source/Package/V3/Git.pm +++ b/scripts/Dpkg/Source/Package/V3/Git.pm @@ -233,4 +233,8 @@ sub do_extract { sanity_check($newdirectory); } +sub do_debian_tarfile { + error(g_('cannot extract debian directory from source format 3.0 (git)')); +} + 1; diff --git a/scripts/Dpkg/Source/Package/V3/Native.pm b/scripts/Dpkg/Source/Package/V3/Native.pm index ea38c27..1146bdc 100644 --- a/scripts/Dpkg/Source/Package/V3/Native.pm +++ b/scripts/Dpkg/Source/Package/V3/Native.pm @@ -64,6 +64,10 @@ sub do_extract { $tar->extract($newdirectory); } +sub do_debian_tarfile { + error(g_('cannot extract debian directory from source format 3.0 (native)')); +} + sub can_build { my ($self, $dir) = @_; diff --git a/scripts/dpkg-source.pl b/scripts/dpkg-source.pl index 9aeec13..ccd04ea 100755 --- a/scripts/dpkg-source.pl +++ b/scripts/dpkg-source.pl @@ -91,6 +91,8 @@ while (@ARGV && $ARGV[0] =~ m/^-/) { setopmode('after-build'); } elsif ($arg eq '--commit') { setopmode('commit'); + } elsif ($arg eq '--debian-tarfile') { + setopmode('debian-tarfile'); } elsif ($arg eq '--print-format') { setopmode('print-format'); report_options(info_fh => \*STDERR); # Avoid clutter on STDOUT @@ -441,15 +443,17 @@ if ($options{opmode} =~ /^(build|print-format|(before|after)-build|commit)$/) { substvars => $substvars); exit(0); -} elsif ($options{opmode} eq 'extract') { +} elsif ($options{opmode} =~ /^(extract|debian-tarfile)$/) { # Check command line unless (scalar(@ARGV)) { usageerr(g_('--%s needs at least one argument, the .dsc'), $options{opmode}); } - if (scalar(@ARGV) > 2) { + if ($options{opmode} eq 'extract' && scalar(@ARGV) > 2) { usageerr(g_('--%s takes no more than two arguments'), $options{opmode}); + } elsif ($options{opmode} eq 'debian-tarfile' && scalar(@ARGV) > 1) { + usageerr(g_('--%s takes no more than one argument'), $options{opmode}); } my $dsc = shift(@ARGV); if (-d $dsc) { @@ -464,16 +468,6 @@ if ($options{opmode} =~ /^(build|print-format|(before|after)-build|commit)$/) { # Parse command line options $srcpkg->parse_cmdline_options(@cmdline_options); - # Decide where to unpack - my $newdirectory = $srcpkg->get_basename(); - $newdirectory =~ s/_/-/g; - if (@ARGV) { - $newdirectory = File::Spec->catdir(shift(@ARGV)); - if (-e $newdirectory) { - error(g_('unpack target exists: %s'), $newdirectory); - } - } - # Various checks before unpacking unless ($options{no_check}) { if ($srcpkg->is_signed()) { @@ -488,9 +482,25 @@ if ($options{opmode} =~ /^(build|print-format|(before|after)-build|commit)$/) { $srcpkg->check_checksums(); } - # Unpack the source package (delegated to Dpkg::Source::Package::*) - info(g_('extracting %s in %s'), $srcpkg->{fields}{'Source'}, $newdirectory); - $srcpkg->extract($newdirectory); + if ($options{opmode} eq 'extract') { + # Decide where to unpack + my $newdirectory = $srcpkg->get_basename(); + $newdirectory =~ s/_/-/g; + if (@ARGV) { + $newdirectory = File::Spec->catdir(shift(@ARGV)); + if (-e $newdirectory) { + error(g_('unpack target exists: %s'), $newdirectory); + } + } + + # Unpack the source package (delegated to Dpkg::Source::Package::*) + info(g_('extracting %s in %s'), $srcpkg->{fields}{'Source'}, $newdirectory); + $srcpkg->extract($newdirectory); + } elsif ($options{opmode} eq 'debian-tarfile') { + # Extract the debian subdirectory (delegated to Dpkg::Source::Package::*) + info(g_('extracting ./debian directory content from %s'), $srcpkg->{fields}{'Source'}); + $srcpkg->debian_tarfile(); + } exit(0); } @@ -539,6 +549,9 @@ sub usage { extract source package. -b, --build <dir> build source package. --print-format <dir> print the format to be used for the source package. + --debian-tarfile <filename.dsc> + extract the debian subdirectory of the source + package. --commit [<dir> [<patch-name>]] store upstream changes in a new patch.') . "\n\n" . g_( -- 2.1.4