Просмотр исходного кода

Add experimental build profiles support

This adds the basic infrastructure support for a new class of generic
build-time dependency restrictions, and in particular implements the
specific build profiles, which will allow to cull build dependencies
depending on the profile being used. There's several things this can
be used for, like new port bootstrapping, reduced package builds, and
similar. In the future other kinds of restrictions could be added as
the build profiles are namespaced with “profile.”. An example field
could be:

  Build-Depends: exotic-compiler, libneeded-dev, tool-tiny,
   tool-huge (>= 1.0) [linux-any] <!profile.embedded !profile.bootstrap>

or even stuff like:

  Depends: net-tools <profile.network>, plugin-curl <!profile.no-plugins>

The generated binary packages and .changes files will get a new
Built-For-Profiles field containing the active profiles during the build.

In addition the build profile can be selected using the environment
variable DEB_BUILD_PROFILES, with space separated values, such as:

  DEB_BUILD_PROFILES="embedded bootstrap"

The management and possible registration in the profile namespace is
currently out of scope in dpkg, this should probably be handled by a
distribution specific process.

See draft <http://www.hadrons.org/~guillem/debian/docs/embedded.proposal>.

Closes: #661538

Based-on-patch-by: Patrick "P. J." McDermott <pjm@nac.net>
Based-on-patch-by: Wookey <wookey@debian.org>
Based-on-patch-by: Johannes Schauer <j.schauer@email.de>
Signed-off-by: Guillem Jover <guillem@debian.org>
Guillem Jover лет назад: 12
Родитель
Сommit
7662e0937b

+ 8 - 0
debian/changelog

@@ -109,6 +109,14 @@ dpkg (1.17.2) UNRELEASED; urgency=low
     the array in a subsequent package processing. Closes: #726112
   * Do not accept empty field names in dpkg.
   * Do not accept an initial hyphen in field names.
+  * Add experimental build profiles support:
+    - Add support for <!profile.name> build-time restrictions in dependencies.
+    - Add support for DEB_BUILD_PROFILES environment variable.
+    - Add new option -P to dpkg-buildpackage and dpkg-checbuilddeps.
+    - Add new Built-For-Profiles output field in .deb and .changes files.
+    Based on a patch by Patrick "P. J." McDermott <pjm@nac.net>,
+    Wookey <wookey@debian.org> and Johannes Schauer <j.schauer@email.de>.
+    Closes: #661538
 
   [ Updated programs translations ]
   * German (Sven Joachim).

+ 1 - 0
dpkg-deb/build.c

