summaryrefslogtreecommitdiff
path: root/scripts/dpkg-buildpackage.pl
diff options
context:
space:
mode:
authorGuillem Jover <guillem@debian.org>2017-09-17 12:18:15 +0200
committerGuillem Jover <guillem@debian.org>2017-09-24 21:03:10 +0200
commitfca1bfe8406898105d1d724fb808f0cbcf985ae4 (patch)
tree7fc20ba945977bb370d3762f21c84fa1830ee93d /scripts/dpkg-buildpackage.pl
parentb3608a01a8ac1416f0620bdf03d497c5b761b244 (diff)
downloaddpkg-fca1bfe8406898105d1d724fb808f0cbcf985ae4.tar.gz
dpkg-buildpackage: Add support for rootless builds
Implement the rootless-builds specification, by honoring the Rules-Requires-Root (R³) field.
Diffstat (limited to 'scripts/dpkg-buildpackage.pl')
-rwxr-xr-xscripts/dpkg-buildpackage.pl100
1 files changed, 87 insertions, 13 deletions
diff --git a/scripts/dpkg-buildpackage.pl b/scripts/dpkg-buildpackage.pl
index 2bdae7b18..affcca8d0 100755
--- a/scripts/dpkg-buildpackage.pl
+++ b/scripts/dpkg-buildpackage.pl
@@ -178,6 +178,12 @@ my $changedby;
my $desc;
my @buildinfo_opts;
my @changes_opts;
+my %target_legacy_root = map { $_ => 1 } qw(
+ clean binary binary-arch binary-indep
+);
+my %target_official = map { $_ => 1 } qw(
+ clean build build-arch build-indep binary binary-arch binary-indep
+);
my @hook_names = qw(
init preclean source build binary buildinfo changes postclean check sign done
);
@@ -431,6 +437,7 @@ my $cwd = cwd();
my $dir = basename($cwd);
my $changelog = changelog_parse();
+my $ctrl = Dpkg::Control::Info->new();
my $pkg = mustsetvar($changelog->{source}, g_('source package'));
my $version = mustsetvar($changelog->{version}, g_('source version'));
@@ -455,6 +462,10 @@ if ($changedby) {
# <https://reproducible-builds.org/specs/source-date-epoch/>
$ENV{SOURCE_DATE_EPOCH} ||= $changelog->{timestamp} || time;
+# Check whether we are doing some kind of rootless build, and sanity check
+# the fields values.
+my %rules_requires_root = parse_rules_requires_root($ctrl);
+
my @arch_opts;
push @arch_opts, ('--host-arch', $host_arch) if $host_arch;
push @arch_opts, ('--host-type', $host_type) if $host_type;
@@ -538,20 +549,14 @@ if ($checkbuilddep) {
}
foreach my $call_target (@call_target) {
- if ($call_target_as_root or
- $call_target =~ /^(clean|binary(|-arch|-indep))$/)
- {
- run_cmd(@rootcommand, @debian_rules, $call_target);
- } else {
- run_cmd(@debian_rules, $call_target);
- }
+ run_rules_cond_root($call_target);
}
exit 0 if scalar @call_target;
run_hook('preclean', ! $noclean);
unless ($noclean) {
- run_cmd(@rootcommand, @debian_rules, 'clean');
+ run_rules_cond_root('clean');
}
run_hook('source', build_has_any(BUILD_SOURCE));
@@ -568,14 +573,16 @@ run_hook('build', build_has_any(BUILD_BINARY));
# XXX Use some heuristics to decide whether to use build-{arch,indep} targets.
# This is a temporary measure to not break too many packages on a flag day.
-build_target_fallback();
+build_target_fallback($ctrl);
my $build_types = get_build_options_from_type();
if (build_has_any(BUILD_BINARY)) {
- run_cmd(@debian_rules, $buildtarget);
+ # If we are building rootless, there is no need to call the build target
+ # independently as non-root.
+ run_cmd(@debian_rules, $buildtarget) if rules_requires_root($buildtarget);
run_hook('binary', 1);
- run_cmd(@rootcommand, @debian_rules, $binarytarget);
+ run_rules_cond_root($binarytarget);
}
run_hook('buildinfo', 1);
@@ -607,7 +614,7 @@ close $changes_fh or subprocerr(g_('dpkg-genchanges'));
run_hook('postclean', $cleansource);
if ($cleansource) {
- run_cmd(@rootcommand, @debian_rules, 'clean');
+ run_rules_cond_root('clean');
}
chdir('..') or syserr('chdir ..');
@@ -678,11 +685,73 @@ sub mustsetvar {
return $var;
}
+sub parse_rules_requires_root {
+ my $ctrl = shift;
+
+ my %rrr;
+ my $rrr = $ctrl->{'Rules-Requires-Root'} // 'binary-targets';
+ my $keywords_base;
+ my $keywords_impl;
+
+ foreach my $keyword (split ' ', $rrr) {
+ if ($keyword =~ m{/}) {
+ if ($keyword =~ m{^dpkg/target/(.*)$}p and $target_official{$1}) {
+ error(g_('disallowed target in %s field keyword %s'),
+ 'Rules-Requires-Root', $keyword);
+ } elsif ($keyword ne 'dpkg/target-subcommand') {
+ error(g_('unknown %s field keyword %s in dpkg namespace'),
+ 'Rules-Requires-Root', $keyword);
+ }
+ $keywords_impl++;
+ } else {
+ if ($keyword ne 'no' and $keyword ne 'binary-targets') {
+ warning(g_('unknown %s field keyword %s'),
+ 'Rules-Requires-Root', $keyword);
+ }
+ $keywords_base++;
+ }
+
+ if ($rrr{$keyword}++) {
+ error(g_('field %s contains duplicate keyword %s'),
+ 'Rules-Requires-Root', $keyword);
+ }
+ }
+
+ if ($keywords_base > 1 or $keywords_base and $keywords_impl) {
+ error(g_('%s field contains both global and implementation specific keywords'),
+ 'Rules-Requires-Root');
+ } elsif ($keywords_impl) {
+ # Set only on <implementations-keywords>.
+ $ENV{DPKG_GAIN_ROOT_CMD} = join ' ', @rootcommand;
+ }
+
+ return %rrr;
+}
+
sub run_cmd {
printcmd(@_);
system @_ and subprocerr("@_");
}
+sub rules_requires_root {
+ my $target = shift;
+
+ return 1 if $call_target_as_root;
+ return 1 if $rules_requires_root{"dpkg/target/$target"};
+ return 1 if $rules_requires_root{'binary-targets'} and $target_legacy_root{$target};
+ return 0;
+}
+
+sub run_rules_cond_root {
+ my $target = shift;
+
+ my @cmd;
+ push @cmd, @rootcommand if rules_requires_root($target);
+ push @cmd, @debian_rules, $target;
+
+ run_cmd(@cmd);
+}
+
sub run_hook {
my ($name, $enabled) = @_;
my $cmd = $hook{$name};
@@ -787,13 +856,18 @@ sub describe_build {
}
sub build_target_fallback {
+ my $ctrl = shift;
+
+ # If we are building rootless, there is no need to call the build target
+ # independently as non-root.
+ return if not rules_requires_root($buildtarget);
+
return if $buildtarget eq 'build';
return if scalar @debian_rules != 1;
# Check if we are building both arch:all and arch:any packages, in which
# case we now require working build-indep and build-arch targets.
my $pkg_arch = 0;
- my $ctrl = Dpkg::Control::Info->new();
foreach my $bin ($ctrl->get_packages()) {
if ($bin->{Architecture} eq 'all') {