[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: build profile syntax ideas



Hi Guillem,

Quoting Guillem Jover (2013-08-16 14:15:04)
> > The current iteration of the Spec, after some refinement at debconf, is
> > here: https://wiki.debian.org/BuildProfileSpec
> 
> I've not checked that yet, will try to do that during this weekend.
> ISTM I had an issue with the new field being Profile instead of
> Build-Profile, for example. Will need to check my notes.

If you already got around to read it, please notice that we made a couple of
updates to the spec recently.

> While the bracket characters are limited, and using <> can be considered to
> waste them when we could reuse others, it's the cleaner and safer option. And
> I'm fine with allowing any namespace there, not just build profiles, so that
> we can have a slightly more future proof syntax. We could even consider
> switching from [] to <arch.foo> if desired for example.

We included the <> syntax as an alternate proposal in above spec.

To get this issue moving, I have attached a patch which implements the <>
version of the proposal. The patch is based upon one by wookey and pehjota [1]
and adds testcases, namespace support and the ability to activate more than one
profile at once.

My perl foo is close to nonexistant so I had to search online for every second
line I wrote for patch. So expect some rough edges here and there.

Let me repeat again that this issue has been open for years and if an
implementation is everything that is needed then here you go. Please review and
comment on my patch. I will implement any fixes necessary so that this issue
can go forward.

cheers, josch

[1] http://people.debian.org/~wookey/bootstrap/patches/profiles/tools/dpkg-build-profiles.patch
From 602caafda554f243f3ef7a860560e88f390f8fc4 Mon Sep 17 00:00:00 2001
From: josch <j.schauer@email.de>
Date: Tue, 17 Sep 2013 11:04:02 +0200
Subject: [PATCH] Implement build profiles

---
 dpkg-deb/build.c                   |    1 +
 man/deb-src-control.5              |    8 +++--
 man/dpkg-buildpackage.1            |   11 ++++++
 man/dpkg-checkbuilddeps.1          |   16 ++++++++-
 scripts/Dpkg/Control/FieldsCore.pm |    3 ++
 scripts/Dpkg/Deps.pm               |   70 ++++++++++++++++++++++++++++++++++++
 scripts/dpkg-buildpackage.pl       |   10 ++++++
 scripts/dpkg-checkbuilddeps.pl     |   13 +++++--
 scripts/dpkg-gencontrol.pl         |    4 +++
 scripts/t/400_Dpkg_Deps.t          |   13 ++++++-
 10 files changed, 142 insertions(+), 7 deletions(-)

diff --git a/dpkg-deb/build.c b/dpkg-deb/build.c
index 348e01e..45a982b 100644
--- a/dpkg-deb/build.c
+++ b/dpkg-deb/build.c
@@ -297,6 +297,7 @@ static const char *arbitrary_fields[] = {
   "Installer-Menu-Item",
   "Homepage",
   "Tag",
+  "Build-Profile",
   NULL
 };
 
diff --git a/man/deb-src-control.5 b/man/deb-src-control.5
index d924985..93d5d94 100644
--- a/man/deb-src-control.5
+++ b/man/deb-src-control.5
@@ -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
diff --git a/man/dpkg-buildpackage.1 b/man/dpkg-buildpackage.1
index 48b71e8..9e3f79d 100644
--- a/man/dpkg-buildpackage.1
+++ b/man/dpkg-buildpackage.1
@@ -131,6 +131,11 @@ 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
+.BI \-P profiles
+Specify the profile(s) we build. The default behavior is to build for no
+specific profile. If more than one profile is required, they are given as a
+comma separated list.
+.TP
 .BI \-j jobs
 Number of jobs allowed to be run simultaneously, equivalent to the
 .BR make (1)
@@ -250,6 +255,12 @@ respective interface to retrieve the needed values.
 \fBdpkg\-architecture\fP is called with the \fB\-a\fP and \fB\-t\fP
 parameters forwarded. Any variable that is output by its \fB\-s\fP
 option is integrated in the build environment.
+.TP
+.B DEB_BUILD_PROFILE
+Sets the build profile(s) for which the package is to be built. For more than
+one activated build profile, this variable contains a comma separated list of
+build profile names. If the \fB\-P\fP option is given, it takes precedence over 
+the value of this environment variable.
 .
 .SH NOTES
 .SS Compiler flags are no longer exported
diff --git a/man/dpkg-checkbuilddeps.1 b/man/dpkg-checkbuilddeps.1
index 41862d6..865eb38 100644
--- a/man/dpkg-checkbuilddeps.1
+++ b/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 "2012-07-14" "Debian Project" "dpkg utilities"
 .SH NAME
 dpkg\-checkbuilddeps \- check build dependencies and conflicts
 .
@@ -61,8 +61,22 @@ 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
+.BI "\-P " profiles
+Check build dependencies/conflicts assuming that the package described in
+the control file is to be built for the given build profile(s). If more than
+one profile is required, they are given as a comma separeted list or by
+specifying the argument multiple times.
+.TP
 .BR \-? ", " \-\-help
 Show the usage message and exit.
 .TP
 .BR \-\-version
 Show the version and exit.
+.
+.SH ENVIRONMENT
+.TP
+.B DEB_BUILD_PROFILE
+Sets the build profile(s) for which the package is to be built. For more than
+one activated build profile, this variable contains a comma separated list of
+build profile names. If the \fB\-P\fP option is given, it takes precedence over
+the value of this environment variable.
diff --git a/scripts/Dpkg/Control/FieldsCore.pm b/scripts/Dpkg/Control/FieldsCore.pm
index 44d90d1..5e7522a 100644
--- a/scripts/Dpkg/Control/FieldsCore.pm
+++ b/scripts/Dpkg/Control/FieldsCore.pm
@@ -111,6 +111,9 @@ our %FIELDS = (
         dependency => 'normal',
         dep_order => 3,
     },
+    'Build-Profile' => {
+        allowed => ALL_PKG,
+    },
     'Built-Using' => {
         allowed => ALL_PKG,
         separator => FIELD_SEP_COMMA,
diff --git a/scripts/Dpkg/Deps.pm b/scripts/Dpkg/Deps.pm
index 449bafb..e1b9713 100644
--- a/scripts/Dpkg/Deps.pm
+++ b/scripts/Dpkg/Deps.pm
@@ -55,6 +55,7 @@ use Dpkg::Version;
 use Dpkg::Arch qw(get_host_arch get_build_arch);
 use Dpkg::ErrorHandling;
 use Dpkg::Gettext;
+use Dpkg::Util qw(:list);
 
 use Exporter qw(import);
 our @EXPORT = qw(deps_concat deps_parse deps_eval_implication deps_compare);
@@ -285,6 +286,22 @@ 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 activated 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 union (defaults to 0)
 
 If set to 1, returns a Dpkg::Deps::Union instead of a Dpkg::Deps::AND. Use
@@ -306,6 +323,9 @@ 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} = [] if not exists $options{build_profiles};
     $options{union} = 0 if not exists $options{union};
     $options{build_dep} = 0 if not exists $options{build_dep};
 
@@ -332,6 +352,11 @@ sub deps_parse {
 		$dep_simple->reduce_arch($options{host_arch});
 		next if not $dep_simple->arch_is_concerned($options{host_arch});
 	    }
+	    $dep_simple->{profiles} = 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;
@@ -580,6 +605,7 @@ sub reset {
     $self->{relation} = undef;
     $self->{version} = undef;
     $self->{arches} = undef;
+    $self->{profiles} = undef;
     $self->{archqual} = undef;
 }
 
@@ -610,6 +636,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)) {
@@ -624,6 +655,9 @@ sub parse_string {
     if (defined($5)) {
 	$self->{arches} = [ split(/\s+/, $5) ];
     }
+    if (defined($6)) {
+	$self->{restrictions} = [ map {lc} split(/\s+/, $6) ];
+    }
 }
 
 sub output {
@@ -638,6 +672,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;
     }
@@ -767,6 +804,39 @@ 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;
+    # copy restrictions into @tmp so that $restrictions can be modified
+    foreach my $restriction (my @tmp = @{$self->{restrictions}}) {
+        # determine if this restriction is negated
+        my $negated = $restriction =~ s/^!//;
+
+        # restriction doesnt concern this check if not starting with "profile."
+        next if ($restriction !~ s/^profile\.//);
+
+        # determine if the restriction matches any of the specified profiles
+        my $found = Dpkg::Util::any { $_ eq $restriction } @$build_profiles;
+
+        # OR the result with $seen_profile, taking negation into account
+        $seen_profile |= $negated ^ $found;
+    }
+    return $seen_profile;
+}
+
+sub reduce_profiles {
+    my ($self, @build_profiles) = @_;
+    if (not $self->profile_is_concerned(@build_profiles)) {
+	$self->reset();
+    } else {
+	$self->{profiles} = undef;
+    }
+}
+
 sub get_evaluation {
     my ($self, $facts) = @_;
     return if not defined $self->{package};
diff --git a/scripts/dpkg-buildpackage.pl b/scripts/dpkg-buildpackage.pl
index 710a261..fe53576 100755
--- a/scripts/dpkg-buildpackage.pl
+++ b/scripts/dpkg-buildpackage.pl
@@ -85,6 +85,9 @@ sub usage {
   -a<arch>       Debian architecture we build for.
   -t<system>     set GNU system type.')
     . "\n\n" . _g(
+'Options passed to dpkg-checkbuilddeps:
+  -P<profiles>   Comma separated list of build profiles.')
+    . "\n\n" . _g(
 'Options passed to dpkg-genchanges:
   -si (default)  source includes orig if new upstream.
   -sa            uploaded source always includes orig.
@@ -122,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);
@@ -186,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.*)$/) {
@@ -338,6 +344,10 @@ if (not $signcommand) {
     $signchanges = 0;
 }
 
+unless (defined $ENV{'DEB_BUILD_PROFILE'}) {
+    $ENV{'DEB_BUILD_PROFILE'} = join(',', @build_profiles);
+}
+
 # Preparation of environment stops here
 
 (my $sversion = $version) =~ s/^\d+://;
diff --git a/scripts/dpkg-checkbuilddeps.pl b/scripts/dpkg-checkbuilddeps.pl
index d9e40e1..85de874 100755
--- a/scripts/dpkg-checkbuilddeps.pl
+++ b/scripts/dpkg-checkbuilddeps.pl
@@ -30,6 +30,7 @@ use Dpkg::ErrorHandling;
 use Dpkg::Arch qw(get_host_arch);
 use Dpkg::Deps;
 use Dpkg::Control::Info;
+use Dpkg::BuildEnv;
 
 textdomain('dpkg-dev');
 
@@ -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.
@@ -64,6 +66,7 @@ my $ignore_bd_arch = 0;
 my $ignore_bd_indep = 0;
 my ($bd_value, $bc_value);
 my $host_arch = get_host_arch();
+my @build_profiles = split(',', Dpkg::BuildEnv::get('DEB_BUILD_PROFILE'));
 my $admindir = $Dpkg::ADMINDIR;
 my @options_spec = (
     'help|?' => sub { usage(); exit(0); },
@@ -73,6 +76,7 @@ my @options_spec = (
     'd=s' => \$bd_value,
     'c=s' => \$bc_value,
     'a=s' => \$host_arch,
+    'P=s' => \@build_profiles,
     'admindir=s' => \$admindir,
 );
 
@@ -81,6 +85,9 @@ my @options_spec = (
     GetOptions(@options_spec);
 }
 
+# support multiple -P arguments as well as comma separated lists
+@build_profiles = split(',',join(',',@build_profiles));
+
 my $controlfile = shift || 'debian/control';
 
 my $control = Dpkg::Control::Info->new($controlfile);
@@ -104,12 +111,14 @@ 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);
+			   reduce_arch => 1, build_profiles => $build_profiles,
+			   reduce_profiles => 1), $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);
+			   reduce_arch => 1, build_profiles => $build_profiles,
+			   reduce_profiles => 1, union => 1), $facts);
 }
 
 if (@unmet) {
diff --git a/scripts/dpkg-gencontrol.pl b/scripts/dpkg-gencontrol.pl
index 63656e7..58a99e1 100755
--- a/scripts/dpkg-gencontrol.pl
+++ b/scripts/dpkg-gencontrol.pl
@@ -37,6 +37,7 @@ use Dpkg::Control::Fields;
 use Dpkg::Substvars;
 use Dpkg::Vars;
 use Dpkg::Changelog::Parse;
+use Dpkg::BuildEnv;
 
 textdomain('dpkg-dev');
 
@@ -292,6 +293,9 @@ foreach my $field (field_list_pkg_dep()) {
     }
 }
 
