[Reproducible-commits] [dpkg] 02/05: Add support for .buildinfo files

Mattia Rizzolo mattia at debian.org
Mon Aug 1 10:27:25 UTC 2016


This is an automated email from the git hooks/post-receive script.

mattia pushed a commit to branch pu/reproducible_builds
in repository dpkg.

commit 1911deef614b94c0f469020c50a323f3321ab6ae
Author: Jérémy Bobbio <lunar at debian.org>
Date:   Fri Nov 6 12:17:39 2015 +0000

    Add support for .buildinfo files
    
    .buildinfo files are a new type of control files, similar to .changes
    files, meant to describe the environment of a build and its products.
    They are meant to be added to the Debian archive to allow independent
    parties to reproduce a build and verify the result.
    
    Specifications for .buildinfo are available at:
    https://wiki.debian.org/ReproducibleBuilds/BuildinfoSpecification
    
    This patch adds support for .buildinfo files in Dpkg::Control and
    adds a new script, dpkg-genbuildinfo, that will now be called by
    dpkg-buildpackage before generating the .changes file.
    
    (Small changes by Niko Tyni <ntyni at debian.org> and HW42 <hw42 at ipsumj.de>)
    
    Closes: #138409
---
 debian/dpkg-dev.install            |   1 +
 debian/dpkg-dev.manpages           |   1 +
 man/Makefile.am                    |   1 +
 man/dpkg-buildpackage.1            |  31 ++-
 man/dpkg-genbuildinfo.1            | 135 ++++++++++++
 man/po/po4a.cfg                    |   3 +
 scripts/.gitignore                 |   1 +
 scripts/Dpkg/Control.pm            |   5 +
 scripts/Dpkg/Control/FieldsCore.pm |  35 +++-
 scripts/Dpkg/Control/Types.pm      |   3 +
 scripts/Dpkg/Dist/Files.pm         |   4 +
 scripts/Dpkg/Vendor/Debian.pm      |   2 +
 scripts/Dpkg/Vendor/Default.pm     |  12 ++
 scripts/Makefile.am                |   2 +
 scripts/dpkg-buildpackage.pl       |  63 +++++-
 scripts/dpkg-genbuildinfo.pl       | 407 +++++++++++++++++++++++++++++++++++++
 scripts/po/POTFILES.in             |   1 +
 17 files changed, 690 insertions(+), 17 deletions(-)

diff --git a/debian/dpkg-dev.install b/debian/dpkg-dev.install
index 5cec6fe..2ed6d65 100644
--- a/debian/dpkg-dev.install
+++ b/debian/dpkg-dev.install
@@ -6,6 +6,7 @@ usr/bin/dpkg-buildflags
 usr/bin/dpkg-buildpackage
 usr/bin/dpkg-checkbuilddeps
 usr/bin/dpkg-distaddfile
+usr/bin/dpkg-genbuildinfo
 usr/bin/dpkg-genchanges
 usr/bin/dpkg-gencontrol
 usr/bin/dpkg-gensymbols
diff --git a/debian/dpkg-dev.manpages b/debian/dpkg-dev.manpages
index dc54340..36e4254 100644
--- a/debian/dpkg-dev.manpages
+++ b/debian/dpkg-dev.manpages
@@ -21,6 +21,7 @@ debian/tmp/usr/share/man/*/dpkg-buildflags.1
 debian/tmp/usr/share/man/*/dpkg-buildpackage.1
 debian/tmp/usr/share/man/*/dpkg-checkbuilddeps.1
 debian/tmp/usr/share/man/*/dpkg-distaddfile.1
+debian/tmp/usr/share/man/*/dpkg-genbuildinfo.1
 debian/tmp/usr/share/man/*/dpkg-genchanges.1
 debian/tmp/usr/share/man/*/dpkg-gencontrol.1
 debian/tmp/usr/share/man/*/dpkg-gensymbols.1
diff --git a/man/Makefile.am b/man/Makefile.am
index fb4167c..39a64a5 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -25,6 +25,7 @@ dist_man_MANS = \
 	dpkg-deb.1 \
 	dpkg-distaddfile.1 \
 	dpkg-divert.1 \
+	dpkg-genbuildinfo.1 \
 	dpkg-genchanges.1 \
 	dpkg-gencontrol.1 \
 	dpkg-gensymbols.1 \
diff --git a/man/dpkg-buildpackage.1 b/man/dpkg-buildpackage.1
index f56c124..d444ac9 100644
--- a/man/dpkg-buildpackage.1
+++ b/man/dpkg-buildpackage.1
@@ -62,26 +62,31 @@ build has been requested with \fB\-\-build\fP or equivalent options), or
 \fBbuild\-indep\fP and \fBbinary\-indep\fP (if an \fBall\fP and not \fBany\fP
 build has been requested with \fB\-\-build\fP or equivalent options).
 .IP \fB6.\fP 3
+Unless a source-only build has been requested, it runs the \fBbuildinfo\fP hook
+and calls \fBdpkg\-genbuildinfo\fP to generate a \fB.buildinfo\fP file.
+Several \fBdpkg\-buildpackage\fP options are forwarded to
+\fBdpkg\-genbuildinfo\fP.
+.IP \fB7.\fP 3
 It runs the \fBchanges\fP hook and calls \fBdpkg\-genchanges\fP to
 generate a \fB.changes\fP file.
 Many \fBdpkg\-buildpackage\fP options are forwarded to
 \fBdpkg\-genchanges\fP.
-.IP \fB7.\fP 3
+.IP \fB8.\fP 3
 It runs the \fBpostclean\fP hook and if \fB\-tc\fP or \fB\-\-post\-clean\fP
 is specified, it will call \fBfakeroot debian/rules clean\fP again.
-.IP \fB8.\fP 3
-It calls \fBdpkg\-source \-\-after\-build\fP.
 .IP \fB9.\fP 3
+It calls \fBdpkg\-source \-\-after\-build\fP.
+.IP \fB10.\fP 3
 It runs the \fBcheck\fP hook and calls a package checker for the
 \fB.changes\fP file (if a command is specified in \fBDEB_CHECK_COMMAND\fP or
 with \fB\-\-check\-command\fP).
