summaryrefslogtreecommitdiff
path: root/dh
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2018-08-30 11:50:11 +0300
committerIgor Pashev <pashev.igor@gmail.com>2018-08-30 11:50:11 +0300
commit90dd10b7719d0e7fffff85474aa44a505ff001ac (patch)
treef982c0ab9a538ea6adac2ce87cf691ef93922af2 /dh
parentd142a13285106cade800a9956aa7cf50fe5873e9 (diff)
parentd9052a05dfc46f9589aaca3ad8e6e01c6e927101 (diff)
downloaddebhelper-90dd10b7719d0e7fffff85474aa44a505ff001ac.tar.gz
Merge https://salsa.debian.org/debian/debhelper
Diffstat (limited to 'dh')
-rwxr-xr-xdh442
1 files changed, 211 insertions, 231 deletions
diff --git a/dh b/dh
index e4e77abf..dd23ba98 100755
--- a/dh
+++ b/dh
@@ -9,6 +9,7 @@ dh - debhelper command sequencer
use strict;
use warnings;
use Debian::Debhelper::Dh_Lib;
+use Debian::Debhelper::SequencerUtil;
our $VERSION = DH_BUILTIN_VERSION;
@@ -54,6 +55,16 @@ This is used when there is a third-party package that provides
debhelper commands. See the F<PROGRAMMING> file for documentation about
the sequence addon interface.
+A B<Build-Depends> relation on the package B<dh-sequence->I<addon>
+implies a B<--with> I<addon>. This avoids the need for an explicit
+B<--with> in F<debian/rules> that only duplicates what is already
+declared via the build dependencies in F<debian/control>. Note that
+only relations in the B<Build-Depends> field are considered
+(i.e. B<Build-Depends-Indep> and B<Build-Depends-Arch> are
+deliberately unsupported). Please keep in mind that B<dh> insists on
+"simple" relations (e.g. a relation like "B<dh-sequence->I<addon> |
+B<some-other-pkg>" will I<not> imply B<--with> I<addon>).
+
=item B<--without> I<addon>
The inverse of B<--with>, disables using the given addon. This option
@@ -291,18 +302,7 @@ matches, the last one in the sequence will be used.
# Stash this away before init modifies it.
my @ARGV_orig=@ARGV;
-
-if (not compat(9, 1)) {
- # Enable autoreconf'ing by default in compat 10 or later. Use the
- # sequence add-on so existing --without=autoreconf
- unshift(@ARGV, "--with=autoreconf");
- # Enable systemd support by default in compat 10 or later.
- # - compat 11 injects the dh_installsystemd tool directly in the
- # sequence instead of using a --with sequence.
- unshift(@ARGV, "--with=systemd") if compat(10, 1);
- unshift(@ARGV, "--with=build-stamp");
-}
-
+my (@addons, @addon_requests);
inhibit_log();
@@ -312,13 +312,12 @@ init(options => {
"before=s" => \$dh{BEFORE},
"remaining" => \$dh{REMAINING},
"with=s" => sub {
- my ($option,$value)=@_;
- push @{$dh{WITH}},split(",", $value);
+ my ($option, $value) = @_;
+ push(@addon_requests, map { "+${_}" } split(",", $value));
},
"without=s" => sub {
- my ($option,$value)=@_;
- my %without = map { $_ => 1 } split(",", $value);
- @{$dh{WITH}} = grep { ! $without{$_} } @{$dh{WITH}};
+ my ($option, $value) = @_;
+ push(@addon_requests, map { "-${_}" } split(",", $value));
},
"l" => \&list_addons,
"list" => \&list_addons,
@@ -363,10 +362,9 @@ if (! defined $sequence) {
# Also support completely empty override targets.
# Note: it's not safe to use rules_explicit_target before this check,
# since it causes dh to be run.
-my $dummy_target="debhelper-fail-me";
if ($sequence eq 'debian/rules' ||
$sequence =~ /^override_dh_/ ||
- $sequence eq $dummy_target) {
+ $sequence eq DUMMY_TARGET) {
exit 0;
}
@@ -413,6 +411,9 @@ qw{
dh_installppp
dh_installudev
dh_installgsettings
+},
+ (!compat(11) ? qw(dh_installinitramfs) : qw()),
+qw{
dh_bugfiles
dh_ucf
dh_lintian
@@ -429,11 +430,13 @@ qw{
dh_fixperms
dh_missing
});
-my @ba=qw{
+my @ba=(
+ (!compat(11) ? qw(dh_dwz) : qw()),
+qw{
dh_strip
dh_makeshlibs
dh_shlibdeps
-};
+});
if (! getpackages("arch")) {
@ba=();
}
@@ -449,25 +452,13 @@ $sequences{clean} = [@bd_minimal, qw{
}];
$sequences{'build-indep'} = [@bd];
$sequences{'build-arch'} = [@bd];
-if (! compat(8)) {
- # From v9, sequences take standard rules targets into account.
- $sequences{build} = [@bd_minimal, rules("build-arch"), rules("build-indep")];
- $sequences{'install-indep'} = [rules("build-indep"), @i];
- $sequences{'install-arch'} = [rules("build-arch"), @i];
- $sequences{'install'} = [rules("build"), rules("install-arch"), rules("install-indep")];
- $sequences{'binary-indep'} = [rules("install-indep"), @b];
- $sequences{'binary-arch'} = [rules("install-arch"), @ba, @b];
- $sequences{binary} = [rules("install"), rules("binary-arch"), rules("binary-indep")];
-}
-else {
- $sequences{build} = [@bd];
- $sequences{'install'} = [@{$sequences{build}}, @i];
- $sequences{'install-indep'} = [@{$sequences{'build-indep'}}, @i];
- $sequences{'install-arch'} = [@{$sequences{'build-arch'}}, @i];
- $sequences{binary} = [@{$sequences{install}}, @ba, @b];
- $sequences{'binary-indep'} = [@{$sequences{'install-indep'}}, @b];
- $sequences{'binary-arch'} = [@{$sequences{'install-arch'}}, @ba, @b];
-}
+$sequences{build} = [to_rules_target("build-arch"), to_rules_target("build-indep")];
+$sequences{'install-indep'} = [to_rules_target("build-indep"), @i];
+$sequences{'install-arch'} = [to_rules_target("build-arch"), @i];
+$sequences{'install'} = [to_rules_target("build"), to_rules_target("install-arch"), to_rules_target("install-indep")];
+$sequences{'binary-indep'} = [to_rules_target("install-indep"), @b];
+$sequences{'binary-arch'} = [to_rules_target("install-arch"), @ba, @b];
+$sequences{binary} = [to_rules_target("install"), to_rules_target("binary-arch"), to_rules_target("binary-indep")];
# Additional command options
my %command_opts;
@@ -557,13 +548,65 @@ sub list_addons {
exit 0;
}
-if (dpkg_architecture_value('DEB_HOST_ARCH_OS') eq 'illumos'
- && -x '/usr/bin/dh_smf') {
- push @{$dh{WITH}}, 'smf';
+sub _compute_addons {
+ my (@addon_requests_from_args) = @_;
+ my (@enabled_addons, %disabled_addons, %enabled);
+ my @addon_requests;
+
+ # Order is important; DH_EXTRA_ADDONS must come before everything
+ # else; then comes built-in and finally argument provided add-ons
+ # requests.
+ push(@addon_requests, map { "+${_}" } split(",", $ENV{DH_EXTRA_ADDONS}))
+ if $ENV{DH_EXTRA_ADDONS};
+ if (not compat(9, 1)) {
+ # Enable autoreconf'ing by default in compat 10 or later.
+ push(@addon_requests, '+autoreconf');
+
+ # Enable systemd support by default in compat 10 or later.
+ # - compat 11 injects the dh_installsystemd tool directly in the
+ # sequence instead of using a --with sequence.
+ push(@addon_requests, '+systemd') if compat(10, 1);
+ push(@addon_requests, '+build-stamp');
+ }
+ push(@addon_requests, map { "+${_}" } Debian::Debhelper::Dh_Lib::bd_dh_sequences());
+ push(@addon_requests, @addon_requests_from_args);
+
+ if (dpkg_architecture_value('DEB_HOST_ARCH_OS') eq 'illumos'
+ && -x '/usr/bin/dh_smf') {
+ push(@addon_requests, '+smf');
+ }
+
+ # Removing disabled add-ons are expensive (O(N) per time), so we
+ # attempt to make removals in bulk. Note that we have to be order
+ # preserving (due to #885580), so there is a limit to how "smart"
+ # we can be.
+ my $flush_disable_cache = sub {
+ @enabled_addons = grep { not exists($disabled_addons{$_}) } @enabled_addons;
+ for my $addon (keys(%disabled_addons)) {
+ delete($enabled{$addon});
+ }
+ %disabled_addons = ();
+ };
+
+ for my $request (@addon_requests) {
+ if ($request =~ s/^[+]//) {
+ $flush_disable_cache->() if %disabled_addons;
+ push(@enabled_addons, $request) if not $enabled{$request}++;
+ } elsif ($request =~ s/^-//) {
+ $disabled_addons{$request} = 1;
+ } else {
+ error("Internal error: Invalid add-on request: $request (Missing +/- prefix)");
+ }
+ }
+
+ $flush_disable_cache->() if %disabled_addons;
+ return @enabled_addons;
}
+@addons = _compute_addons(@addon_requests);
+
# Load addons, which can modify sequences.
-foreach my $addon (@{$dh{WITH}}) {
+foreach my $addon (@addons) {
my $mod="Debian::Debhelper::Sequence::$addon";
$mod=~s/-/_/g;
eval "use $mod";
@@ -576,10 +619,27 @@ if (! exists $sequences{$sequence}) {
error "Unknown sequence $sequence (choose from: ".
join(" ", sort keys %sequences).")";
}
-my @sequence=optimize_sequence(@{$sequences{$sequence}});
# The list of all packages that can be acted on.
my @packages=@{$dh{DOPACKAGES}};
+my @arch_packages = getpackages("arch");
+my @indep_packages = getpackages("indep");
+my %sequence2packages = (
+ 'build-arch' => \@arch_packages,
+ 'install-arch' => \@arch_packages,
+ 'binary-arch' => \@arch_packages,
+
+ 'build-indep' => \@indep_packages,
+ 'install-indep' => \@indep_packages,
+ 'binary-indep' => \@indep_packages,
+
+ 'clean' => \@packages,
+ 'build' => \@packages,
+ 'install' => \@packages,
+ 'binary' => \@packages,
+);
+
+my %completed_sequences;
# Get the options to pass to commands in the sequence.
# Filter out options intended only for this program.
@@ -592,16 +652,14 @@ if ($sequence eq 'build-arch' ||
push @options, "-a";
# as an optimisation, remove from the list any packages
# that are not arch dependent
- my %arch_packages = map { $_ => 1 } getpackages("arch");
- @packages = grep { $arch_packages{$_} } @packages;
+ @packages = @{$sequence2packages{$sequence}};
}
elsif ($sequence eq 'build-indep' ||
$sequence eq 'install-indep' ||
$sequence eq 'binary-indep') {
push @options, "-i";
# ditto optimisation for arch indep
- my %indep_packages = map { $_ => 1 } getpackages("indep");
- @packages = grep { $indep_packages{$_} } @packages;
+ @packages = @{$sequence2packages{$sequence}};
}
while (@ARGV_orig) {
my $opt=shift @ARGV_orig;
@@ -646,63 +704,115 @@ while (@ARGV_orig) {
}
# Figure out at what point in the sequence to start for each package.
-my %logged;
-my %startpoint;
-my %stamp_file;
+my (%logged, %startpoint, %stamp_file);
-if ( -f $build_stamp_file) {
+if ( -f $build_stamp_file and not compat(9)) {
open(my $fd, '<', $build_stamp_file) or error("open($build_stamp_file, ro) failed: $!");
while (my $line = <$fd>) {
chomp($line);
$stamp_file{$line} = 1;
}
close($fd);
+ my $build_indep_target_done = 1;
+ my $build_arch_target_done = 1;
+ for my $pkg (@{$sequence2packages{'build-arch'}}) {
+ if (not $stamp_file{$pkg}) {
+ $build_arch_target_done = 0;
+ last;
+ }
+ }
+ for my $pkg (@{$sequence2packages{'build-indep'}}) {
+ if (not $stamp_file{$pkg}) {
+ $build_indep_target_done = 0;
+ last;
+ }
+ }
+ $completed_sequences{'build-arch'} = 1 if $build_arch_target_done;
+ $completed_sequences{'build-indep'} = 1 if $build_indep_target_done;
+ $completed_sequences{'build'} = 1 if $build_indep_target_done and $build_arch_target_done;
}
-# Lazy cache of the result of optimize_sequence on the "build"
-# sequence
-my $optimized_build_seq;
-foreach my $package (@packages) {
- my @log;
- if (compat(9)) {
- @log = load_log($package, \%logged);
- } elsif (exists($stamp_file{$package})) {
- if (not defined($optimized_build_seq)) {
- # Expand "build" so we can accurately filter out
- # everything (admittedly, it is bit of an over
- # approximation)
- # Related bug: #851071
- my @seq = optimize_sequence(@{$sequences{'build'}});
- $optimized_build_seq = \@seq;
+# In compat <= 8, the sequences are always inlined (those versions do not
+# recurse into debian/rules anyway). In compat 9+, we never inline an
+# existing rules target.
+my ($rules_targets, $full_sequence) = unpack_sequence(\%sequences,
+ $sequence,
+ (!compat(8) ? 0 : 1),
+ \%completed_sequences
+ );
+my $stoppoint = $#{$full_sequence};
+
+if (compat(9)) {
+ foreach my $package (@packages) {
+ my @log = load_log($package, \%logged);
+ if ($dh{AFTER}) {
+ # Run commands in the sequence that come after the
+ # specified command.
+ $startpoint{$package} = command_pos($dh{AFTER}, @{$full_sequence}) + 1;
+ # Write a dummy log entry indicating that the specified
+ # command was, in fact, run. This handles the case where
+ # no commands remain to run after it, communicating to
+ # future dh instances that the specified command should not
+ # be run again.
+ write_log($full_sequence->[$startpoint{$package} - 1], $package);
+ }
+ elsif ($dh{REMAINING}) {
+ # Start at the beginning so all remaining commands will get
+ # run.
+ $startpoint{$package} = 0;
+ }
+ else {
+ # Find the last logged command that is in the sequence, and
+ # continue with the next command after it. If no logged
+ # command is in the sequence, we're starting at the beginning..
+ $startpoint{$package} = 0;
+ COMMAND:
+ foreach my $command (reverse @log) {
+ foreach my $i (0 .. $#{$full_sequence}) {
+ if ($command eq $full_sequence->[$i]) {
+ $startpoint{$package} = $i + 1;
+ last COMMAND;
+ }
+ }
+ }
}
- @log = @{$optimized_build_seq};
- # We do not need %logged in compat 10
}
- if ($dh{AFTER}) {
- # Run commands in the sequence that come after the
- # specified command.
- $startpoint{$package}=command_pos($dh{AFTER}, @sequence) + 1;
- # Write a dummy log entry indicating that the specified
- # command was, in fact, run. This handles the case where
- # no commands remain to run after it, communicating to
- # future dh instances that the specified command should not
- # be run again.
- write_log($sequence[$startpoint{$package}-1], $package);
+ # Figure out what point in the sequence to go to.
+ if ($dh{UNTIL}) {
+ $stoppoint = command_pos($dh{UNTIL}, @{$full_sequence});
+ } elsif ($dh{BEFORE}) {
+ $stoppoint = command_pos($dh{BEFORE}, @{$full_sequence}) - 1;
}
- elsif ($dh{REMAINING}) {
- # Start at the beginning so all remaining commands will get
- # run.
- $startpoint{$package}=0;
+} else {
+ foreach my $package (@packages) {
+ $startpoint{$package} = 0;
}
- else {
- # Find the last logged command that is in the sequence, and
- # continue with the next command after it. If no logged
- # command is in the sequence, we're starting at the beginning..
- $startpoint{$package}=0;
-COMMAND: foreach my $command (reverse @log) {
- foreach my $i (0..$#sequence) {
- if ($command eq $sequence[$i]) {
- $startpoint{$package}=$i+1;
+}
+
+for my $rules_command (@{$rules_targets}) {
+ my $rules_target = extract_rules_target_name($rules_command)
+ // error("Internal error: $rules_command was not a rules target!?");
+ # Don't pass DH_ environment variables, since this is
+ # a fresh invocation of debian/rules and any sub-dh commands.
+ delete($ENV{DH_INTERNAL_OPTIONS});
+ delete($ENV{DH_INTERNAL_OVERRIDE});
+ run("debian/rules", $rules_target);
+ my $override_packages = $sequence2packages{$rules_target} // \@packages;
+ for my $package (@{$override_packages}) {
+ my (undef, $seq) = unpack_sequence(\%sequences, $rules_target, 1);
+ COMMAND: for my $c (reverse(@{$seq})) {
+ for my $j (0 .. $#{$full_sequence}) {
+ if ($c eq $full_sequence->[$j]) {
+ # Unfortunately, we do not guarantee any order
+ # between the run targets. Assuming e.g.
+ # "install-arch" and "build" are opague targets
+ # then we could process "install-arch" first and
+ # then "build". In this case, it is important
+ # that we do not "reset" the starting point for
+ # "arch" packages. Otherwise, we might repeat
+ # part of the "install-arch" sequence when we
+ # should not.
+ $startpoint{$package} = $j + 1 if $j + 1 > $startpoint{$package};
last COMMAND;
}
}
@@ -710,25 +820,16 @@ COMMAND: foreach my $command (reverse @log) {
}
}
-# Figure out what point in the sequence to go to.
-my $stoppoint=$#sequence;
-if ($dh{UNTIL}) {
- $stoppoint=command_pos($dh{UNTIL}, @sequence);
-}
-elsif ($dh{BEFORE}) {
- $stoppoint=command_pos($dh{BEFORE}, @sequence) - 1;
-}
-
# Now run the commands in the sequence.
foreach my $i (0..$stoppoint) {
- my $command=$sequence[$i];
+ my $command = $full_sequence->[$i];
# Figure out which packages need to run this command.
my @todo;
my @opts=@options;
foreach my $package (@packages) {
if ($startpoint{$package} > $i ||
- $logged{$package}{$sequence[$i]}) {
+ $logged{$package}{$full_sequence->[$i]}) {
push @opts, "-N$package";
}
else {
@@ -737,15 +838,9 @@ foreach my $i (0..$stoppoint) {
}
next unless @todo;
- my $rules_target = rules_target($command);
- if (defined $rules_target) {
- # Don't pass DH_ environment variables, since this is
- # a fresh invocation of debian/rules and any sub-dh commands.
- delete $ENV{DH_INTERNAL_OPTIONS};
- delete $ENV{DH_INTERNAL_OVERRIDE};
- run("debian/rules", $rules_target);
- next;
- }
+ my $rules_target = extract_rules_target_name($command);
+ error("Internal error: $command is a rules target, but it is not supported to be!?") if defined($rules_target);
+
if (my $stamp_file = stamp_target($command)) {
my %seen;
print " create-stamp ".escape_shell($stamp_file)."\n";
@@ -897,42 +992,6 @@ sub run_override {
return @rest;
}
-sub optimize_sequence {
- my (@commands) = @_;
- my (@sequence, %seen);
- my $add=sub {
- # commands can appear multiple times when sequences are
- # inlined together; only the first should be needed
- my $command=shift;
- if (! $seen{$command}) {
- $seen{$command}=1;
- push @sequence, $command;
- }
- };
- foreach my $command (@commands) {
- my $rules_target=rules_target($command);
- if (defined $rules_target &&
- ! defined rules_explicit_target($rules_target)) {
- # inline the sequence for this implicit target
- $add->($_) foreach optimize_sequence(@{$sequences{$rules_target}});
- }
- else {
- $add->($command);
- }
- }
- return @sequence;
-}
-
-sub rules_target {
- my ($command) = @_;
- if ($command =~ /^debian\/rules\s+(.*)/) {
- return $1
- }
- else {
- return undef;
- }
-}
-
sub stamp_target {
my ($command) = @_;
if ($command =~ s/^create-stamp\s+//) {
@@ -941,71 +1000,6 @@ sub stamp_target {
return;
}
-sub rules {
- return "debian/rules ".join(" ", @_);
-}
-
-{
-my %targets;
-my $rules_parsed;
-
-sub rules_explicit_target {
- # Checks if a specified target exists as an explicit target
- # in debian/rules.
- # undef is returned if target does not exist, 0 if target is noop
- # and 1 if target has dependencies or executes commands.
- my ($target) = @_;
-
- if (! $rules_parsed) {
- my $processing_targets = 0;
- my $not_a_target = 0;
- my $current_target;
- open(MAKE, "LC_ALL=C make -Rrnpsf debian/rules $dummy_target 2>/dev/null |");
- while (<MAKE>) {
- if ($processing_targets) {
- if (/^# Not a target:/) {
- $not_a_target = 1;
- }
- else {
- if (!$not_a_target && /^([^#:]+)::?\s*(.*)$/) {
- # Target is defined. NOTE: if it is a dependency of
- # .PHONY it will be defined too but that's ok.
- # $2 contains target dependencies if any.
- $current_target = $1;
- $targets{$current_target} = ($2) ? 1 : 0;
- }
- else {
- if (defined $current_target) {
- if (/^#/) {
- # Check if target has commands to execute
- if (/^#\s*(commands|recipe) to execute/) {
- $targets{$current_target} = 1;
- }
- }
- else {
- # Target parsed.
- $current_target = undef;
- }
- }
- }
- # "Not a target:" is always followed by
- # a target name, so resetting this one
- # here is safe.
- $not_a_target = 0;
- }
- }
- elsif (/^# Files$/) {
- $processing_targets = 1;
- }
- }
- close MAKE;
- $rules_parsed = 1;
- }
-
- return $targets{$target};
-}
-
-}
sub warn_deprecated {
foreach my $deprecated ('until', 'after', 'before', 'remaining') {
@@ -1049,6 +1043,9 @@ sub can_skip {
return 0 if $user_specified_options ||
(exists $ENV{DH_OPTIONS} && length $ENV{DH_OPTIONS});
+ return 0 if exists($command_opts{$command})
+ and @{$command_opts{$command}};
+
if (! defined $skipinfo{$command}) {
$skipinfo{$command}=[extract_skipinfo($command)];
}
@@ -1095,23 +1092,6 @@ sub can_skip {
return 1;
}
-sub extract_skipinfo {
- my ($command) = @_;
-
- foreach my $dir (split (':', $ENV{PATH})) {
- if (open (my $h, "<", "$dir/$command")) {
- while (<$h>) {
- if (m/PROMISE: DH NOOP( WITHOUT\s+(.*))?\s*$/) {
- close $h;
- return split(' ', $2) if defined($2);
- return ('always-skip');
- }
- }
- close $h;
- return ();
- }
- }
-}
=head1 SEE ALSO