+my $build_profiles = Dpkg::BuildEnv::get('DEB_BUILD_PROFILE');
+$fields->{'Build-Profile'} = $build_profiles unless !$build_profiles;
+
 for my $f (qw(Package Version)) {
     defined($fields->{$f}) || error(_g('missing information for output field %s'), $f);
 }
diff --git a/scripts/t/400_Dpkg_Deps.t b/scripts/t/400_Dpkg_Deps.t
index b37a1c6..85fb534 100644
--- a/scripts/t/400_Dpkg_Deps.t
+++ b/scripts/t/400_Dpkg_Deps.t
@@ -16,7 +16,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 20;
+use Test::More tests => 24;
 use Dpkg::Arch qw(get_host_arch);
 
 use_ok('Dpkg::Deps');
@@ -58,6 +58,17 @@ 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.stage1 profile.notest>, 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 <!profile.stage1 !profile.notest>, dep2 <profile.stage1 !profile.notest>, dep3 <!profile.stage1 profile.notest>, dep6 <!profile.stage1>', 'Profile reduce 1/4');
+is($dep_stage1->output(), 'dep1 <!profile.stage1 !profile.notest>, dep2 <profile.stage1 !profile.notest>, dep4 <profile.stage1 profile.notest>, dep5 <profile.stage1>', 'Profile reduce 2/4');
+is($dep_notest->output(), 'dep1 <!profile.stage1 !profile.notest>, dep3 <!profile.stage1 profile.notest>, dep4 <profile.stage1 profile.notest>, dep6 <!profile.stage1>', 'Profile reduce 3/4');
+is($dep_stage1notest->output(), 'dep2 <profile.stage1 !profile.notest>, dep3 <!profile.stage1 profile.notest>, dep4 <profile.stage1 profile.notest>, dep5 <profile.stage1>', 'Profile reduce 4/4');
 
 my $facts = Dpkg::Deps::KnownFacts->new();
 $facts->add_installed_package('mypackage', '1.3.4-1', get_host_arch(), 'no');
-- 
1.7.10.4


Reply to: