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

scripts: Update restriction formula syntax

 - The restriction list parsing now reflects the changes at
   <https://wiki.debian.org/BuildProfileSpec> which were agreed upon
   in the bootstrap sprint 2014 in Paris.
 - Restriction lists are now restriction formulas.
 - Restriction formulas are given in disjunctive normal form expression:
       pkg <bar baz> <blub>
 - Removal of the implicit prefix/namespace mechanic.
 - Since there can be more than one <> block, the regex in
   parse_string() in Dpkg::Deps is now greedy.
 - Construct the profiles entry of the Packages-List field by converting
   the "<bar baz> <blub>" syntax into "bar,baz+blub".
 - Include a temporary compatibility mapping for the old way to write
   the Build-Profiles field in binary packages which can be removed once
   all affected source packages have moved to the new syntax.
 - Adjust testcases.

Closes: #760158

[guillem@debian.org:
 - Add a warning when using the old syntax in dpkg-source.
 - Move comments before the regexes, not besides them in dpkg-source.
 - Change from an xnor to == in evaluate_restriction_formula().
 - Move the Dpkg::Util import close to the other Dpkg imports.
 - Add test cases for the new parse_build_profiles() behavior. ]

Signed-off-by: Guillem Jover <guillem@debian.org>
Johannes Schauer лет назад: 12
Родитель
Сommit
98956c26a2

+ 11 - 0
debian/changelog

@@ -72,6 +72,17 @@ dpkg (1.17.14) UNRELEASED; urgency=low
   * Map i786 to i386 also for the multiarch pathname in dpkg-architecture.
   * Handle omitted binary packages due to build profiles in dpkg-genchanges.
     Based on a patch by Johannes Schauer <j.schauer@email.de>. Closes: #758191
+  * Update restriction formula syntax for build profiles:
+    - Restriction lists are now restriction formulas.
+    - Restriction formulas are given in disjunctive normal form expression:
+      <foo> <bar baz> <blub>
+    - Removal of the implicit prefix/namespace mechanic.
+    - Construct the profiles entry of the Packages-List field by converting
+      the "<bar baz> <blub>" syntax into "bar,baz+blub".
+    - Include a temporary compatibility mapping with the old way to write
+      the Build-Profiles field in binary packages which can be removed once
+      all affected source packages have moved to the new syntax.
+    Thanks to Johannes Schauer <j.schauer@email.de>. Closes: #760158
 
   [ Raphaël Hertzog ]
   * Explain better in deb-triggers(5) why interest/activate-noawait should be

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

@@ -187,8 +187,8 @@ 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, an
-architecture specification in square brackets, and a profile specification
-in angle brackets.
+architecture specification in square brackets, and a restriction formula
+consisting of one or more lists of profile names in angle brackets.
 
 The syntax of the
 .BR Build\-Conflicts ,
@@ -198,8 +198,9 @@ 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, an architecture specification in square brackets, and a profile
-specification in angle brackets.
+parentheses, an architecture specification in square brackets, and a
+restriction formula consisting of one or more lists of profile names 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
@@ -211,9 +212,11 @@ An 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".
+A restriction formula consists of one or more restriction lists, separated
+by whitespace. Each restriction list is enclosed in angle brackets. Items
+in the restriction list are build profile names, separated by whitespace
+and can be prefixed with an exclamation mark, meaning "NOT". A restriction
+formula represents a disjunctive normal form expression.
 
 Note that dependencies on packages in the
 .B build\-essential

+ 7 - 7
man/dpkg-buildpackage.1

@@ -150,11 +150,11 @@ 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.
+Specify the profile(s) we build, as a comma-separated list. The default
+behavior is to build for no specific profile. Also sets them (as a space
+separated list) as the \fBDEB_BUILD_PROFILES\fP environment variable which
+allows, for example, \fBdebian/rules\fP files to use this information for
+conditional builds.
 .TP
 .BR \-j [\fIjobs\fP|\fBauto\fP]
 Number of jobs allowed to be run simultaneously, number of jobs matching
@@ -341,8 +341,8 @@ will be ignored.
 .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.
+being built. It is a space separated list of profile names. Overridden
+by the \fB\-P\fP option.
 
 .SS Reliance on exported environment flags
 Even if \fBdpkg\-buildpackage\fP exports some variables, \fBdebian/rules\fP

+ 3 - 4
man/dpkg-checkbuilddeps.1

@@ -64,8 +64,7 @@ the architecture of the current system.
 .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.
+argument is a comma-separated list of profile names.
 .TP
 .BR \-? ", " \-\-help
 Show the usage message and exit.
@@ -77,5 +76,5 @@ Show the version and exit.
 .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.
+being built. It is a space separated list of profile names. Overridden
+by the \fB\-P\fP option.

+ 25 - 24
scripts/Dpkg/BuildProfiles.pm

@@ -24,6 +24,7 @@ our @EXPORT_OK = qw(get_build_profiles set_build_profiles parse_build_profiles
 
 use Exporter qw(import);
 
+use Dpkg::Util qw(:list);
 use Dpkg::BuildEnv;
 
 my $cache_profiles;
@@ -78,53 +79,53 @@ sub set_build_profiles {
 
 =item my @profiles = parse_build_profiles($string)
 
-Parses a build profiles specification, into an array.
+Parses a build profiles specification, into an array of array references.
 
 =cut
 
 sub parse_build_profiles {
     my $string = shift;
 
-    return map { lc } split /\s+/, $string;
+    $string =~ s/^\s*<(.*)>\s*$/$1/;
+
+    return map { [ split /\s+/ ] } split />\s+</, $string;
 }
 
 =item evaluate_restriction_formula(\@formula, \@profiles)
 
-Evaluate whether a restriction list, is true or false, given the array of
-enabled build profiles.
+Evaluate whether a restriction formula of the form "<foo bar> <baz>", given as
+a nested array, is true or false, given the array of enabled build profiles.
 
 =cut
 
 sub evaluate_restriction_formula {
-    my ($restrictions, $build_profiles) = @_;
+    my ($formula, $profiles) = @_;
 
-    my $seen_profile = 0;
-    foreach my $restriction (@{$restrictions}) {
-        # Determine if this restriction is negated, and within the "profile"
-        # namespace, otherwise it does not concern this check.
-        next if $restriction !~ m/^(!)?profile\.(.*)/;
+    # Restriction formulas are in disjunctive normal form:
+    # (foo AND bar) OR (blub AND bla)
+    foreach my $restrlist (@{$formula}) {
+        my $seen_profile = 1;
 
-        my $negated = defined $1 && $1 eq '!';
-        my $profile = $2;
+        foreach my $restriction (@$restrlist) {
+            next if $restriction !~ m/^(!)?(.+)/;
 
-        # Determine if the restriction matches any of the specified profiles.
-        my $found = any { $_ eq $profile } @{$build_profiles};
+            my $negated = defined $1 && $1 eq '!';
+            my $profile = $2;
+            my $found = any { $_ eq $profile } @{$profiles};
 
-        if ($negated) {
-            if ($found) {
+            # If a negative set profile is encountered, stop processing.
+            # If a positive unset profile is encountered, stop processing.
+            if ($found == $negated) {
                 $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;
         }
+
+        # This conjunction evaluated to true so we don't have to evaluate
+        # the others.
+        return 1 if $seen_profile;
     }
-    return $seen_profile;
+    return 0;
 }
 
 =back

+ 8 - 5
scripts/Dpkg/Deps.pm

@@ -209,15 +209,16 @@ 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
+profile. This implicitly strips off the profile restriction formula 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.
+restrictions. This implicitly strips off any architecture restriction list
+or restriction formula 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.
 
@@ -601,7 +602,7 @@ sub parse_string {
               )?                            # end of optional architecture
               (?:                           # start of optional restriction
                 \s* <                       # open bracket for restriction
-                \s* (.*?)                   # don't parse restrictions now
+                \s* (.*)                    # do not parse restrictions now
                 \s* >                       # closing bracket
               )?                            # end of optional restriction
               \s*$                          # trailing spaces at end
@@ -636,7 +637,9 @@ sub output {
 	$res .= ' [' . join(' ', @{$self->{arches}}) . ']';
     }
     if (defined($self->{restrictions})) {
-	$res .= ' <' . join(' ', @{$self->{restrictions}}) . '>';
+        for my $restrlist (@{$self->{restrictions}}) {
+            $res .= ' <' . join(' ', @{$restrlist}) . '>';
+        }
     }
     if (defined($fh)) {
 	print { $fh } $res;

+ 25 - 2
scripts/dpkg-source.pl

@@ -273,8 +273,31 @@ if ($options{opmode} =~ /^(-b|--print-format|--(before|after)-build|--commit)$/)
         my $pkg_summary = sprintf('%s %s %s %s', $p, $type, $sect, $prio);
 
         $pkg_summary .= ' arch=' . join ',', split /\s+/, $arch;
-        $pkg_summary .= ' profile=' . join ',', split /\s+/, $profile
-            if defined $profile;
+
+        if (defined $profile) {
+            # If the string does not contain brackets then it is using the
+            # old syntax (glibc, file, dbus and doxygen are affected).
+            # Thus we convert the old syntax to the new one. This conversion
+            # can be dropped once the old syntax is not in the archive anymore.
+            # <http://codesearch.debian.net/search?q=Build-Profiles%3A\s%2B[^<]+path%3Adebian%2Fcontrol>
+            if ($profile !~ m/^\s*<.*>\s*$/) {
+                # Issue an ephemereal non-translatable warning.
+                warning('binary package stanza %s is using an obsolete ' .
+                        'Build-Profiles syntax', $p);
+                $profile =~ s/([!a-z0-9]+)/<$1>/g;
+            }
+
+            # Instead of splitting twice and then joining twice, we just do
+            # simple string replacements:
+
+            # Remove the enclosing <>
+            $profile =~ s/^\s*<(.*)>\s*$/$1/;
+            # Join lists with a plus (OR)
+            $profile =~ s/>\s+</+/g;
+            # Join their elements with a comma (AND)
+            $profile =~ s/\s+/,/g;
+            $pkg_summary .= " profile=$profile";
+        }
 
         push @pkglist, $pkg_summary;
 	push @binarypackages, $p;

+ 21 - 4
scripts/t/Dpkg_BuildProfiles.t

@@ -16,7 +16,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 2;
+use Test::More tests => 6;
 
 BEGIN {
     use_ok('Dpkg::BuildProfiles', qw(parse_build_profiles));
@@ -24,9 +24,26 @@ BEGIN {
 
 # TODO: Add actual test cases.
 
-my @build_profiles = qw(nocheck nodoc stage1);
+my $formula;
 
-is(parse_build_profiles('nocheck nodoc stage1'), @build_profiles,
-   'parse build profiles');
+$formula = [ ];
+is_deeply([ parse_build_profiles('') ], $formula,
+    'parse build profiles formula empty');
+
+$formula = [ [ qw(nocheck) ] ];
+is_deeply([ parse_build_profiles('<nocheck>') ], $formula,
+    'parse build profiles formula single');
+
+$formula = [ [ qw(nocheck nodoc stage1) ] ];
+is_deeply([ parse_build_profiles('<nocheck nodoc stage1>') ], $formula,
+    'parse build profiles formula AND');
+
+$formula = [ [ qw(nocheck) ], [ qw(nodoc) ] ];
+is_deeply([ parse_build_profiles('<nocheck> <nodoc>') ], $formula,
+    'parse build profiles formula OR');
+
+$formula = [ [ qw(nocheck nodoc) ], [ qw(stage1) ] ];
+is_deeply([ parse_build_profiles('<nocheck nodoc> <stage1>') ], $formula,
+    'parse build profiles formula AND, OR');
 
 1;

+ 29 - 21
scripts/t/Dpkg_Deps.t

@@ -70,20 +70,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>, ' .
-'dep7 <profile.stage1> | dep8 <profile.notest>';
+my $field_profile = 'dep1 <!stage1 !notest>, ' .
+'dep2 <stage1 !notest>, ' .
+'dep3 <notest !stage1>, ' .
+'dep4 <stage1 notest>, ' .
+'dep5 <stage1>, dep6 <!stage1>, ' .
+'dep7 <stage1> | dep8 <notest>, ' .
+'dep9 <!stage1> <!notest>, ' .
+'dep10 <stage1> <!notest>, ' .
+'dep11 <stage1> <notest>, '.
+'dep12 <!notest> <!stage1>, ' .
+'dep13 <notest> <!stage1>, ' .
+'dep14 <notest> <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, dep7', 'Profile reduce 2/4');
-is($dep_notest->output(), 'dep3, dep4, dep6, dep8', 'Profile reduce 3/4');
-is($dep_stage1notest->output(), 'dep2, dep3, dep4, dep5, dep7 | dep8', 'Profile reduce 4/4');
+is($dep_noprof->output(), 'dep1, dep6, dep9, dep10, dep12, dep13', 'Profile reduce 1/4');
+is($dep_stage1->output(), 'dep2, dep5, dep7, dep9, dep10, dep11, dep12, dep14', 'Profile reduce 2/4');
+is($dep_notest->output(), 'dep3, dep6, dep8, dep9, dep11, dep12, dep13, dep14', 'Profile reduce 3/4');
+is($dep_stage1notest->output(), 'dep4, dep5, dep7 | dep8, dep10, dep11, dep13, dep14', 'Profile reduce 4/4');
 
 $dep_noprof = deps_parse($field_profile);
 $dep_noprof->reduce_profiles([]);
@@ -93,21 +99,23 @@ $dep_notest = deps_parse($field_profile);
 $dep_notest->reduce_profiles(['notest']);
 $dep_stage1notest = deps_parse($field_profile);
 $dep_stage1notest->reduce_profiles(['stage1', 'notest']);
-is($dep_noprof->output(), 'dep1, dep2, dep3, dep6', 'Profile post-reduce 1/4');
-is($dep_stage1->output(), 'dep2, dep4, dep5, dep7', 'Profile post-reduce 2/4');
-is($dep_notest->output(), 'dep3, dep4, dep6, dep8', 'Profile post-reduce 3/4');
-is($dep_stage1notest->output(), 'dep2, dep3, dep4, dep5, dep7 | dep8', 'Profile post-reduce 4/4');
-
-my $field_restrict = 'dep1 <!profile.bootstrap !other.restrict>, ' .
-'dep2 <profile.bootstrap other.restrict>, ' .
-'dep3 <!other.restrict>, ' .
-'dep4 <other.restrict>';
+is($dep_noprof->output(), 'dep1, dep6, dep9, dep10, dep12, dep13', 'Profile post-reduce 1/4');
+is($dep_stage1->output(), 'dep2, dep5, dep7, dep9, dep10, dep11, dep12, dep14', 'Profile post-reduce 2/4');
+is($dep_notest->output(), 'dep3, dep6, dep8, dep9, dep11, dep12, dep13, dep14', 'Profile post-reduce 3/4');
+is($dep_stage1notest->output(), 'dep4, dep5, dep7 | dep8, dep10, dep11, dep13, dep14', 'Profile post-reduce 4/4');
+
+my $field_restrict = 'dep1 <!bootstrap !restrict>, ' .
+'dep2 <bootstrap restrict>, ' .
+'dep3 <!restrict>, ' .
+'dep4 <restrict>, ' .
+'dep5 <!bootstrap> <!restrict>, ' .
+'dep6 <bootstrap> <restrict>';
 my $dep_restrict = deps_parse($field_restrict, reduce_restrictions => 1, build_profiles => []);
-is($dep_restrict->output(), 'dep1', 'Unknown restrictions reduce');
+is($dep_restrict->output(), 'dep1, dep3, dep5', 'Unknown restrictions reduce');
 
 $dep_restrict = deps_parse($field_restrict);
 $dep_restrict->reduce_profiles([]);
-is($dep_restrict->output(), 'dep1', 'Unknown restrictions post-reduce');
+is($dep_restrict->output(), 'dep1, dep3, dep5', 'Unknown restrictions post-reduce');
 
 my $facts = Dpkg::Deps::KnownFacts->new();
 $facts->add_installed_package('mypackage', '1.3.4-1', get_host_arch(), 'no');