summaryrefslogtreecommitdiffstats
path: root/scripts/dpkg-buildpackage.pl
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/dpkg-buildpackage.pl')
-rwxr-xr-xscripts/dpkg-buildpackage.pl317
1 files changed, 103 insertions, 214 deletions
diff --git a/scripts/dpkg-buildpackage.pl b/scripts/dpkg-buildpackage.pl
index 7b8181b..d849d6e 100755
--- a/scripts/dpkg-buildpackage.pl
+++ b/scripts/dpkg-buildpackage.pl
@@ -4,7 +4,7 @@
#
# Copyright © 1996 Ian Jackson
# Copyright © 2000 Wichert Akkerman
-# Copyright © 2006-2010, 2012-2015 Guillem Jover <guillem@debian.org>
+# Copyright © 2006-2024 Guillem Jover <guillem@debian.org>
# Copyright © 2007 Frank Lichtenheld
#
# This program is free software; you can redistribute it and/or modify
@@ -23,8 +23,7 @@
use strict;
use warnings;
-use File::Temp qw(tempdir);
-use File::Basename;
+use File::Path qw(remove_tree);
use File::Copy;
use File::Glob qw(bsd_glob GLOB_TILDE GLOB_NOCHECK);
use POSIX qw(:sys_wait_h);
@@ -33,9 +32,9 @@ use Dpkg ();
use Dpkg::Gettext;
use Dpkg::ErrorHandling;
use Dpkg::BuildTypes;
-use Dpkg::BuildAPI qw(get_build_api);
use Dpkg::BuildOptions;
use Dpkg::BuildProfiles qw(set_build_profiles);
+use Dpkg::BuildDriver;
use Dpkg::Conf;
use Dpkg::Compression;
use Dpkg::Checksums;
@@ -64,7 +63,7 @@ later for copying conditions. There is NO warranty.
sub usage {
printf g_(
-'Usage: %s [<option>...]')
+'Usage: %s [<option>...] [--] [<filename.dsc>|<directory>]')
. "\n\n" . g_(
'Options:
--build=<type>[,...] specify the build <type>: full, source, binary,
@@ -173,7 +172,10 @@ my $parallel;
my $parallel_force = 0;
my $checkbuilddep = 1;
my $check_builtin_builddep = 1;
+my $source;
+my $source_from_dsc = 0;
my @source_opts;
+my $srcdir;
my $check_command = $ENV{DEB_CHECK_COMMAND};
my @check_opts;
my $signpause;
@@ -202,21 +204,6 @@ my $buildinfo_file;
my @buildinfo_opts;
my $changes_file;
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(
preinit
init
@@ -423,8 +410,14 @@ while (@ARGV) {
} elsif (/^-R(.*)$/ or /^--rules-file=(.*)$/) {
my $arg = $1;
@debian_rules = split ' ', $arg;
- } else {
+ } elsif ($_ eq '--') {
+ $source = shift @ARGV;
+ last;
+ } elsif (/^-/) {
usageerr(g_('unknown option or argument %s'), $_);
+ } else {
+ $source = $_;
+ last;
}
}
@@ -501,13 +494,54 @@ if ($build_opts->has('terse')) {
set_build_profiles(@build_profiles) if @build_profiles;
+# Handle specified source trees.
+if (defined $source) {
+ if (-d $source) {
+ chdir $source
+ or syserr(g_('cannot change directory to %s'), $source);
+ } elsif (-f $source) {
+ require Dpkg::Source::Package;
+
+ if (build_has_any(BUILD_SOURCE)) {
+ error(g_('building source package would overwrite input source %s'),
+ $source);
+ }
+
+ if ($source =~ m{/}) {
+ error(g_('source package %s is expected in the current directory'),
+ $source);
+ }
+
+ my $srcpkg = Dpkg::Source::Package->new(
+ filename => $source,
+ options => {
+ no_check => 0,
+ no_overwrite_dir => 1,
+ require_valid_signature => 0,
+ require_strong_checksums => 0,
+ },
+ );
+ $srcdir = $srcpkg->get_basedirname();
+
+ if (-e $srcdir) {
+ error(g_('source directory %s exists already, aborting'), $srcdir);
+ }
+
+ info(g_('extracting source package %s'), $source);
+
+ run_cmd('dpkg-source', @source_opts, '--extract', $source);
+
+ chdir $srcdir
+ or syserr(g_('cannot change directory to %s'), $srcdir);
+
+ # Track whether we extracted the source from a specified .dsc.
+ $source_from_dsc = 1;
+ }
+}
+
my $changelog = changelog_parse();
my $ctrl = Dpkg::Control::Info->new();
-# 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 $pkg = mustsetvar($changelog->{source}, g_('source package'));
my $version = mustsetvar($changelog->{version}, g_('source version'));
my $v = Dpkg::Version->new($version);
@@ -609,16 +643,21 @@ if ($sanitize_env) {
run_vendor_hook('sanitize-environment');
}
+my $build_driver = Dpkg::BuildDriver->new(
+ ctrl => $ctrl,
+ debian_rules => \@debian_rules,
+ root_cmd => \@rootcommand,
+ as_root => $call_target_as_root,
+ rrr_override => $rrr_override,
+);
+
#
# Preparation of environment stops here
#
run_hook('init');
-if (not -x 'debian/rules') {
- warning(g_('debian/rules is not executable; fixing that'));
- chmod(0755, 'debian/rules'); # No checks of failures, non fatal
-}
+$build_driver->pre_check();
if (scalar @call_target == 0) {
run_cmd('dpkg-source', @source_opts, '--before-build', '.');
@@ -643,7 +682,7 @@ if ($checkbuilddep) {
}
foreach my $call_target (@call_target) {
- run_rules_cond_root($call_target);
+ $build_driver->run_task($call_target);
}
exit 0 if scalar @call_target;
@@ -652,7 +691,7 @@ run_hook('preclean', {
});
if ($preclean) {
- run_rules_cond_root('clean');
+ $build_driver->run_task('clean');
}
run_hook('source', {
@@ -670,26 +709,19 @@ if (build_has_any(BUILD_SOURCE)) {
my $build_types = get_build_options_from_type();
-if (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($ctrl);
-}
+my $need_buildtask = $build_driver->need_build_task($buildtarget, $binarytarget);
+
+run_hook('build', {
+ enabled => build_has_any(BUILD_BINARY) && $need_buildtask,
+ env => {
+ DPKG_BUILDPACKAGE_HOOK_BUILD_TARGET => $buildtarget,
+ },
+});
# If we are building rootless, there is no need to call the build target
# independently as non-root.
-if (build_has_any(BUILD_BINARY) && rules_requires_root($binarytarget)) {
- run_hook('build', {
- env => {
- DPKG_BUILDPACKAGE_HOOK_BUILD_TARGET => $buildtarget,
- },
- });
- run_cmd(@debian_rules, $buildtarget);
-} else {
- run_hook('build', {
- enabled => 0,
- });
+if (build_has_any(BUILD_BINARY) && $need_buildtask) {
+ $build_driver->run_build_task($buildtarget, $binarytarget);
}
if (build_has_any(BUILD_BINARY)) {
@@ -698,12 +730,21 @@ if (build_has_any(BUILD_BINARY)) {
DPKG_BUILDPACKAGE_HOOK_BINARY_TARGET => $binarytarget,
},
});
- run_rules_cond_root($binarytarget);
+ $build_driver->run_task($binarytarget);
}
$buildinfo_file //= "../$pva.buildinfo";
-push @buildinfo_opts, "--build=$build_types" if build_has_none(BUILD_DEFAULT);
+if (build_has_none(BUILD_DEFAULT) || $source_from_dsc) {
+ my $buildinfo_buildtypes = $build_types;
+
+ # We can now let dpkg-genbuildinfo know that we can include the .dsc
+ # in the .buildinfo file as we handled it ourselves, and what we are
+ # building matches either the source we built or extracted it from.
+ $buildinfo_buildtypes .= ',source' if $source_from_dsc;
+
+ push @buildinfo_opts, "--build=$buildinfo_buildtypes";
+}
push @buildinfo_opts, "--admindir=$admindir" if $admindir;
push @buildinfo_opts, "-O$buildinfo_file" if $buildinfo_file;
@@ -738,7 +779,7 @@ run_hook('postclean', {
});
if ($postclean) {
- run_rules_cond_root('clean');
+ $build_driver->run_task('clean');
}
run_cmd('dpkg-source', @source_opts, '--after-build', '.');
@@ -802,6 +843,13 @@ if (not $signreleased) {
warning(g_('not signing UNRELEASED build; use --force-sign to override'));
}
+if ($source_from_dsc) {
+ info(g_('removing extracted source directory %s'), $srcdir);
+ chdir '..'
+ or syserr(g_('cannot change directory to %s'), '..');
+ remove_tree($srcdir);
+}
+
run_hook('done');
sub mustsetvar {
@@ -814,94 +862,6 @@ sub mustsetvar {
return $var;
}
-sub setup_rootcommand {
- if ($< == 0) {
- warning(g_('using a gain-root-command while being root')) if @rootcommand;
- } else {
- push @rootcommand, 'fakeroot' unless @rootcommand;
- }
-
- if (@rootcommand and not find_command($rootcommand[0])) {
- if ($rootcommand[0] eq 'fakeroot' and $< != 0) {
- error(g_("fakeroot not found, either install the fakeroot\n" .
- 'package, specify a command with the -r option, ' .
- 'or run this as root'));
- } else {
- error(g_("gain-root-command '%s' not found"), $rootcommand[0]);
- }
- }
-}
-
-sub parse_rules_requires_root {
- my $ctrl = shift;
-
- my %rrr;
- my $rrr;
- my $rrr_default;
- my $keywords_base;
- my $keywords_impl;
-
- if (get_build_api($ctrl) >= 1) {
- $rrr_default = 'no';
- } else {
- $rrr_default = 'binary-targets';
- }
-
- my $ctrl_src = $ctrl->get_source();
- $rrr = $rrr_override // $ctrl_src->{'Rules-Requires-Root'} // $rrr_default;
-
- 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 =~ m{^dpkg/(.*)$} and $1 ne 'target-subcommand') {
- error(g_('%s field keyword "%s" is unknown in dpkg namespace'),
- 'Rules-Requires-Root', $keyword);
- }
- $keywords_impl++;
- } else {
- if ($keyword ne lc $keyword and
- (lc $keyword eq 'no' or lc $keyword eq 'binary-targets')) {
- error(g_('%s field keyword "%s" is uppercase; use "%s" instead'),
- 'Rules-Requires-Root', $keyword, lc $keyword);
- } elsif (lc $keyword eq 'yes') {
- error(g_('%s field keyword "%s" is invalid; use "%s" instead'),
- 'Rules-Requires-Root', $keyword, 'binary-targets');
- } elsif ($keyword ne 'no' and $keyword ne 'binary-targets') {
- warning(g_('%s field keyword "%s" is unknown'),
- 'Rules-Requires-Root', $keyword);
- }
- $keywords_base++;
- }
-
- if ($rrr{$keyword}++) {
- error(g_('field %s contains duplicate keyword "%s"'),
- 'Rules-Requires-Root', $keyword);
- }
- }
-
- if ($call_target_as_root or not exists $rrr{no}) {
- setup_rootcommand();
- }
-
- # Notify the children we do support R³.
- $ENV{DEB_RULES_REQUIRES_ROOT} = join ' ', sort keys %rrr;
-
- 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{DEB_GAIN_ROOT_CMD} = join ' ', @rootcommand;
- } else {
- # We should not provide the variable otherwise.
- delete $ENV{DEB_GAIN_ROOT_CMD};
- }
-
- return %rrr;
-}
-
sub run_cmd {
my @cmd = @_;
@@ -909,25 +869,6 @@ sub run_cmd {
system @cmd and subprocerr("@cmd");
}
-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, $opts) = @_;
my $cmd = $hook{$name};
@@ -994,24 +935,16 @@ sub signkey_validate {
sub signfile {
my $file = shift;
+ my $signfile = "../$file";
printcmd("signfile $file");
- my $signdir = tempdir('dpkg-sign.XXXXXXXX', CLEANUP => 1);
- my $signfile = "$signdir/$file";
-
- # Make sure the file to sign ends with a newline.
- copy("../$file", $signfile);
- open my $signfh, '>>', $signfile or syserr(g_('cannot open %s'), $signfile);
- print { $signfh } "\n";
- close $signfh or syserr(g_('cannot close %s'), $signfile);
-
my $status = $openpgp->inline_sign($signfile, "$signfile.asc", $signkey);
if ($status == OPENPGP_OK) {
- move("$signfile.asc", "../$file")
- or syserror(g_('cannot move %s to %s'), "$signfile.asc", "../$file");
+ move("$signfile.asc", $signfile)
+ or syserror(g_('cannot move %s to %s'), "$signfile.asc", $signfile);
} else {
- error(g_('failed to sign %s file: %s'), $file,
+ error(g_('failed to sign %s file: %s'), $signfile,
openpgp_errorcode_to_string($status));
}
@@ -1049,47 +982,3 @@ sub describe_build {
return g_('full upload (original source is included)');
}
}
-
-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($binarytarget);
-
- return if $buildtarget eq 'build';
- return if scalar @debian_rules != 1;
-
- # Avoid further heuristics in newer dpkg-build-api levels.
- return if get_build_api($ctrl) >= 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;
-
- foreach my $bin ($ctrl->get_packages()) {
- if ($bin->{Architecture} eq 'all') {
- $pkg_arch |= BUILD_ARCH_INDEP;
- } else {
- $pkg_arch |= BUILD_ARCH_DEP;
- }
- }
-
- return if $pkg_arch == BUILD_BINARY;
-
- # Check if the build-{arch,indep} targets are supported. If not, fallback
- # to build.
- my $pid = spawn(exec => [ $Dpkg::PROGMAKE, '-f', @debian_rules, '-qn', $buildtarget ],
- from_file => '/dev/null', to_file => '/dev/null',
- error_to_file => '/dev/null');
- my $cmdline = "make -f @debian_rules -qn $buildtarget";
- wait_child($pid, nocheck => 1, cmdline => $cmdline);
- my $exitcode = WEXITSTATUS($?);
- subprocerr($cmdline) unless WIFEXITED($?);
- if ($exitcode == 2) {
- warning(g_("%s must be updated to support the 'build-arch' and " .
- "'build-indep' targets (at least '%s' seems to be " .
- 'missing)'), "@debian_rules", $buildtarget);
- $buildtarget = 'build';
- }
-}