diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2018-08-30 11:50:11 +0300 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2018-08-30 11:50:11 +0300 |
commit | 90dd10b7719d0e7fffff85474aa44a505ff001ac (patch) | |
tree | f982c0ab9a538ea6adac2ce87cf691ef93922af2 /dh | |
parent | d142a13285106cade800a9956aa7cf50fe5873e9 (diff) | |
parent | d9052a05dfc46f9589aaca3ad8e6e01c6e927101 (diff) | |
download | debhelper-90dd10b7719d0e7fffff85474aa44a505ff001ac.tar.gz |
Merge https://salsa.debian.org/debian/debhelper
Diffstat (limited to 'dh')
-rwxr-xr-x | dh | 442 |
1 files changed, 211 insertions, 231 deletions
@@ -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 |