-.IP \fB10.\fP 3
+.IP \fB11.\fP 3
 It runs the \fBsign\fP hook and calls \fBgpg2\fP or \fBgpg\fP to sign
 the \fB.dsc\fP file (if any, unless \fB\-us\fP or \fB\-\-unsigned\-source\fP
 is specified or on UNRELEASED builds), and the \fB.changes\fP file
 (unless \fB\-uc\fP or \fB\-\-unsigned\-changes\fP is specified or on
 UNRELEASED builds).
-.IP \fB11.\fP 3
+.IP \fB12.\fP 3
 It runs the \fBdone\fP hook.
 .
 .SH OPTIONS
@@ -356,6 +361,17 @@ The source package version (without the epoch).
 The upstream version.
 .RE
 .TP
+.BI \-\-buildinfo-identifier= identifier
+Specify the identifier part of the \fB.buildinfo\fP file name (since dpkg
+1.18.5).
+By default, \fBdpkg\-buildpackage\fP will create an identifier using the
+current time and the first characters of the MD5 hash.
+An arbitrary identifier can be specified as a replacement.
+The identifier has the same restriction as package names: they
+must consist only of lower case letters (a-z), digits (0-9), plus (+)
+and minus (\-) signs, and periods (.), be at least two characters long
+and must start with an alphanumeric character.
+.TP
 .BR \-p ", " \-\-sign\-command= \fIsign-command\fP
 When \fBdpkg\-buildpackage\fP needs to execute GPG to sign a source
 control (\fB.dsc\fP) file or a \fB.changes\fP file it will run
@@ -410,6 +426,10 @@ Passed unchanged to \fBdpkg\-source\fP. See its manual page.
 Pass option \fIopt\fP to \fBdpkg\-source\fP (since dpkg 1.15.6).
 Can be used multiple times.
 .TP
+.BI \-\-buildinfo\-option= opt
+Pass option \fIopt\fP to \fBdpkg\-genbuildinfo\fP (since dpkg 1.18.5).
+Can be used multiple times.
+.TP
 .BI \-\-changes\-option= opt
 Pass option \fIopt\fP to \fBdpkg\-genchanges\fP (since dpkg 1.15.6).
 Can be used multiple times.
@@ -511,6 +531,7 @@ and initial arguments for
 .BR dpkg\-source (1),
 .BR dpkg\-architecture (1),
 .BR dpkg\-buildflags (1),
+.BR dpkg\-genbuildinfo (1),
 .BR dpkg\-genchanges (1),
 .BR fakeroot (1),
 .BR lintian (1),
