summaryrefslogtreecommitdiffstats
path: root/debian/dh_dlopenlibdeps
blob: 067618c1c609bdb1a3ea9244fc4389182258bcff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/perl
# SPDX-License-Identifier: CC0-1.0

=head1 NAME

dh_dlopenlibdeps - parse dlopen library dependencies from ELF notes

=cut

use strict;
use warnings;
use Debian::Debhelper::Dh_Lib;

our $VERSION = DH_BUILTIN_VERSION;

=head1 SYNOPSIS

B<dh_dlopenlibdeps> [S<I<debhelper options>>] [B<-X>I<item>]

=head1 DESCRIPTION

B<dh_dlopenlibdeps> is a debhelper program that is responsible for calculating
dlopen library dependencies for packages.

This program follows the dlopen notes metadata specification as defined at
https://systemd.io/ELF_PACKAGE_METADATA/

=head1 OPTIONS

=over 4

=item B<-X>I<item>, B<--exclude=>I<item>

Exclude files that contain F<item> anywhere in their filename from being
parsed. This will make their dependencies be ignored.
This may be useful in some situations, but use it with caution. This option
may be used more than once to exclude more than one thing.

=back

=cut

init();

on_pkgs_in_parallel {
	my $is_elf_file = sub {
		my ($file) = @_;
		my @file_args = Debian::Debhelper::Dh_Lib::_internal_optional_file_args();
		my $ff = qx_cmd('file', @file_args, '--brief', '-e', 'apptype', '-e', 'ascii',
						'-e', 'encoding', '-e', 'cdf', '-e', 'compress', '-e', 'tar', '--', $file);
		return 1 if $ff =~ m/ELF/;
		return 0;
	};

	foreach my $package (@_) {
		my $tmp = tmpdir($package);
		my $ext = pkgext($package);
		my (@filelist);
		my %required_packages;
		my %recommended_packages;
		my %suggested_packages;

		# Generate a list of ELF binaries in the package, ignoring any we were told to exclude.
		my $find_options='';
		if (defined($dh{EXCLUDE_FIND}) && $dh{EXCLUDE_FIND} ne '') {
			$find_options="! \\( $dh{EXCLUDE_FIND} \\)";
		}
		next if not -d $tmp;
		foreach my $file (split(/\n/, `find $tmp -type f \\( -perm /111 -or -name "*.so*" -or -name "*.cmxs" -or -name "*.node" \\) $find_options -print`)) {
			# Prune directories that contain separated debug symbols.
			# CAVEAT: There are files in /usr/lib/debug that are not detached debug symbols, which should be processed. (see #865982)
			next if $file =~ m!^\Q$tmp\E/usr/lib/debug/(lib|lib64|usr|bin|sbin|opt|dev|emul|\.build-id)/!;
			if ($is_elf_file->($file)) {
				push @filelist, $file;
			}
		}

		if (@filelist) {
			my $required_sonames = '';
			my $recommended_sonames = '';
			my $suggested_sonames = '';

			my $sonames = `dlopen-notes --sonames @filelist`;
			foreach my $line (split(/\n/, $sonames)) {
				my ($soname, $priority) = split(' ', $line, 2);

				if ($priority eq 'required') {
					$required_sonames .= " $soname";
				} elsif ($priority eq 'recommended') {
					$recommended_sonames .= " $soname";
				} elsif ($priority eq 'suggested') {
					$suggested_sonames .= " $soname";
				} else {
					warning("Unknown priority $priority for $soname");
				}
			}

			if ($required_sonames) {
				my $dpkg_query = `dpkg-query --search -- $required_sonames`;
				foreach my $line (split(/\n/, $dpkg_query)) {
					chomp $line;
					if ($line =~ m/^local diversion |^diversion by/) {
						next;
					}
					if ($line =~ m/^([-a-z0-9+]+):/) {
						$required_packages{$1} = 1;
					}
				}
			}

			if ($recommended_sonames) {
				my $dpkg_query = `dpkg-query --search -- $recommended_sonames`;
				foreach my $line (split(/\n/, $dpkg_query)) {
					chomp $line;
					if ($line =~ m/^local diversion |^diversion by/) {
						next;
					}
					if ($line =~ m/^([-a-z0-9+]+):/) {
						$recommended_packages{$1} = 1;
					}
				}
			}

			if ($suggested_sonames) {
				my $dpkg_query = `dpkg-query --search -- $suggested_sonames`;
				foreach my $line (split(/\n/, $dpkg_query)) {
					chomp $line;
					if ($line =~ m/^local diversion |^diversion by/) {
						next;
					}
					if ($line =~ m/^([-a-z0-9+]+):/) {
						$suggested_packages{$1} = 1;
					}
				}
			}
		}

		# Always write the substvars file, even if it's empty, so that the variables are defined and
		# there are no warnings when using them in the control file.
		open(SV, ">>debian/${ext}substvars") || error("open debian/${ext}substvars: $!");
		print SV "dlopen:Depends=" . join(", ", sort keys %required_packages) . "\n";
		print SV "dlopen:Recommends=" . join(", ", sort keys %recommended_packages) . "\n";
		print SV "dlopen:Suggests=" . join(", ", sort keys %suggested_packages) . "\n";
		close(SV);
	}
};

=head1 SEE ALSO

L<debhelper(7)>, L<dh_shlibsdeps(1)>

=head1 AUTHOR

Luca Boccassi <bluca@debian.org>

=cut