@@ -339,6 +339,7 @@ check_conffiles(const char *dir)
 }
 
 static const char *arbitrary_fields[] = {
+  "Built-For-Profiles",
   "Built-Using",
   "Package-Type",
   "Subarchitecture",

+ 10 - 4
man/deb-src-control.5

@@ -17,7 +17,7 @@
 .\" You should have received a copy of the GNU General Public License
 .\" along with this program.  If not, see <http://www.gnu.org/licenses/>.
 .
-.TH deb\-src\-control 5 "2013-04-02" "Debian Project" "Debian"
+.TH deb\-src\-control 5 "2013-09-17" "Debian Project" "Debian"
 .SH NAME
 deb\-src\-control \- Debian source packages' master control file format
 .
@@ -172,8 +172,9 @@ fields is a list of groups of alternative packages. Each group is a list
 of packages separated by vertical bar (or "pipe") symbols, "|". The
 groups are separated by commas. Commas are to be read as "AND", and pipes
 as "OR", with pipes binding more tightly. Each package name is
-optionally followed by a version number specification in parentheses and an
-architecture specification in square brackets.
+optionally followed by a version number specification in parentheses, an
+architecture specification in square brackets, and a profile specification
+in angle brackets.
 
 The syntax of the
 .BR Build\-Conflicts ,
@@ -183,7 +184,8 @@ and
 fields is a list of comma-separated package names, where the comma is read
 as an "AND". Specifying alternative packages using a "pipe" is not supported.
 Each package name is optionally followed by a version number specification in
-parentheses and an architecture specification in square brackets.
+parentheses, an architecture specification in square brackets, and a profile
+specification in angle brackets.
 
 A version number may start with a ">>", in which case any later version
 will match, and may specify or omit the Debian packaging revision (separated
@@ -195,6 +197,10 @@ A architecture specification consists of one or more architecture names,
 separated by whitespace. Exclamation marks may be prepended to each of the
 names, meaning "NOT".
 
+A profile specification consists of one or more profile names, prefixed
+with the "\fBprofile.\fP" namespace, separated by whitespace. Exclamation
+marks may be prepended to each of the names, meaning "NOT".
+
 Note that dependencies on packages in the
 .B build\-essential
 set can be omitted and that declaring build conflicts against them is

+ 13 - 1
man/dpkg-buildpackage.1

@@ -19,7 +19,7 @@
 .\" You should have received a copy of the GNU General Public License
 .\" along with this program.  If not, see <http://www.gnu.org/licenses/>.
 .
-.TH dpkg\-buildpackage 1 "2013-08-31" "Debian Project" "dpkg utilities"
+.TH dpkg\-buildpackage 1 "2013-09-17" "Debian Project" "dpkg utilities"
 .SH NAME
 dpkg\-buildpackage \- build binary or source packages from sources
 .
@@ -131,6 +131,13 @@ Specify the GNU system type we build for. It can be used in place
 of \-a or as a complement to override the default GNU system type
 of the target Debian architecture.
 .TP
+.BR \-P \fIprofile\fP[ , ...]
+Specify the profile(s) we build, as a comma-separated list, without the
+"\fBprofile.\fP" namespace prefix. The default behavior is to build for
+no specific profile. Also adds them (as a space separated list) to the
+\fBDEB_BUILD_PROFILES\fP environment variable which allows, for example,
+\fBdebian/rules\fP files to use this information for conditional builds.
+.TP
 .BI \-j jobs
 Number of jobs allowed to be run simultaneously, equivalent to the
 .BR make (1)
@@ -247,6 +254,11 @@ Show the version and exit.
 .B DEB_SIGN_KEYID
 If set, it will be used to sign the \fB.changes\fP and \fB.dsc\fP files.
 Overridden by the \fB\-k\fP option.
+.TP
+.B DEB_BUILD_PROFILES
+If set, it will be used as the active build profile(s) for the package
+being built. It is a space separated list of profile names, without the
+"\fBprofile.\fP" namespace prefix. Overridden by the \fB\-P\fP option.
 
 .SS Reliance on exported environment flags
 Even if \fBdpkg\-buildpackage\fP exports some variables, \fBdebian/rules\fP

+ 14 - 1
man/dpkg-checkbuilddeps.1

@@ -17,7 +17,7 @@
 .\" You should have received a copy of the GNU General Public License
 .\" along with this program.  If not, see <http://www.gnu.org/licenses/>.
 .
-.TH dpkg\-checkbuilddeps 1 "2012-05-22" "Debian Project" "dpkg utilities"
+.TH dpkg\-checkbuilddeps 1 "2013-09-17" "Debian Project" "dpkg utilities"
 .SH NAME
 dpkg\-checkbuilddeps \- check build dependencies and conflicts
 .
@@ -61,8 +61,21 @@ Check build dependencies/conflicts assuming that the package described in
 the control file is to be built for the given host architecture instead of
 the architecture of the current system.
 .TP
+.BR "\-P " \fIprofile\fP[ , ...]
+Check build dependencies/conflicts assuming that the package described
+in the control file is to be built for the given build profile(s). The
+argument is a comma-separated list of profile names, without the
+"\fBprofile.\fP" namespace prefix.
+.TP
 .BR \-? ", " \-\-help
 Show the usage message and exit.
 .TP
 .BR \-\-version
 Show the version and exit.
+.
+.SH ENVIRONMENT
+.TP
+.B DEB_BUILD_PROFILES
+If set, it will be used as the active build profile(s) for the package
+being built. It is a space separated list of profile names, without the
+"\fBprofile.\fP" namespace prefix. Overridden by the \fB\-P\fP option.

+ 82 - 0
scripts/Dpkg/BuildProfiles.pm

@@ -0,0 +1,82 @@
+# Copyright © 2013 Guillem Jover <guillem@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 <http://www.gnu.org/licenses/>.
+
+package Dpkg::BuildProfiles;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.01';
+our @EXPORT_OK = qw(get_build_profiles set_build_profiles);
+
+use Exporter qw(import);
+
+use Dpkg::BuildEnv;
+
+my $cache_profiles;
+my @build_profiles;
+
+=encoding utf8
+
+=head1 NAME
+
+Dpkg::BuildProfiles - handle build profiles
+
+=head1 DESCRIPTION
+
+The Dpkg::BuildProfiles module provides functions to handle the build
+profiles.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item my @profiles = get_build_profiles()
+
+Get an array with the currently active build profiles, taken from
+the environment variable B<DEB_BUILD_PROFILES>.
+
+=cut
+
+sub get_build_profiles {
+    return @build_profiles if $cache_profiles;
+
+    if (Dpkg::BuildEnv::has('DEB_BUILD_PROFILES')) {
+        @build_profiles = split / /, Dpkg::BuildEnv::get('DEB_BUILD_PROFILES');
+    }
+    $cache_profiles = 1;
+
+    return @build_profiles;
+}
+
+=item set_build_profiles(@profiles)
+
+Set C<@profiles> as the current active build profiles, by setting
+the environment variable B<DEB_BUILD_PROFILES>.
+
+=cut
+
+sub set_build_profiles {
+    my (@profiles) = @_;
+
+    @build_profiles = @profiles;
+    Dpkg::BuildEnv::set('DEB_BUILD_PROFILES', join ' ', @profiles);
+}
+
+=back
+
+=cut
+
+1;

+ 8 - 3
scripts/Dpkg/Control/FieldsCore.pm

@@ -111,6 +111,10 @@ our %FIELDS = (
         dependency => 'normal',
         dep_order => 3,
     },
+    'Built-For-Profiles' => {
+        allowed => ALL_PKG | CTRL_FILE_CHANGES,
+        separator => FIELD_SEP_SPACE,
+    },
     'Built-Using' => {
         allowed => ALL_PKG,
         separator => FIELD_SEP_COMMA,
@@ -337,7 +341,8 @@ my @sum_fields = map { $_ eq 'md5' ? 'MD5sum' : &field_capitalize($_) }
 our %FIELD_ORDER = (
     CTRL_PKG_DEB() => [
         qw(Package Package-Type Source Version Built-Using Kernel-Version
-        Architecture Subarchitecture Installer-Menu-Item Essential Origin Bugs
+        Built-For-Profiles Architecture Subarchitecture
+        Installer-Menu-Item Essential Origin Bugs
         Maintainer Installed-Size), &field_list_pkg_dep(),
         qw(Section Priority Multi-Arch Homepage Description Tag Task)
     ],
@@ -349,8 +354,8 @@ our %FIELD_ORDER = (
         @checksum_fields, qw(Files)
     ],
     CTRL_FILE_CHANGES() => [
-        qw(Format Date Source Binary Binary-Only Architecture Version
-        Distribution Urgency Maintainer Changed-By Description
+        qw(Format Date Source Binary Binary-Only Built-For-Profiles Architecture
+        Version Distribution Urgency Maintainer Changed-By Description
         Closes Changes),
         @checksum_fields, qw(Files)
     ],

+ 98 - 0
scripts/Dpkg/Deps.pm

@@ -53,6 +53,7 @@ our $VERSION = '1.02';
 
 use Dpkg::Version;
 use Dpkg::Arch qw(get_host_arch get_build_arch);
+use Dpkg::BuildProfiles qw(get_build_profiles);
 use Dpkg::ErrorHandling;
 use Dpkg::Gettext;
 
@@ -195,6 +196,30 @@ architecture. This implicitely strips off the architecture restriction
 list so that the resulting dependencies are directly applicable to the
 current architecture.
 
+=item use_profiles (defaults to 1)
+
+Take into account the profile restriction part of the dependencies. Set
+to 0 to completely ignore that information.
+
+=item build_profiles (defaults to no profile)
+
+Define the active build profiles. By default no profile is defined.
+
+=item reduce_profiles (defaults to 0)
+
+If set to 1, ignore dependencies that do not concern the current build
+profile. This implicitly strips off the profile restriction list so
+that the resulting dependencies are directly applicable to the current
+profiles.
+
+=item reduce_restrictions (defaults to 0)
+
+If set to 1, ignore dependencies that do not concern the current set of
+restrictions. This implicitly strips off any restriction list so that the
+resulting dependencies are directly applicable to the current restriction.
+This currently implies C<reduce_arch> and C<reduce_profiles>, and overrides
+them if set.
+
 =item union (defaults to 0)
 
 If set to 1, returns a Dpkg::Deps::Union instead of a Dpkg::Deps::AND. Use
@@ -216,9 +241,19 @@ sub deps_parse {
     $options{reduce_arch} = 0 if not exists $options{reduce_arch};
     $options{host_arch} = get_host_arch() if not exists $options{host_arch};
     $options{build_arch} = get_build_arch() if not exists $options{build_arch};
+    $options{use_profiles} = 1 if not exists $options{use_profiles};
+    $options{reduce_profiles} = 0 if not exists $options{reduce_profiles};
+    $options{build_profiles} = [ get_build_profiles() ]
+        if not exists $options{build_profiles};
+    $options{reduce_restrictions} = 0 if not exists $options{reduce_restrictions};
     $options{union} = 0 if not exists $options{union};
     $options{build_dep} = 0 if not exists $options{build_dep};
 
+    if ($options{reduce_restrictions}) {
+        $options{reduce_arch} = 1;
+        $options{reduce_profiles} = 1;
+    }
+
     # Strip trailing/leading spaces
     $dep_line =~ s/^\s+//;
     $dep_line =~ s/\s+$//;
@@ -242,6 +277,11 @@ sub deps_parse {
 		$dep_simple->reduce_arch($options{host_arch});
 		next if not $dep_simple->arch_is_concerned($options{host_arch});
 	    }
+	    $dep_simple->{restrictions} = undef if not $options{use_profiles};
+	    if ($options{reduce_profiles}) {
+		$dep_simple->reduce_profiles($options{build_profiles});
+		next if not $dep_simple->profile_is_concerned($options{build_profiles});
+	    }
 	    push @or_list, $dep_simple;
         }
 	next if not @or_list;
@@ -470,6 +510,7 @@ use Dpkg::Arch qw(debarch_is);
 use Dpkg::Version;
 use Dpkg::ErrorHandling;
 use Dpkg::Gettext;
+use Dpkg::Util qw(:list);
 
 use parent qw(Dpkg::Interface::Storable);
 
@@ -493,6 +534,7 @@ sub reset {
     $self->{version} = undef;
     $self->{arches} = undef;
     $self->{archqual} = undef;
+    $self->{restrictions} = undef;
 }
 
 sub parse {
@@ -522,6 +564,11 @@ sub parse_string {
                 \s* (.*?)                   # don't parse architectures now
                 \s* \]                      # closing bracket
               )?                            # end of optional architecture
+              (?:                           # start of optional restriction
+                \s* <                       # open bracket for restriction
+                \s* (.*?)                   # don't parse restrictions now
+                \s* >                       # closing bracket
+              )?                            # end of optional restriction
               \s*$                          # trailing spaces at end
             }x;
     if (defined($2)) {
@@ -536,6 +583,9 @@ sub parse_string {
     if (defined($5)) {
 	$self->{arches} = [ split(/\s+/, $5) ];
     }
+    if (defined($6)) {
+	$self->{restrictions} = [ map { lc } split /\s+/, $6 ];
+    }
 }
 
 sub output {
@@ -550,6 +600,9 @@ sub output {
     if (defined($self->{arches})) {
 	$res .= ' [' . join(' ', @{$self->{arches}}) . ']';
     }
+    if (defined($self->{restrictions})) {
+	$res .= ' <' . join(' ', @{$self->{restrictions}}) . '>';
+    }
     if (defined($fh)) {
 	print { $fh } $res;
     }
@@ -764,6 +817,51 @@ sub has_arch_restriction {
     }
 }
 
+sub profile_is_concerned {
+    my ($self, $build_profiles) = @_;
+
+    return 0 if not defined $self->{package}; # Empty dep
+    return 1 if not defined $self->{restrictions}; # Dep without restrictions
+
+    my $seen_profile = 0;
+    foreach my $restriction (@{$self->{restrictions}}) {
+        # Determine if this restriction is negated, and within the "profile"
+        # namespace, otherwise it does not concern this check.
+        next if $restriction !~ m/^(!)?profile\.(.*)/;
+
+        my $negated = defined $1 && $1 eq '!';
+        my $profile = $2;
+
+        # Determine if the restriction matches any of the specified profiles.
+        my $found = any { $_ eq $profile } @{$build_profiles};
+
+        if ($negated) {
+            if ($found) {
+                $seen_profile = 0;
+                last;
+            } else {
+                # "!profile.this" includes by default all other profiles
+                # unless they also appear in a "!profile.other".
+                $seen_profile = 1;
+            }
+        } elsif ($found) {
+            $seen_profile = 1;
+            last;
+        }
+    }
+    return $seen_profile;
+}
+
+sub reduce_profiles {
+    my ($self, $build_profiles) = @_;
+
+    if (not $self->profile_is_concerned($build_profiles)) {
+        $self->reset();
+    } else {
+        $self->{restrictions} = undef;
+    }
+}
+
 sub get_evaluation {
     my ($self, $facts) = @_;
     return if not defined $self->{package};

+ 1 - 0
scripts/Makefile.am

@@ -58,6 +58,7 @@ nobase_dist_perllib_DATA = \
 	Dpkg/BuildEnv.pm \
 	Dpkg/BuildFlags.pm \
 	Dpkg/BuildOptions.pm \
+	Dpkg/BuildProfiles.pm \
 	Dpkg/Changelog.pm \
 	Dpkg/Changelog/Debian.pm \
 	Dpkg/Changelog/Entry.pm \

+ 7 - 0
scripts/dpkg-buildpackage.pl

@@ -31,6 +31,7 @@ use Dpkg ();
 use Dpkg::Gettext;
 use Dpkg::ErrorHandling;
 use Dpkg::BuildOptions;
+use Dpkg::BuildProfiles qw(set_build_profiles);
 use Dpkg::Compression;
 use Dpkg::Version;
 use Dpkg::Changelog::Parse;
@@ -62,6 +63,7 @@ sub usage {
   -tc            clean source tree when finished.
   -D (default)   check build dependencies and conflicts.
   -d             do not check build dependencies and conflicts.
+  -P<profiles>   assume given build profiles as active (comma-separated list).
   -R<rules>      rules file to execute (default is debian/rules).
   -T<target>     call debian/rules <target> with the proper environment.
       --as-root  ensure -T calls the target with root rights.
@@ -123,6 +125,7 @@ my $signchanges = 1;
 my $buildtarget = 'build';
 my $binarytarget = 'binary';
 my $targetarch = my $targetgnusystem = '';
+my @build_profiles = ();
 my $call_target = '';
 my $call_target_as_root = 0;
 my (@checkbuilddep_opts, @changes_opts, @source_opts);
@@ -187,6 +190,8 @@ while (@ARGV) {
 	$usepause = 1;
     } elsif (/^-a(.*)$/) {
 	$targetarch = $1;
+    } elsif (/^-P(.*)$/) {
+	@build_profiles = split /,/, $1;
     } elsif (/^-s[iad]$/) {
 	push @changes_opts, $_;
     } elsif (/^-(?:s[insAkurKUR]|[zZ].*|i.*|I.*)$/) {
@@ -283,6 +288,8 @@ if (defined $parallel) {
     $build_opts->export();
 }
 
+set_build_profiles(@build_profiles) if @build_profiles;
+
 my $cwd = cwd();
 my $dir = basename($cwd);
 

+ 14 - 4
scripts/dpkg-checkbuilddeps.pl

@@ -28,6 +28,7 @@ use Dpkg ();
 use Dpkg::Gettext;
 use Dpkg::ErrorHandling;
 use Dpkg::Arch qw(get_host_arch);
+use Dpkg::BuildProfiles qw(get_build_profiles set_build_profiles);
 use Dpkg::Deps;
 use Dpkg::Control::Info;
 
@@ -51,6 +52,7 @@ sub usage {
   -c build-conf  use given string for build conflicts instead of
                  retrieving them from control file
   -a arch        assume given host architecture
+  -P profiles    assume given build profiles (comma-separated list)
   --admindir=<directory>
                  change the administrative directory.
   -?, --help     show this help message.
@@ -63,6 +65,7 @@ sub usage {
 my $ignore_bd_arch = 0;
 my $ignore_bd_indep = 0;
 my ($bd_value, $bc_value);
+my $bp_value;
 my $host_arch = get_host_arch();
 my $admindir = $Dpkg::ADMINDIR;
 my @options_spec = (
@@ -73,6 +76,7 @@ my @options_spec = (
     'd=s' => \$bd_value,
     'c=s' => \$bc_value,
     'a=s' => \$host_arch,
+    'P=s' => \$bp_value,
     'admindir=s' => \$admindir,
 );
 
@@ -81,6 +85,10 @@ my @options_spec = (
     GetOptions(@options_spec);
 }
 
+# Update currently active build profiles.
+set_build_profiles(split(/,/, $bp_value)) if ($bp_value);
+my @build_profiles = get_build_profiles();
+
 my $controlfile = shift || 'debian/control';
 
 my $control = Dpkg::Control::Info->new($controlfile);
@@ -103,13 +111,15 @@ my (@unmet, @conflicts);
 
 if ($bd_value) {
 	push @unmet, build_depends('Build-Depends/Build-Depends-Arch/Build-Depends-Indep',
-		deps_parse($bd_value, build_dep => 1, host_arch => $host_arch,
-			   reduce_arch => 1), $facts);
+		deps_parse($bd_value, reduce_restrictions => 1, build_dep => 1,
+			   build_profiles => \@build_profiles,
+			   host_arch => $host_arch), $facts);
 }
 if ($bc_value) {
 	push @conflicts, build_conflicts('Build-Conflicts/Build-Conflicts-Arch/Build-Conflicts-Indep',
-		deps_parse($bc_value, build_dep => 1, host_arch => $host_arch,
-			   reduce_arch => 1, union => 1), $facts);
+		deps_parse($bc_value, reduce_restrictions => 1, build_dep => 1,
+			   build_profiles => \@build_profiles, union => 1,
+			   host_arch => $host_arch), $facts);
 }
 
 if (@unmet) {

+ 3 - 0
scripts/dpkg-genchanges.pl

@@ -30,6 +30,7 @@ use Dpkg::Util qw(:list);
 use Dpkg::File;
 use Dpkg::Checksums;
 use Dpkg::ErrorHandling;
+use Dpkg::BuildProfiles qw(get_build_profiles);
 use Dpkg::Arch qw(get_host_arch debarch_eq debarch_is);
 use Dpkg::Compression;
 use Dpkg::Control::Info;
@@ -470,6 +471,8 @@ unshift(@archvalues,'source') unless is_binaryonly;
     unless $include & ARCH_INDEP;
 $fields->{'Architecture'} = join(' ',@archvalues);
 
+$fields->{'Built-For-Profiles'} = join ' ', get_build_profiles();
+
 $fields->{'Description'} = "\n" . join("\n", sort @descriptions);
 
 $fields->{'Files'} = '';

+ 8 - 3
scripts/dpkg-gencontrol.pl

@@ -30,6 +30,7 @@ use Dpkg::Util qw(:list);
 use Dpkg::File;
 use Dpkg::Arch qw(get_host_arch debarch_eq debarch_is);
 use Dpkg::Package;
+use Dpkg::BuildProfiles qw(get_build_profiles);
 use Dpkg::Deps;
 use Dpkg::Control;
 use Dpkg::Control::Info;
@@ -253,7 +254,7 @@ $facts->add_installed_package($fields->{'Package'}, $fields->{'Version'},
                               $fields->{'Architecture'}, $fields->{'Multi-Arch'});
 if (exists $pkg->{'Provides'}) {
     my $provides = deps_parse($substvars->substvars($pkg->{'Provides'}, no_warn => 1),
-                              reduce_arch => 1, union => 1);
+                              reduce_restrictions => 1, union => 1);
     if (defined $provides) {
 	foreach my $subdep ($provides->get_deps()) {
 	    if ($subdep->isa('Dpkg::Deps::Simple')) {
@@ -275,7 +276,8 @@ foreach my $field (field_list_pkg_dep()) {
 	    msg_prefix => sprintf(_g('%s field of package %s: '), $field, $pkg->{Package}));
 	if (field_get_dep_type($field) eq 'normal') {
 	    $dep = deps_parse($field_value, use_arch => 1,
-			      reduce_arch => $reduce_arch);
+	                      reduce_arch => $reduce_arch,
+	                      reduce_profiles => 1);
 	    error(_g('error occurred while parsing %s field: %s'), $field,
                   $field_value) unless defined $dep;
 	    $dep->simplify_deps($facts, @seen_deps);
@@ -283,7 +285,8 @@ foreach my $field (field_list_pkg_dep()) {
 	    push @seen_deps, $dep;
 	} else {
 	    $dep = deps_parse($field_value, use_arch => 1,
-                              reduce_arch => $reduce_arch, union => 1);
+	                      reduce_arch => $reduce_arch,
+	                      reduce_profiles => 1, union => 1);
 	    error(_g('error occurred while parsing %s field: %s'), $field,
                   $field_value) unless defined $dep;
 	    $dep->simplify_deps($facts);
@@ -297,6 +300,8 @@ foreach my $field (field_list_pkg_dep()) {
     }
 }
 
+$fields->{'Built-For-Profiles'} = join ' ', get_build_profiles();
+
 for my $f (qw(Package Version)) {
     error(_g('missing information for output field %s'), $f)
         unless defined $fields->{$f};

+ 1 - 1
scripts/dpkg-shlibdeps.pl

@@ -147,7 +147,7 @@ usageerr(_g('need at least one executable')) unless scalar keys %exec;
 my $control = Dpkg::Control::Info->new();
 my $fields = $control->get_source();
 my $bd_value = deps_concat($fields->{'Build-Depends'}, $fields->{'Build-Depends-Arch'});
-my $build_deps = deps_parse($bd_value, build_dep => 1, reduce_arch => 1);
+my $build_deps = deps_parse($bd_value, build_dep => 1, reduce_restrictions => 1);
 error(_g('error occurred while parsing %s'), 'Build-Depends/Build-Depends-Arch')
     unless defined $build_deps;
 

+ 21 - 1
scripts/t/400_Dpkg_Deps.t

@@ -16,7 +16,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 20;
+use Test::More tests => 25;
 use Dpkg::Arch qw(get_host_arch);
 
 use_ok('Dpkg::Deps');
@@ -58,6 +58,26 @@ is($dep_i386->output(), 'libc6 (>= 2.5)', 'Arch reduce 1/3');
 is($dep_alpha->output(), 'libc6.1', 'Arch reduce 2/3');
 is($dep_hurd->output(), 'libc0.1', 'Arch reduce 3/3');
 
+my $field_profile = 'dep1 <!profile.stage1 !profile.notest>, ' .
+'dep2 <profile.stage1 !profile.notest>, ' .
+'dep3 <profile.notest !profile.stage1>, ' .
+'dep4 <profile.stage1 profile.notest>, ' .
+'dep5 <profile.stage1>, dep6 <!profile.stage1>';
+my $dep_noprof = deps_parse($field_profile, reduce_profiles => 1, build_profiles => []);
+my $dep_stage1 = deps_parse($field_profile, reduce_profiles => 1, build_profiles => ['stage1']);
+my $dep_notest = deps_parse($field_profile, reduce_profiles => 1, build_profiles => ['notest']);
+my $dep_stage1notest = deps_parse($field_profile, reduce_profiles => 1, build_profiles => ['stage1', 'notest']);
+is($dep_noprof->output(), 'dep1, dep2, dep3, dep6', 'Profile reduce 1/4');
+is($dep_stage1->output(), 'dep2, dep4, dep5', 'Profile reduce 2/4');
+is($dep_notest->output(), 'dep3, dep4, dep6', 'Profile reduce 3/4');
+is($dep_stage1notest->output(), 'dep2, dep3, dep4, dep5', 'Profile reduce 4/4');
+
+my $field_restrict = 'dep1 <!profile.bootstrap !other.restrict>, ' .
+'dep2 <profile.bootstrap other.restrict>, ' .
+'dep3 <!other.restrict>, ' .
+'dep4 <other.restrict>';
+my $dep_restrict = deps_parse($field_restrict, reduce_restrictions => 1, build_profiles => []);
+is($dep_restrict->output(), 'dep1', 'Unknown restrictions reduce');
 
 my $facts = Dpkg::Deps::KnownFacts->new();
 $facts->add_installed_package('mypackage', '1.3.4-1', get_host_arch(), 'no');