diff --git a/man/dpkg-genbuildinfo.1 b/man/dpkg-genbuildinfo.1
new file mode 100644
index 0000000..e4eba10
--- /dev/null
+++ b/man/dpkg-genbuildinfo.1
@@ -0,0 +1,135 @@
+.\" dpkg manual page - dpkg-genbuildinfo(1)
+.\"
+.\" Copyright © 1995-1996 Ian Jackson <ian at chiark.chu.cam.ac.uk>
+.\" Copyright © 2000 Wichert Akkerman <wakkerma at debian.org>
+.\" Copyright © 2008-2010 Raphaël Hertzog <hertzog at debian.org>
+.\" Copyright © 2006-2014 Guillem Jover <guillem at debian.org>
+.\" Copyright © 2015 Jérémy Bobbio <lunar at debian.org>
+.\"
+.\" This is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program.  If not, see <https://www.gnu.org/licenses/>.
+.
+.TH dpkg\-genbuildinfo 1 "2015-01-01" "Debian Project" "dpkg utilities"
+.SH NAME
+dpkg\-genbuildinfo \- generate Debian .buildinfo files
+.
+.SH SYNOPSIS
+.B dpkg\-genbuildinfo
+.RI [ option ...]
+.br
+.
+.SH DESCRIPTION
+.B dpkg\-genbuildinfo
+reads information from an unpacked and built Debian source tree and
+from the files it has generated and generates a Debian control
+file describing the build environment and the build products
+.RB ( .buildinfo " file)."
+.
+.SH OPTIONS
+.TP
+.BI \-\-build= type
+Specifies the build \fItype\fP from a comma-separated list of components
+(since dpkg 1.18.5).
+
+The allowed values are:
+.RS
+.TP
+.B any
+Generate build information including unqualified build dependencies
+(\fBBuild-Depends\fP) and architecture specific build dependencies
+(\fBBuild-Depends-Arch\fP).
+.TP
+.B all
+Generate build information including unqualified build dependencies
+(\fBBuild-Depends\fP) and architecture independent build dependencies
+(\fBBuild-Depends-Arch\fP).
+.TP
+.B source
+Effectively ignored: generate build information with just the unqualified
+build dependencies (\fBBuild-Depends\fP).
+.TP
+.B binary
+Generate build information with all three types of build dependencies.
+This is an alias for \fBany,all\fP.
+.TP
+.B full
+Generate build information with all three types of build dependencies.
+This is an alias for \fBany,all,source\fP, and the same as the default case
+when no build option is specified.
+.RE
+.TP
+.B -A
+Equivalent to \fB\-\-build=all\fP.
+.TP
+.B -B
+Equivalent to \fB\-\-build=any\fP.
+.TP
+.BI \-c controlfile
+Specifies the main source control file to read information from. The
+default is
+.BR debian/control .
+.TP
+.BI \-l changelog-file
+Specifies the changelog file to read information from. The
+default is
+.BR debian/changelog .
+.TP
+.BI \-f files-list-file
+Specifies where is the list of files that have been produced by the build,
+rather than using
+.BR debian/files .
+.TP
+.BI \-F changelog-format
+Specifies the format of the changelog. See \fBdpkg\-parsechangelog\fP(1)
+for information about alternative formats.
+.TP
+.BI \-u upload-files-dir
+Look for the files to be uploaded in
+.I upload-files-dir
+rather than
+.B ..
+.RB ( dpkg\-genbuildinfo
+needs to find these files so that it can include their sizes and
+checksums in the
+.B .buildinfo
+file).
+.TP
+.BI \-\-admindir= dir
+Change the location of the \fBdpkg\fR database. The default location is
+\fI/var/lib/dpkg\fP.
+.TP
+.BI \-\-always\-include\-path
+By default, the \fBBuild\-Path\fR field will only be written if the current
+directory starts with \fB/build/\fR. Specify this option to always write
+a \fBBuild\-Path\fR field when generating the \fB.buildinfo\fR.
+.TP
+.B \-q
+.B dpkg\-genbuildinfo
+might produce informative messages on standard error.
+.B \-q
+suppresses these messages.
+.TP
+.BR \-? ", " \-\-help
+Show the usage message and exit.
+.TP
+.BR \-\-version
+Show the version and exit.
+.
+.SH FILES
+.TP
+.B debian/files
+The list of generated files.
+.B dpkg\-genbuildinfo
+reads the data here when producing a
+.B .buildinfo
+file.
diff --git a/man/po/po4a.cfg b/man/po/po4a.cfg
index 4f92836..8540edf 100644
--- a/man/po/po4a.cfg
+++ b/man/po/po4a.cfg
@@ -85,6 +85,9 @@
 [type:man] dpkg-divert.1 $lang:$lang/dpkg-divert.1 \
            add_$lang:po/$lang.add
 
+[type:man] dpkg-genbuildinfo.1 $lang:$lang/dpkg-genbuildinfo.1 \
+           add_$lang:po/$lang.add
+
 [type:man] dpkg-genchanges.1 $lang:$lang/dpkg-genchanges.1 \
            add_$lang:po/$lang.add
 
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 380ad91..0d1f29a 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -3,6 +3,7 @@ dpkg-buildflags
 dpkg-buildpackage
 dpkg-checkbuilddeps
 dpkg-distaddfile
+dpkg-genbuildinfo
 dpkg-genchanges
 dpkg-gencontrol
 dpkg-gensymbols
diff --git a/scripts/Dpkg/Control.pm b/scripts/Dpkg/Control.pm
index 967f32a..6120ec0 100644
--- a/scripts/Dpkg/Control.pm
+++ b/scripts/Dpkg/Control.pm
@@ -28,6 +28,7 @@ our @EXPORT = qw(
     CTRL_REPO_RELEASE
     CTRL_PKG_SRC
     CTRL_PKG_DEB
+    CTRL_FILE_BUILDINFO
     CTRL_FILE_CHANGES
     CTRL_FILE_VENDOR
     CTRL_FILE_STATUS
@@ -104,6 +105,10 @@ Corresponds to a .dsc file of a Debian source package.
 Corresponds to the F<control> file generated by dpkg-gencontrol
 (F<DEBIAN/control>) and to the same file inside .deb packages.
 
+=item CTRL_FILE_BUILDINFO
+
+Corresponds to a .buildinfo file.
+
 =item CTRL_FILE_CHANGES
 
 Corresponds to a .changes file.
diff --git a/scripts/Dpkg/Control/FieldsCore.pm b/scripts/Dpkg/Control/FieldsCore.pm
index bbc19aa..cae8d6b 100644
--- a/scripts/Dpkg/Control/FieldsCore.pm
+++ b/scripts/Dpkg/Control/FieldsCore.pm
@@ -66,7 +66,7 @@ use constant {
 # Deprecated fields of dpkg's status file are also not listed
 our %FIELDS = (
     'Architecture' => {
-        allowed => (ALL_PKG | ALL_SRC | CTRL_FILE_CHANGES) & (~CTRL_INFO_SRC),
+        allowed => (ALL_PKG | ALL_SRC | CTRL_FILE_BUILDINFO | CTRL_FILE_CHANGES) & (~CTRL_INFO_SRC),
         separator => FIELD_SEP_SPACE,
     },
     'Architectures' => {
@@ -74,7 +74,7 @@ our %FIELDS = (
         separator => FIELD_SEP_SPACE,
     },
     'Binary' => {
-        allowed => CTRL_PKG_SRC | CTRL_FILE_CHANGES,
+        allowed => CTRL_PKG_SRC | CTRL_FILE_BUILDINFO | CTRL_FILE_CHANGES,
         # XXX: This field values are separated either by space or comma
         # depending on the context.
         separator => FIELD_SEP_SPACE | FIELD_SEP_COMMA,
@@ -82,6 +82,9 @@ our %FIELDS = (
     'Binary-Only' => {
         allowed => ALL_CHANGES,
     },
+    'Binary-Only-Changes' => {
+        allowed => CTRL_FILE_BUILDINFO,
+    },
     'Breaks' => {
         allowed => ALL_PKG,
         separator => FIELD_SEP_COMMA,
@@ -91,6 +94,9 @@ our %FIELDS = (
     'Bugs' => {
         allowed => (ALL_PKG | CTRL_INFO_SRC | CTRL_FILE_VENDOR) & (~CTRL_INFO_PKG),
     },
+    'Build-Architecture' => {
+        allowed => CTRL_FILE_BUILDINFO,
+    },
     'Build-Conflicts' => {
         allowed => ALL_SRC,
         separator => FIELD_SEP_COMMA,
@@ -130,6 +136,9 @@ our %FIELDS = (
     'Build-Essential' => {
         allowed => ALL_PKG,
     },
+    'Build-Path' => {
+        allowed => CTRL_FILE_BUILDINFO,
+    },
     'Build-Profiles' => {
         allowed => CTRL_INFO_PKG,
         separator => FIELD_SEP_SPACE,
@@ -151,7 +160,7 @@ our %FIELDS = (
         allowed => CTRL_REPO_RELEASE,
     },
     'Changes' => {
-        allowed => ALL_CHANGES,
+        allowed => CTRL_FILE_BUILDINFO | ALL_CHANGES,
     },
     'Classes' => {
         allowed => CTRL_TESTS,
@@ -230,11 +239,17 @@ our %FIELDS = (
         separator => FIELD_SEP_LINE | FIELD_SEP_SPACE,
     },
     'Format' => {
-        allowed => CTRL_PKG_SRC | CTRL_FILE_CHANGES | CTRL_COPYRIGHT_HEADER,
+        allowed => CTRL_PKG_SRC | CTRL_FILE_CHANGES | CTRL_COPYRIGHT_HEADER | CTRL_FILE_BUILDINFO,
     },
     'Homepage' => {
         allowed => ALL_SRC | ALL_PKG,
     },
+    'Installed-Build-Depends' => {
+        allowed => CTRL_FILE_BUILDINFO,
+        separator => FIELD_SEP_COMMA,
+        dependency => 'union',
+        dep_order => 11,
+    },
     'Installed-Size' => {
         allowed => ALL_PKG & ~CTRL_INFO_PKG,
     },
@@ -311,7 +326,7 @@ our %FIELDS = (
         separator => FIELD_SEP_LINE | FIELD_SEP_SPACE,
     },
     'Source' => {
-        allowed => (ALL_PKG | ALL_SRC | ALL_CHANGES | CTRL_COPYRIGHT_HEADER) &
+        allowed => (ALL_PKG | ALL_SRC | ALL_CHANGES | CTRL_COPYRIGHT_HEADER | CTRL_FILE_BUILDINFO) &
                    (~(CTRL_INDEX_SRC | CTRL_INFO_PKG)),
     },
     'Standards-Version' => {
@@ -419,7 +434,7 @@ our %FIELDS = (
         allowed => CTRL_FILE_VENDOR,
     },
     'Version' => {
-        allowed => (ALL_PKG | ALL_SRC | ALL_CHANGES) &
+        allowed => (ALL_PKG | ALL_SRC | CTRL_FILE_BUILDINFO | ALL_CHANGES) &
                     (~(CTRL_INFO_SRC | CTRL_INFO_PKG)),
     },
 );
@@ -427,7 +442,7 @@ our %FIELDS = (
 my @checksum_fields = map { &field_capitalize("Checksums-$_") } checksums_get_list();
 my @sum_fields = map { $_ eq 'md5' ? 'MD5sum' : &field_capitalize($_) }
                  checksums_get_list();
-&field_register($_, CTRL_PKG_SRC | CTRL_FILE_CHANGES) foreach @checksum_fields;
+&field_register($_, CTRL_PKG_SRC | CTRL_FILE_CHANGES | CTRL_FILE_BUILDINFO) foreach @checksum_fields;
 &field_register($_, CTRL_INDEX_PKG | CTRL_REPO_RELEASE,
                 separator => FIELD_SEP_LINE | FIELD_SEP_SPACE) foreach @sum_fields;
 
@@ -446,6 +461,12 @@ our %FIELD_ORDER = (
         Vcs-Svn Testsuite Testsuite-Triggers), &field_list_src_dep(),
         qw(Package-List), @checksum_fields, qw(Files)
     ],
+    CTRL_FILE_BUILDINFO() => [
+        qw(Format Build-Architecture Source Binary Architecture Version
+        Binary-Only-Changes),
+        @checksum_fields,
+        qw(Build-Path Installed-Build-Depends),
+    ],
     CTRL_FILE_CHANGES() => [
         qw(Format Date Source Binary Binary-Only Built-For-Profiles Architecture
         Version Distribution Urgency Maintainer Changed-By Description
diff --git a/scripts/Dpkg/Control/Types.pm b/scripts/Dpkg/Control/Types.pm
index 1bb3e0a..a6c6639 100644
--- a/scripts/Dpkg/Control/Types.pm
+++ b/scripts/Dpkg/Control/Types.pm
@@ -34,6 +34,7 @@ our @EXPORT = qw(
     CTRL_COPYRIGHT_FILES
     CTRL_COPYRIGHT_LICENSE
     CTRL_TESTS
+    CTRL_FILE_BUILDINFO
 );
 
 use Exporter qw(import);
@@ -86,6 +87,8 @@ use constant {
     CTRL_COPYRIGHT_LICENSE => 8192,
     # Package test suite control file in debian/tests/control.
     CTRL_TESTS => 16384,
+    # .buildinfo file
+    CTRL_FILE_BUILDINFO => 32768,
 };
 
 =head1 CHANGES
diff --git a/scripts/Dpkg/Dist/Files.pm b/scripts/Dpkg/Dist/Files.pm
index 2045a1a..8015021 100644
--- a/scripts/Dpkg/Dist/Files.pm
+++ b/scripts/Dpkg/Dist/Files.pm
@@ -58,6 +58,10 @@ sub parse_filename {
         $file->{version} = $3;
         $file->{arch} = $4;
         $file->{package_type} = $5;
+
+        if ($file->{package_type} eq 'buildinfo') {
+            $file->{arch} = undef;
+        }
     } elsif ($fn =~ m/^([-+:.,_0-9a-zA-Z~]+)$/) {
         $file->{filename} = $1;
     } else {
diff --git a/scripts/Dpkg/Vendor/Debian.pm b/scripts/Dpkg/Vendor/Debian.pm
index ebb1750..4a1d36d 100644
--- a/scripts/Dpkg/Vendor/Debian.pm
+++ b/scripts/Dpkg/Vendor/Debian.pm
@@ -76,6 +76,8 @@ sub run_hook {
 	$self->_add_reproducible_flags(@params);
 	$self->_add_sanitize_flags(@params);
 	$self->_add_hardening_flags(@params);
+    } elsif ($hook eq 'builtin-system-build-paths') {
+        return qw(/build/);
     } else {
         return $self->SUPER::run_hook($hook, @params);
     }
diff --git a/scripts/Dpkg/Vendor/Default.pm b/scripts/Dpkg/Vendor/Default.pm
index e2d91bd..9860a70 100644
--- a/scripts/Dpkg/Vendor/Default.pm
+++ b/scripts/Dpkg/Vendor/Default.pm
@@ -116,6 +116,16 @@ The hook is called in Dpkg::BuildFlags to allow the vendor to override
 the default values set for the various build flags. $flags is a
 Dpkg::BuildFlags object.
 
+=item builtin-system-build-paths ()
+
+The hook is called by dpkg-genbuildinfo to determine if the current
+path should be recorded in the Build-Path field. It takes no parameters,
+but returns a (possibly empty) list of root paths considered acceptable.
+As an example, if the list contains "/build/", a Build-Path field will
+be created if the current directory is "/build/dpkg-1.18.5". If the
+list contains "/", the path will always be recorded. If the list is
+empty, the current path will never be recorded.
+
 =back
 
 =cut
@@ -139,6 +149,8 @@ sub run_hook {
 	my ($textref, $ch_info) = @params;
     } elsif ($hook eq 'update-buildflags') {
 	my $flags = shift @params;
+    } elsif ($hook eq 'builtin-system-build-paths') {
+        return ();
     }
 
     # Default return value for unknown/unimplemented hooks
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index 7b1ac36..3530fbc 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -8,6 +8,7 @@ bin_SCRIPTS = \
 	dpkg-buildpackage \
 	dpkg-checkbuilddeps \
 	dpkg-distaddfile \
+	dpkg-genbuildinfo \
 	dpkg-genchanges \
 	dpkg-gencontrol \
 	dpkg-gensymbols \
@@ -27,6 +28,7 @@ EXTRA_DIST = \
 	dpkg-buildpackage.pl \
 	dpkg-checkbuilddeps.pl \
 	dpkg-distaddfile.pl \
+	dpkg-genbuildinfo.pl \
 	dpkg-genchanges.pl \
 	dpkg-gencontrol.pl \
 	dpkg-gensymbols.pl \
diff --git a/scripts/dpkg-buildpackage.pl b/scripts/dpkg-buildpackage.pl
index 42131d6..3f61768 100755
--- a/scripts/dpkg-buildpackage.pl
+++ b/scripts/dpkg-buildpackage.pl
@@ -27,9 +27,10 @@ use Cwd;
 use File::Temp qw(tempdir);
 use File::Basename;
 use File::Copy;
-use POSIX qw(:sys_wait_h);
+use POSIX qw(:sys_wait_h strftime);
 
 use Dpkg ();
+use Dpkg::Control::Info;
 use Dpkg::Gettext;
 use Dpkg::ErrorHandling;
 use Dpkg::Build::Types;
@@ -38,6 +39,7 @@ use Dpkg::BuildProfiles qw(set_build_profiles);
 use Dpkg::Conf;
 use Dpkg::Compression;
 use Dpkg::Checksums;
+use Dpkg::Package;
 use Dpkg::Version;
 use Dpkg::Control;
 use Dpkg::Control::Info;
@@ -92,8 +94,10 @@ sub usage {
                               command to check the .changes file (no default).
       --check-option=<opt>    pass <opt> to check <command>.
       --hook-<name>=<command> set <command> as the hook <name>, known hooks:
-                                init preclean source build binary changes
-                                postclean check sign done
+                                init preclean source build binary buildinfo
+                                changes postclean check sign done
+      --buildinfo-identifier=<identifier>
+                              set the identifier part of the .buildinfo filename.
   -p, --sign-command=<command>
                               command to sign .dsc and/or .changes files
                                 (default is gpg2 or gpg).
@@ -112,6 +116,10 @@ sub usage {
       --target-arch <arch>    set the target Debian architecture.
       --target-type <type>    set the target GNU system type.')
     . "\n\n" . g_(
+'Options passed to dpkg-genbuildinfo:
+      --buildinfo-option=<opt>
+                 pass option <opt> to dpkg-genbuildinfo.')
+    . "\n\n" . g_(
 'Options passed to dpkg-genchanges:
   -si                         source includes orig, if new upstream (default).
   -sa                         source includes orig, always.
@@ -168,9 +176,11 @@ my $since;
 my $maint;
 my $changedby;
 my $desc;
+my $buildinfo_identifier;
+my @buildinfo_opts;
 my @changes_opts;
 my @hook_names = qw(
-    init preclean source build binary changes postclean check sign done
+    init preclean source build binary buildinfo changes postclean check sign done
 );
 my %hook;
 $hook{$_} = undef foreach @hook_names;
@@ -205,6 +215,8 @@ while (@ARGV) {
 	$admindir = $1;
     } elsif (/^--source-option=(.*)$/) {
 	push @source_opts, $1;
+    } elsif (/^--buildinfo-option=(.*)$/) {
+	push @buildinfo_opts, $1;
     } elsif (/^--changes-option=(.*)$/) {
 	push @changes_opts, $1;
     } elsif (/^(?:-j|--jobs=)(\d*|auto)$/) {
@@ -227,6 +239,8 @@ while (@ARGV) {
 	usageerr(g_('missing hook %s command'), $hook_name)
 	    if not defined $hook_cmd;
 	$hook{$hook_name} = $hook_cmd;
+    } elsif (/^--buildinfo-identifier=(.*)$/) {
+	$buildinfo_identifier = $1;
     } elsif (/^(?:-p|--sign-command=)(.*)$/) {
 	$signcommand = $1;
     } elsif (/^(?:-k|--sign-key=)(.*)$/) {
@@ -389,6 +403,12 @@ if (defined $parallel) {
     $build_opts->export();
 }
 
+if (defined $buildinfo_identifier) {
+    # .buildinfo identifiers have the same restrictions as package names
+    my $err = pkg_name_is_illegal($buildinfo_identifier);
+    error(g_("illegal .buildinfo identifier '%s': %s"), $buildinfo_identifier, $err) if $err;
+}
+
 set_build_profiles(@build_profiles) if @build_profiles;
 
 my $cwd = cwd();
@@ -530,15 +550,48 @@ run_hook('build', build_has_any(BUILD_BINARY));
 # This is a temporary measure to not break too many packages on a flag day.
 build_target_fallback();
 
+my $build_types = get_build_options_from_type();
+
 if (build_has_any(BUILD_BINARY)) {
     withecho(@debian_rules, $buildtarget);
     run_hook('binary', 1);
     withecho(@rootcommand, @debian_rules, $binarytarget);
+
+    run_hook('buildinfo', 1);
+
+    push @buildinfo_opts, "--build=$build_types" if build_has_none(BUILD_DEFAULT);
+    push @buildinfo_opts, "--admindir=$admindir" if $admindir;
+
+    open my $genbuildinfo_fh, '-|', 'dpkg-genbuildinfo', @buildinfo_opts
+        or subprocerr('dpkg-genbuildinfo');
+    my @buildinfo_content = <$genbuildinfo_fh>;
+    close $genbuildinfo_fh;
+
+    if (!defined $buildinfo_identifier) {
+        require Digest::MD5;
+
+        my $timestamp = strftime('%Y%m%dT%H%M%Sz', gmtime());
+        my $buildinfo_md5 = Digest::MD5::md5_hex(@buildinfo_content);
+        $buildinfo_identifier = "$timestamp-" . substr($buildinfo_md5, 0, 8);
+    }
+
+    my $buildinfo = "${pv}_${buildinfo_identifier}.buildinfo";
+
+    open my $buildinfo_fh, '>', "../$buildinfo"
+        or syserr(g_('cannot open %s', "../$buildinfo"));
+    print { $buildinfo_fh } @buildinfo_content;
+    close $buildinfo_fh;
+
+    my $control = Dpkg::Control::Info->new('debian/control');
+    my $sec = $control->get_source->{'Section'} // '-';
+    my $pri = $control->get_source->{'Priority'} // '-';
+    warning(g_('missing Section for source files')) if $sec eq '-';
+    warning(g_('missing Priority for source files')) if $pri eq '-';
+    withecho('dpkg-distaddfile', $buildinfo, $sec, $pri);
 }
 
 run_hook('changes', 1);
 
-my $build_types = get_build_options_from_type();
 push @changes_opts, "--build=$build_types" if build_has_none(BUILD_DEFAULT);
 push @changes_opts, "-m$maint" if defined $maint;
 push @changes_opts, "-e$changedby" if defined $changedby;
diff --git a/scripts/dpkg-genbuildinfo.pl b/scripts/dpkg-genbuildinfo.pl
new file mode 100755
index 0000000..7183d94
--- /dev/null
+++ b/scripts/dpkg-genbuildinfo.pl
@@ -0,0 +1,407 @@
+#!/usr/bin/perl
+#
+# dpkg-genbuildinfo
+#
+# Copyright © 1996 Ian Jackson
+# Copyright © 2000,2001 Wichert Akkerman
+# Copyright © 2003-2013 Yann Dirson <dirson at debian.org>
+# Copyright © 2006-2014 Guillem Jover <guillem at debian.org>
+# Copyright © 2014 Niko Tyni <ntyni at debian.org>
+# Copyright © 2014-2015 Jérémy Bobbio <lunar at debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+use strict;
+use warnings;
+
+use Carp;
+use Cwd;
+
+use Dpkg ();
+use Dpkg::Gettext;
+use Dpkg::Checksums;
+use Dpkg::ErrorHandling;
+use Dpkg::Arch qw(get_build_arch);
+use Dpkg::Build::Types;
+use Dpkg::BuildFlags;
+use Dpkg::BuildProfiles qw(get_build_profiles);
+use Dpkg::Control::Info;
+use Dpkg::Control::Fields;
+use Dpkg::Control;
+use Dpkg::Changelog::Parse;
+use Dpkg::Deps;
+use Dpkg::Dist::Files;
+use Dpkg::Util qw(:list);
+use Dpkg::Version;
+use Dpkg::Vendor qw(run_vendor_hook);
+
+textdomain('dpkg-dev');
+
+my $controlfile = 'debian/control';
+my $changelogfile = 'debian/changelog';
+my $changelogformat;
+my $fileslistfile = 'debian/files';
+my $uploadfilesdir = '..';
+my $admindir = $Dpkg::ADMINDIR;
+my $always_include_path = 0;
+my @build_profiles = get_build_profiles();
+my $buildinfo_format = '1.0';
+
+my $checksums = Dpkg::Checksums->new();
+my %archadded;
+my @archvalues;
+
+# There's almost the same function in dpkg-checkbuilddeps,
+# they probably should be factored out.
+sub parse_status {
+    my $status = shift;
+
+    my $facts = Dpkg::Deps::KnownFacts->new();
+    my %depends;
+    my @essential_pkgs;
+    local $/ = '';
+    open(my $status_fh, '<', $status)
+        or syserr(g_('cannot open %s'), $status);
+    while (<$status_fh>) {
+        next unless /^Status: .*ok installed$/m;
+
+        my ($package) = /^Package: (.*)$/m;
+        my ($version) = /^Version: (.*)$/m;
+        my ($arch) = /^Architecture: (.*)$/m;
+        my ($multiarch) = /^Multi-Arch: (.*)$/m;
+        $facts->add_installed_package($package, $version, $arch,
+                                      $multiarch);
+
+        if (/^Essential: yes$/m) {
+            push @essential_pkgs, $package;
+        }
+
+        if (/^Provides: (.*)$/m) {
+            my $provides = deps_parse($1, reduce_arch => 1, union => 1);
+            next if not defined $provides;
+            foreach (grep { $_->isa('Dpkg::Deps::Simple') }
+                                 $provides->get_deps())
+            {
+                $facts->add_provided_package($_->{package},
+                                    $_->{relation}, $_->{version},
+                                    $package);
+            }
+        }
+
+        ## no critic (RegularExpressions::ProhibitUnusedCapture)
+        if (/^(?:Pre-)?Depends: (.*)$/m) {
+            foreach (split(/,\s*/, $1)) {
+                push @{$depends{"$package:$arch"}}, $_;
+            }
+        }
+        ## use critic
+    }
+    close $status_fh;
+
+    return ($facts, \%depends, \@essential_pkgs);
+}
+
+sub append_deps {
+    my $pkgs = shift;
+
+    foreach my $dep_str (@_) {
+        next unless $dep_str;
+        my $deps = deps_parse($dep_str, reduce_restrictions => 1,
+                              build_dep => 1, build_profiles => \@build_profiles);
+        # We add every sub-dependencies as we can't know which package in an OR
+        # dependency has been effectively used.
+        deps_iterate($deps, sub {
+            push @{$pkgs},
+                $_[0]->{package} . (defined $_[0]->{archqual} ? ':' . $_[0]->{archqual} : '');
+            1 });
+    }
+}
+
+sub version {
+    printf g_("Debian %s version %s.\n"), $Dpkg::PROGNAME, $Dpkg::PROGVERSION;
+
+    printf g_('
+This is free software; see the GNU General Public License version 2 or
+later for copying conditions. There is NO warranty.
+');
+}
+
+sub usage {
+    printf g_(
+'Usage: %s [<option>...]')
+    . "\n\n" . g_(
+"Options:
+  --build=<type>[,...]     specify the build <type>: full, source, binary,
+                             any, all (default is \'full\').
+  -B                       build of only arch-specific files.
+  -A                       build of only arch-indep files.
+  -c<control-file>         get control info from this file.
+  -l<changelog-file>       get per-version info from this file.
+  -f<files-list-file>      get .deb files list from this file.
+  -F<changelog-format>     force changelog format.
+  -u<upload-files-dir>     directory with files (default is '..').
+  --admindir=<directory>   change the administrative directory.
+  --always-include-path    always include Build-Path
+  -?, --help               show this help message.
+      --version            show the version.
+"), $Dpkg::PROGNAME;
+}
+
+while (@ARGV) {
+    $_ = shift(@ARGV);
+    if (m/^--build=(.*)$/) {
+        set_build_type_from_options($1, $_);
+    } elsif (m/^-B$/) {
+        set_build_type(BUILD_ARCH_DEP, $_);
+    } elsif (m/^-A$/) {
+        set_build_type(BUILD_ARCH_INDEP, $_);
+    } elsif (m/^-c(.*)$/) {
+        $controlfile = $1;
+    } elsif (m/^-l(.*)$/) {
+        $changelogfile = $1;
+    } elsif (m/^-f(.*)$/) {
+        $fileslistfile = $1;
+    } elsif (m/^-F([0-9a-z]+)$/) {
+        $changelogformat = $1;
+    } elsif (m/^-u(.*)$/) {
+        $uploadfilesdir = $1;
+    } elsif (m/^--admindir=(.*)$/) {
+        $admindir = $1;
+    } elsif (m/^--always-include-path$/) {
+        $always_include_path = 1;
+    } elsif (m/^-(?:\?|-help)$/) {
+        usage();
+        exit(0);
+    } elsif (m/^--version$/) {
+        version();
+        exit(0);
+    } else {
+        usageerr(g_("unknown option \`%s'"), $_);
+    }
+}
+
+my $control = Dpkg::Control::Info->new($controlfile);
+my $fields = Dpkg::Control->new(type => CTRL_FILE_BUILDINFO);
+my $dist = Dpkg::Dist::Files->new();
+
+# Retrieve info from the current changelog entry
+my %options = (file => $changelogfile);
+$options{changelogformat} = $changelogformat if $changelogformat;
+my $changelog = changelog_parse(%options);
+
+# Retrieve info from the former changelog entry to handle binNMUs
+$options{count} = 1;
+$options{offset} = 1;
+my $prev_changelog = changelog_parse(%options);
+
+my $sourceversion = $changelog->{'Binary-Only'} ?
+                    $prev_changelog->{'Version'} : $changelog->{'Version'};
+my $binaryversion = $changelog->{'Version'};
+
+# include .dsc if available
+my $spackage = $changelog->{'Source'};
+(my $sversion = $sourceversion) =~ s/^\d+://;
+my $dsc = "${spackage}_${sversion}.dsc";
+my $dsc_pathname = "$uploadfilesdir/$dsc";
+if ( -e $dsc_pathname) {
+    $checksums->add_from_file($dsc_pathname, key => $dsc);
+} else {
+    warning(g_('no .dsc file found: the resulting .buildinfo will not be ' .
+               'usable to verify the provenance of the binaries.'));
+}
+
+my $dist_count = 0;
+
+$dist_count = $dist->load($fileslistfile) if -e $fileslistfile;
+
+warning(g_('binary build with no binary artifacts found; .buildinfo will be meaningless'))
+    if $dist_count == 0;
+
+foreach my $file ($dist->get_files()) {
+    my $path = "$uploadfilesdir/$file->{filename}";
+    $checksums->add_from_file($path, key => $file->{filename});
+
+    if (defined $file->{arch}) {
+        push @archvalues, $file->{arch}
+            if $file->{arch} and not $archadded{$file->{arch}}++;
+    }
+}
+
+my $build_arch = get_build_arch();
+
+$fields->{'Format'} = $buildinfo_format;
+$fields->{'Build-Architecture'} = $build_arch;
+
+$fields->{'Source'} = $spackage;
+if ($changelog->{'Binary-Only'}) {
+    $fields->{'Source'} .= ' (' . $sourceversion . ')';
+    $fields->{'Binary-Only-Changes'} =
+        $changelog->{'Changes'} . "\n\n"
+        . ' -- ' . $changelog->{'Maintainer'}
+        . '  ' . $changelog->{'Date'};
+}
+
+$fields->{'Binary'} = join(' ', map { $_->{'Package'} } $control->get_packages());
+# Avoid overly long line by splitting over multiple lines
+if (length($fields->{'Binary'}) > 980) {
+    $fields->{'Binary'} =~ s/(.{0,980}) /$1\n/g;
+}
+
+$fields->{'Architecture'} = join ' ', sort @archvalues;
+$fields->{'Version'} = $binaryversion;
+
+my $cwd = cwd();
+if ($always_include_path) {
+    $fields->{'Build-Path'} = $cwd;
+} else {
+    # Only include build path if its root path is considered acceptable by the vendor
+    foreach my $root_path (run_vendor_hook('builtin-system-build-paths')) {
+        if (index($cwd, $root_path) == 0) {
+            $fields->{'Build-Path'} = $cwd;
+            last;
+        }
+    }
+}
+
+$checksums->export_to_control($fields);
+
+my ($facts, $depends, $essential_pkgs) = parse_status("$admindir/status");
+my %seen_pkgs;
+my @unprocessed_pkgs;
+
+# parse essential list
+
+append_deps(\@unprocessed_pkgs,
+            @{$essential_pkgs},
+            run_vendor_hook('builtin-build-depends'),
+            $control->get_source->{'Build-Depends'});
+
+if (build_has_any(BUILD_ARCH_DEP)) {
+    append_deps(\@unprocessed_pkgs, $control->get_source->{'Build-Depends-Arch'});
+}
+
+if (build_has_any(BUILD_ARCH_INDEP)) {
+    append_deps(\@unprocessed_pkgs, $control->get_source->{'Build-Depends-Indep'});
+}
+
+my $installed_deps = Dpkg::Deps::AND->new();
+while (my $pkg_name = shift @unprocessed_pkgs) {
+    next if $seen_pkgs{$pkg_name};
+    $seen_pkgs{$pkg_name} = 1;
+
+    my $required_architecture;
+    if ($pkg_name =~ /\A(.*):(.*)\z/) {
+        $pkg_name = $1;
+        my $arch = $2;
+        $required_architecture = $arch if $arch !~ /\A(?:all|any|native)\Z/
+    }
+    my $pkg;
+    my $qualified_pkg_name;
+    foreach my $installed_pkg (@{$facts->{pkg}->{$pkg_name}}) {
+        if (!defined $required_architecture || $required_architecture eq $installed_pkg->{architecture}) {
+            $pkg = $installed_pkg;
+            $qualified_pkg_name = $pkg_name . ':' . $installed_pkg->{architecture};
+            last;
+        }
+    }
+    if (defined $pkg) {
+        my $version = $pkg->{version};
+        my $architecture = $pkg->{architecture};
+        my $new_deps_str = defined $depends->{$qualified_pkg_name} ? deps_concat(@{$depends->{$qualified_pkg_name}}) : '';
+        my $new_deps = deps_parse($new_deps_str);
+        if (!defined $required_architecture) {
+            $installed_deps->add(Dpkg::Deps::Simple->new("$pkg_name (= $version)"));
+        } else {
+            $installed_deps->add(Dpkg::Deps::Simple->new("$qualified_pkg_name (= $version)"));
+            # Dependencies of foreign packages are also foreign packages (or Arch:all)
+            # so we need to qualify them as well. We figure out if the package is actually
+            # foreign by searching for an installed package of the right architecture.
+            deps_iterate($new_deps, sub {
+                my $dep = shift;
+                $dep->{archqual} //= $architecture
+                    if any { $_[0]->{architecture} eq $architecture }, @{$facts->{pkg}->{$dep->{package}}};
+                1;
+            });
+        }
+        # We add every sub-dependencies as we can't know which package in an OR
+        # dependency has been effectively used.
+        deps_iterate($new_deps, sub {
+            push @unprocessed_pkgs,
+                $_[0]->{package} . (defined $_[0]->{archqual} ? ':' . $_[0]->{archqual} : '');
+            1 });
+    } elsif (defined $facts->{virtualpkg}->{$pkg_name}) {
+        # virtual package: we can't know for sure which implementation
+        # is the one that has been used, so let's add them all...
+        foreach my $provided (@{$facts->{virtualpkg}->{$pkg_name}}) {
+            my ($provided_by, $provided_rel, $provided_ver) = @{$provided};
+            push @unprocessed_pkgs, $provided_by;
+        }
+    }
+    # else: it's a package in an OR dependency that has been satisfied otherwise
+}
+$installed_deps->sort();
+$installed_deps = "\n" . $installed_deps->output();
+$installed_deps =~ s/, /,\n/g;
+$fields->{'Installed-Build-Depends'} = $installed_deps;
+
+my $toolchain =
+    qr{( CC | CPP | CXX
+       | LD | AR | RANLIB
+       | MAKE | AWK
+       )}x;
+my $toolchain_flags =
+    qr{( CFLAGS | CPPFLAGS | CXXFLAGS
+       | OBJCFLAGS | OBJCXXFLAGS
+       | GCJFLAGS | FFLAGS
+       | LDFLAGS | ARFLAGS | MAKEFLAGS
+       )}x;
+my $environment_whitelist =
+    qr{\A( PATH
+         | ${toolchain}
+         | ${toolchain_flags}
+         | LD_LIBRARY_PATH # see ld(1)
+         | LANG | LC_(CTYPE|NUMERIC|TIME|COLLATE|MONETARY|MESSAGES
+                     |PAPER|NAME|ADDRESS|TELEPHONE|MEASUREMENT
+                     |IDENTIFICATION|ALL) # documented in locale(1)
+         | DEB_BUILD_OPTIONS # documented in dpkg-buildpackage(1)
+         | DEB_BUILD_PROFILES # documented in dpkg-buildpackage(1)
+                              # env var will be set if -P is given
+         # DEB_flag_{SET,STRIP,APPEND,PREPEND} will be recorded after
+         # being merged with system config and user config
+         | DEB_VENDOR # documented in deb-vendor(1)
+         | DPKG_DATADIR # documented in dpkg-architecure(1)
+         | DPKG_ORIGINS_DIR # used by Dpkg::Vendor
+         | SOURCE_DATE_EPOCH # see https://reproducible-builds.org/specs/source-date-epoch
+         )\z}x;
+
+my %environment = map { $_ => $ENV{$_} } (grep { /$environment_whitelist/ } sort keys %ENV);
+
+# Record flags from dpkg-buildflags
+my $bf = Dpkg::BuildFlags->new();
+$bf->load_system_config();
+$bf->load_user_config();
+$bf->load_environment_config();
+foreach my $flag ($bf->list()) {
+    if ($bf->get_origin($flag) ne 'vendor') {
+        $environment{"DEB_${flag}_SET"} = $bf->get($flag);
+        # We don't need to record *_{STRIP,APPEND,PREPEND} as they
+        # have been used already to compute the above value
+    }
+}
+
+$fields->{'Environment'} = "\n" .
+    join "\n", map { $_ . '="' . ($environment{$_} =~ s/"/\\"/gr) . '"' }
+                   (sort keys %environment);
+
+$fields->output(\*STDOUT);
diff --git a/scripts/po/POTFILES.in b/scripts/po/POTFILES.in
index ab99f47..2ba1705 100644
--- a/scripts/po/POTFILES.in
+++ b/scripts/po/POTFILES.in
@@ -5,6 +5,7 @@ scripts/dpkg-buildflags.pl
 scripts/dpkg-buildpackage.pl
 scripts/dpkg-checkbuilddeps.pl
 scripts/dpkg-distaddfile.pl
+scripts/dpkg-genbuildinfo.pl
 scripts/dpkg-genchanges.pl
 scripts/dpkg-gencontrol.pl
 scripts/dpkg-gensymbols.pl

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/reproducible/dpkg.git



More information about the Reproducible-commits mailing list