diff options
Diffstat (limited to 'lib/Debian/Debhelper/DH')
-rw-r--r-- | lib/Debian/Debhelper/DH/AddonAPI.pm | 228 | ||||
-rw-r--r-- | lib/Debian/Debhelper/DH/SequenceState.pm | 31 |
2 files changed, 259 insertions, 0 deletions
diff --git a/lib/Debian/Debhelper/DH/AddonAPI.pm b/lib/Debian/Debhelper/DH/AddonAPI.pm new file mode 100644 index 0000000..44082a9 --- /dev/null +++ b/lib/Debian/Debhelper/DH/AddonAPI.pm @@ -0,0 +1,228 @@ +# Defines dh sequence state variables +# +# License: GPL-2+ + +package Debian::Debhelper::DH::AddonAPI; +use strict; +use warnings; + +use Debian::Debhelper::Dh_Lib qw(warning error); +use Debian::Debhelper::Sequence; +use Debian::Debhelper::SequencerUtil; +use Debian::Debhelper::DH::SequenceState; + + +our ($DH_INTERNAL_ADDON_TYPE, $DH_INTERNAL_ADDON_NAME); + +sub _add_sequence { + my @args = @_; + my $seq = Debian::Debhelper::Sequence->new(@args); + my $name = $seq->name; + $Debian::Debhelper::DH::SequenceState::sequences{$name} = $seq; + if ($seq->allowed_subsequences eq SEQUENCE_ARCH_INDEP_SUBSEQUENCES) { + for my $subseq ((SEQUENCE_TYPE_ARCH_ONLY, SEQUENCE_TYPE_INDEP_ONLY)) { + my $subname = "${name}-${subseq}"; + $Debian::Debhelper::DH::SequenceState::sequences{$subname} = $seq; + } + } + return; +} + +sub _skip_cmd_if_deb_build_options_contains { + my ($command, $flag) = @_; + push(@{$Debian::Debhelper::DH::SequenceState::commands_skippable_via_deb_build_options{$command}}, $flag); + return; +} + +sub _assert_not_conditional_sequence_addon { + my ($feature) = @_; + return if $DH_INTERNAL_ADDON_TYPE eq 'both'; + warning("The add-on ${DH_INTERNAL_ADDON_NAME} relies on a feature (${feature}) (possibly indirectly), which is " + . 'not supported for conditional debhelper sequence add-ons.'); + warning("Hint: You may have to move the build-dependency for dh-sequence-${DH_INTERNAL_ADDON_NAME} to " + . 'Build-Depends to avoid this error assuming it is possible to use the sequence unconditionally.'); + error("${feature} is not supported for conditional dh sequence add-ons.\n"); +} + +sub _filter_sequences_for_conditional_add_ons { + my @sequences = @_; + # If it is unconditional, then there is no issues. + return @sequences if $DH_INTERNAL_ADDON_TYPE eq 'both' or not @sequences; + for my $seq (@sequences) { + # Typically, if you add a command to a sequence, then you will in fact add it to two. E.g. + # Adding dh_foo after dh_installdocs will affect both install-arch AND install-indep. We want + # this to "just work(tm)" with a conditional add-on to avoid too much hassle (i.e. only affect + # the relevant sequence). At the same time, we must abort if a sequence like "clean" is + # affected. + # + # We solve the above by checking if the sequence has an -arch + an -indep variant and then + # insert the command only for that sequence variant. + + if ($seq->allowed_subsequences ne SEQUENCE_ARCH_INDEP_SUBSEQUENCES) { + my $sequence_name = $seq->name; + warning("The add-on ${DH_INTERNAL_ADDON_NAME} attempted to modify the sequence ${sequence_name} (possibly " + . "indirectly) but the add-on is conditional for \"*-${DH_INTERNAL_ADDON_TYPE}\" targets"); + warning("Hint: You may have to move the build-dependency for dh-sequence-${DH_INTERNAL_ADDON_NAME} to " + . 'Build-Depends to avoid this error assuming it is possible to use the sequence unconditionally.'); + error("The add-on ${DH_INTERNAL_ADDON_NAME} cannot be use conditionally for \"*-${DH_INTERNAL_ADDON_TYPE}\"" + . " targets\n"); + } + } + return @sequences; +} + +sub _register_cmd_added_by_addon { + my ($cmd) = @_; + my $existing = $Debian::Debhelper::DH::SequenceState::commands_added_by_addon{$cmd}; + if ($existing) { + if ($existing->{'addon-type'} ne $DH_INTERNAL_ADDON_TYPE) { + my $old_addon_name = $existing->{'addon-name'}; + my $old_addon_type = $existing->{'addon-type'}; + # Technically, "both" could be made compatible with "indep" OR "arch" (but not both at the same time). + # Implement if it turns out to be relevant. + warning("Both dh sequence add-ons ${DH_INTERNAL_ADDON_NAME} and ${old_addon_name} have attempted to add " + . "the command $cmd (possibly indirectly)."); + warning("However, the two add-ons do not have compatible constraints (${DH_INTERNAL_ADDON_TYPE} vs. " + . "${old_addon_type})."); + warning("Hint: You may have to move the build-dependency for dh-sequence-<X> to " + . ' the same build-dependency field to avoid this error assuming it is possible.'); + error("Multiple sequences have conflicting requests for $cmd.\n"); + } + return; + } + + $Debian::Debhelper::DH::SequenceState::commands_added_by_addon{$cmd} = { + 'addon-name' => $DH_INTERNAL_ADDON_NAME, + 'addon-type' => $DH_INTERNAL_ADDON_TYPE, + }; + return; +} + +sub _sequences_containing_cmd { + my ($cmd) = @_; + my @sequences; + foreach my $sequence_name (keys(%Debian::Debhelper::DH::SequenceState::sequences)) { + my $seq = $Debian::Debhelper::DH::SequenceState::sequences{$sequence_name}; + for my $scmd (@{$seq->{'_cmds'}}) { + if ($scmd->{'command'} eq $cmd) { + push(@sequences, $seq); + last; + } + } + } + return @sequences; +} + +sub _seq_cmd { + my ($cmd_name) = @_; + return { + 'command' => $cmd_name, + 'command-options' => [], + 'sequence-limitation' => $DH_INTERNAL_ADDON_TYPE, + }; +} + +# sequence addon interface +sub _insert { + my ($offset, $existing, $new) = @_; + my @affected_sequences = _sequences_containing_cmd($existing); + @affected_sequences = _filter_sequences_for_conditional_add_ons(@affected_sequences); + return if not @affected_sequences; + _register_cmd_added_by_addon($new); + for my $seq (@affected_sequences) { + $seq->_insert($offset, $existing, _seq_cmd($new)); + } + return 1; +} +sub insert_before { + return _insert(-1, @_); +} +sub insert_after { + return _insert(1, @_); +} +sub remove_command { + my ($command) = @_; + # Implement if actually needed (I *think* it basically means to transform dh_foo to dh_foo -a/-i) + _assert_not_conditional_sequence_addon('remove_command'); + my @affected_sequences = _sequences_containing_cmd($command); + @affected_sequences = _filter_sequences_for_conditional_add_ons(@affected_sequences); + return 1 if not @affected_sequences; + for my $seq (@affected_sequences) { + $seq->remove_command($command); + } + return 1; +} +sub add_command { + my ($command, $sequence) = @_; + _assert_not_conditional_sequence_addon('add_command'); + _register_cmd_added_by_addon($command); + if (not exists($Debian::Debhelper::DH::SequenceState::sequences{$sequence})) { + _add_sequence($sequence, SEQUENCE_NO_SUBSEQUENCES, _seq_cmd($command)); + } else { + my $seq = $Debian::Debhelper::DH::SequenceState::sequences{$sequence}; + _filter_sequences_for_conditional_add_ons($seq); + $seq->add_command_at_start(_seq_cmd($command)) + } + return 1; +} +sub add_command_at_end { + my ($command, $sequence) = @_; + _assert_not_conditional_sequence_addon('add_command'); + _register_cmd_added_by_addon($command); + if (not exists($Debian::Debhelper::DH::SequenceState::sequences{$sequence})) { + _add_sequence($sequence, SEQUENCE_NO_SUBSEQUENCES, _seq_cmd($command)); + } else { + my $seq = $Debian::Debhelper::DH::SequenceState::sequences{$sequence}; + _filter_sequences_for_conditional_add_ons($seq); + $seq->add_command_at_end(_seq_cmd($command)) + } + return 1; +} + +sub add_command_options { + my $command=shift; + # Implement if actually needed (Complicated as dh_foo becomes dh_foo -a && dh_foo -i <extra_options> + # and that implies smarter deduplication logic) + _assert_not_conditional_sequence_addon('add_command_options'); + push(@{$Debian::Debhelper::DH::SequenceState::command_opts{$command}}, @_); + return 1; +} + +sub remove_command_options { + my ($command, @cmd_options) = @_; + # Implement if actually needed (Complicated as dh_foo <extra_options> becomes + # dh_foo -a <extra_options> && dh_foo -i and that implies smarter deduplication logic) + _assert_not_conditional_sequence_addon('remove_command_options'); + if (@cmd_options) { + # Remove only specified options + if (my $opts = $Debian::Debhelper::DH::SequenceState::command_opts{$command}) { + foreach my $opt (@cmd_options) { + $opts = [ grep { $_ ne $opt } @$opts ]; + } + $Debian::Debhelper::DH::SequenceState::command_opts{$command} = $opts; + } + } + else { + # Clear all additional options + delete($Debian::Debhelper::DH::SequenceState::command_opts{$command}); + } + return 1; +} + +sub declare_command_obsolete { + my ($error_compat, $command) = @_; + if (not defined($command) and defined($error_compat)) { + # Backwards compat - originally this only accepted one command. + $command = $error_compat; + $error_compat = 13; + } + if ($error_compat < 13) { + error("Minimum error compat is 13 (got ${error_compat} for command: ${command})"); + } + _assert_not_conditional_sequence_addon('declare_command_obsolete'); + $Debian::Debhelper::DH::SequenceState::obsolete_command{$command} = [$DH_INTERNAL_ADDON_NAME, $error_compat]; + return 1; +} + + +1; diff --git a/lib/Debian/Debhelper/DH/SequenceState.pm b/lib/Debian/Debhelper/DH/SequenceState.pm new file mode 100644 index 0000000..b029e01 --- /dev/null +++ b/lib/Debian/Debhelper/DH/SequenceState.pm @@ -0,0 +1,31 @@ +# Defines dh sequence state variables +# +# License: GPL-2+ + +package Debian::Debhelper::DH::SequenceState; +use strict; +use warnings; + +our ( + # Definitions of sequences. + %sequences, + # Additional command options + %command_opts, + # Track commands added by (which) addons + %commands_added_by_addon, + # Removed commands + %obsolete_command, + # Commands that can be skipped due to DEB_BUILD_OPTIONS=X flags + %commands_skippable_via_deb_build_options, + # Options passed that should be passed on to underlying helpers (in order) + @options, + # Options passed by name (to assist can_skip with which options are used) + %seen_options, + # Whether there were sequences of options that inhibit certain optimizations + # * $unoptimizable_option_bundle => can skip iff cli-options hint is present and empty + # * $unoptimizable_user_option => We can never skip anything (non-option seen) + $unoptimizable_option_bundle, + $unoptimizable_user_option, +); + +1; |