summaryrefslogtreecommitdiffstats
path: root/dh_makeshlibs
diff options
context:
space:
mode:
Diffstat (limited to 'dh_makeshlibs')
-rwxr-xr-xdh_makeshlibs466
1 files changed, 466 insertions, 0 deletions
diff --git a/dh_makeshlibs b/dh_makeshlibs
new file mode 100755
index 0000000..c928cad
--- /dev/null
+++ b/dh_makeshlibs
@@ -0,0 +1,466 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+dh_makeshlibs - automatically create shlibs file and call dpkg-gensymbols
+
+=cut
+
+use strict;
+use warnings;
+use Debian::Debhelper::Dh_Lib;
+
+our $VERSION = DH_BUILTIN_VERSION;
+
+=head1 SYNOPSIS
+
+B<dh_makeshlibs> [S<I<debhelper options>>] [B<-m>I<major>] [B<-V>I<[dependencies]>] [B<-n>] [B<-X>I<item>] [S<B<--> I<params>>]
+
+=head1 DESCRIPTION
+
+B<dh_makeshlibs> is a debhelper program that automatically scans for shared
+libraries, and generates a shlibs file for the libraries it finds.
+
+It will also ensure that ldconfig is invoked during install and removal when
+it finds shared libraries. Since debhelper 9.20151004, this is done via a
+dpkg trigger. In older versions of debhelper, B<dh_makeshlibs> would
+generate a maintainer script for this purpose.
+
+Since debhelper 12.3, B<dh_makeshlibs> will by default add an additional
+I<udeb> line for udebs in the shlibs file, when the udeb has the same
+name as the deb followed by a "-udeb" suffix (e.g. if the deb is called
+"libfoo1", then debhelper will auto-detect the udeb if it is named
+"libfoo1-udeb"). Please use the B<--add-udeb> and B<--no-add-udeb> options
+below when this auto-detection is insufficient.
+
+If you previously used B<--add-udeb> and are considering to migrate to
+using the new auto-detection feature in 12.3, then
+please remember to test that the resulting F<DEBIAN/shlibs> files are
+as expected. There are some known corner cases, where the
+auto-detection is insufficient. These include when the udeb contains
+library files from multiple regular deb packages or when the packages
+do not follow the expected naming convention.
+
+=head1 FILES
+
+=over 4
+
+=item debian/I<package>.shlibs
+
+Installs this file, if present, into the package as DEBIAN/shlibs. If
+omitted, debhelper will generate a shlibs file automatically if it
+detects any libraries.
+
+Note in compat levels 9 and earlier, this file was installed by
+L<dh_installdeb(1)> rather than B<dh_makeshlibs>.
+
+=item debian/I<package>.symbols
+
+=item debian/I<package>.symbols.I<arch>
+
+These symbols files, if present, are passed to L<dpkg-gensymbols(1)> to
+be processed and installed. Use the I<arch> specific names if you need
+to provide different symbols files for different architectures.
+
+=back
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-m>I<major>, B<--major=>I<major>
+
+Instead of trying to guess the major number of the library with objdump,
+use the major number specified after the -m parameter. This is much less
+useful than it used to be, back in the bad old days when this program
+looked at library filenames rather than using objdump.
+
+=item B<-V>, B<-V>I<dependencies>
+
+=item B<--version-info>, B<--version-info=>I<dependencies>
+
+If a shlibs file is generated by this program, this option controls
+what version will be used in the dependency relation.
+
+In compat 12 and later, B<dh_makeshlibs> defaults to B<-VUpstream-Version>.
+In compat 11 and earlier the default behaved like B<-VNone>.
+
+The B<dh_makeshlibs> tool can generate dependencies in three variants:
+
+=over 4
+
+=item B<-VUpstream-Version>
+
+The dependency will be "I<packagename> B<(E<gt>>= I<packageversion>B<)>".
+Note that I<Upstream-Version> is case-sensitive and must be written
+exactly as shown here.
+
+This is a conservative setting that always ensures that other packages'
+shared library dependencies are at least as tight as they need to be
+(unless the library is prone to changing ABI without updating the
+upstream version number).
+
+The flip side is that packages might end up with dependencies that are
+too tight in some cases (note a symbols file can mitigate this issue).
+This is often of minor temporary inconvenience and usually a lot
+better than the fall out caused by forgetting to bump the dependency
+information.
+
+This explicit form was added in debhelper/11.3. In previous versions,
+a B<-V> without any dependency information was used instead (and that
+form still works)
+
+=item B<-VNone>
+
+The dependency will be "I<packagename>". Note that I<None> is
+case-sensitive and must be written exactly as shown here.
+
+This form is generally unsafe with the only exception being if upstream
+does not extend the ABI in any way. However, most upstreams improve their
+interfaces over time and packagers are recommended to use
+B<-VUpstream-Version> (or one of the other forms of B<-V>I<dependencies>).
+
+Alternatively, this may be sufficient if (and only if) the package uses
+symbol versioning (see L<dpkg-gensymbols(1)>) and does I<not> build any
+udeb packages. Note that symbols are not supported for udeb packages,
+which solely relies on shlibs for dependency handling.
+
+=item B<-V>I<package-relation>
+
+In this case, the value passed to B<-V> will be used as a dependency
+relation. The I<package-relation> should generally be of the form
+"I<some-package-name> B<(E<gt>>= I<some-package-version>B<)>". Remember
+to include the package name.
+
+Note that debhelper will use the value I<as it is> with no sanity
+checking or modification. In I<rare special> cases, this is needed to
+generate a dependency on a different package than the one containing
+the library.
+
+=back
+
+When choosing a value for this option, please keep mind that if the
+package provides a symbols file, then that this is generally preferred over
+the shlibs file for regular .deb packages. See L<dpkg-shlibdeps(1)>
+for more information on this topic.
+
+=item B<-n>, B<--no-scripts>
+
+Do not add the "ldconfig" trigger even if it seems like the package
+might need it. The option is called B<--no-scripts> for historical
+reasons as B<dh_makeshlibs> would previously generate maintainer
+scripts that called B<ldconfig>.
+
+=item B<-X>I<item>, B<--exclude=>I<item>
+
+Exclude files that contain I<item> anywhere in their filename or directory
+from being treated as shared libraries.
+
+=item B<--add-udeb=>I<udeb>
+
+Create an additional line for udebs in the shlibs file and use I<udeb> as the
+package name for udebs to depend on instead of the regular library package.
+
+This option is only useful for special cases such as when debhelper
+cannot auto-detect package name of the udeb package, when the udeb
+will contain libraries from multiple deb packages, or when the udeb
+contains libraries B<not> present in the deb package.
+
+=item B<--no-add-udeb>
+
+Do not add any udeb lines to the shlibs file. This can be used to disable the
+default auto-detection of udebs.
+
+This may be useful in case you do not want a shlibs file at all for the udeb
+because no package will depend on it. E.g. because adding a udeb package
+for the library was "overkill" and the library is embedded in a different
+udeb package.
+
+=item B<--> I<params>
+
+Pass I<params> to L<dpkg-gensymbols(1)>.
+
+=back
+
+=head1 EXAMPLES
+
+=over 4
+
+=item B<dh_makeshlibs -VNone>
+
+Assuming this is a package named F<libfoobar1>, generates a shlibs file that
+looks something like:
+ libfoobar 1 libfoobar1
+
+=item B<dh_makeshlibs -VUpstream-Version>
+
+Assuming the current version of the package is 1.1-3, generates a shlibs
+file that looks something like:
+ libfoobar 1 libfoobar1 (>= 1.1)
+
+=item B<dh_makeshlibs -V 'libfoobar1 (E<gt>= 1.0)'>
+
+Generates a shlibs file that looks something like:
+ libfoobar 1 libfoobar1 (>= 1.0)
+
+=back
+
+=cut
+
+my ($shlibs_udeb, %known_udeb_solibs);
+
+init(options => {
+ "m=s", => \$dh{M_PARAMS},
+ "major=s" => \$dh{M_PARAMS},
+ "version-info:s" => \$dh{V_FLAG},
+ "add-udeb=s" => \$shlibs_udeb,
+ "no-add-udeb" => sub { $shlibs_udeb = ''; },
+});
+
+my $ok=1;
+
+sub _all_so_files {
+ my ($package, $root_dir) = @_;
+ return if not -d $root_dir;
+ my (@all_so_files, @so_file_data);
+ my $objdump = cross_command($package, "objdump");
+ require File::Find;
+ File::Find::find(sub {
+ # Lazy loading of File::Find makes perl think that File::Find::dir is only used once
+ # and we might have typo'ed something
+ no warnings qw(once);
+ # Only real/regular files
+ -l && return;
+ -f _ || return;
+ my $path = "$File::Find::dir/$_";
+ return if excludefile($path);
+ return if not is_so_or_exec_elf_file($_);
+ push(@all_so_files, $path);
+ }, $root_dir);
+
+ @all_so_files = sort(@all_so_files);
+ for my $lib_file (@all_so_files) {
+ my ($library, $major, $ret);
+ if (compat(10)) {
+ # In compat 10, we silently ignored failing exit codes
+ # from objdump. Its horrible, but such was compat 10.
+ $ret = `$objdump -p "$lib_file"`;
+ chomp($ret);
+ } else {
+ $ret = qx_cmd($objdump, '-p', $lib_file);
+ }
+ if ($ret=~m/\s+SONAME\s+(.*)\.so\.(.*)/) {
+ # proper soname format
+ $library=$1;
+ $major=$2;
+ } elsif ($ret=~m/\s+SONAME\s+(.*)-(\d.*)\.so/) {
+ # idiotic crap soname format
+ $library=$1;
+ $major=$2;
+ } elsif ($ret !~ m/\s+SONAME\s+(?:\S)/) {
+ next;
+ }
+ push(@so_file_data, [$lib_file, $library, $major,]);
+ };
+ return @so_file_data;
+}
+
+foreach my $package (@{$dh{DOPACKAGES}}) {
+ next if is_udeb($package);
+
+ my $tmp=tmpdir($package);
+
+ my (%seen, $unversioned_so);
+ my $need_ldconfig = 0;
+ # Note that since each package can have a shlibs file independently of
+ # each other, we need to make these local.
+ my $v_flag_set = $dh{V_FLAG_SET};
+ my $v_flag = $dh{V_FLAG} // '';
+ my $shlibs_file = pkgfile($package, 'shlibs');
+
+ rm_files("$tmp/DEBIAN/shlibs");
+
+ # So, we look for files or links to existing files with names that
+ # match "*.so.*". And we only look at real files not
+ # symlinks, so we don't accidentally add shlibs data to -dev
+ # packages. This may have a few false positives, which is ok,
+ # because only if we can get a library name and a major number from
+ # objdump is anything actually added.
+ my (@udeb_lines, @deb_lines, @lib_files, $udeb_name);
+ if (defined($shlibs_udeb)) {
+ $udeb_name = $shlibs_udeb if $shlibs_udeb ne '';
+ } else {
+ my $guessed_udeb = "${package}-udeb";
+ $udeb_name = $guessed_udeb if is_known_package($guessed_udeb) and is_udeb($guessed_udeb);
+ }
+ # If there is a udeb (which we assume there never is under the "noudeb" build-profile)
+ # then check it for libraries.
+ if (defined($udeb_name) and not is_build_profile_active('noudeb')) {
+ for my $so_data (_all_so_files($udeb_name, tmpdir($udeb_name))) {
+ my (undef, $library, $major) = @{$so_data};
+ $major = $dh{M_PARAMS} if defined($dh{M_PARAMS}) and $dh{M_PARAMS} ne '';
+ next if not defined($library) or not defined($major);
+ $known_udeb_solibs{$udeb_name}{"${library}\x1f${major}"} = 1;
+ }
+ # If the udeb contains no SO files but there was an explicit --add-udeb, then
+ # something is wrong.
+ error("The udeb $shlibs_udeb does not contain any shared libraries but --add-udeb=$shlibs_udeb was passed!?")
+ if defined($shlibs_udeb) and not exists($known_udeb_solibs{$udeb_name});
+ }
+ for my $so_data (_all_so_files($package, $tmp)) {
+ my ($lib_file, $library, $major) = @{$so_data};
+ push(@lib_files, $lib_file) if compat(11);
+ if (not defined($library)) {
+ $unversioned_so = 1;
+ push(@lib_files, $lib_file) if not compat(11);
+ }
+
+ if (defined($dh{M_PARAMS}) && $dh{M_PARAMS} ne '') {
+ $major=$dh{M_PARAMS};
+ }
+
+ my $deps=$package;
+ if ($v_flag_set) {
+ if ($shlibs_file) {
+ warning("The provided ${shlibs_file} file overwrites -V");
+ # Clear the flag to avoid duplicate warnings.
+ $v_flag_set = 0;
+ $v_flag = '';
+ } else {
+ # Set the default "-V" (with no value) is passed.
+ $v_flag = 'Upstream-Version' if $v_flag eq '';
+ }
+ } elsif ($v_flag eq '') {
+ # Set the default if "-V" is omitted.
+ $v_flag = compat(11) ? 'None' : 'Upstream-Version';
+ }
+ if ($v_flag ne '') {
+ if ($v_flag eq 'Upstream-Version') {
+ # Call isnative because it sets $dh{VERSION}
+ # as a side effect.
+ isnative($package);
+ my $version = $dh{VERSION};
+ # Old compatibility levels include the
+ # debian revision, while new do not.
+ # Remove debian version, if any.
+ $version =~ s/-[^-]+$//;
+ $deps = "$package (>= $version)";
+ } elsif ($v_flag ne 'None') {
+ $deps = $v_flag;
+ }
+ }
+ if (defined($library) && defined($major) && defined($deps) &&
+ $library ne '' && $major ne '' && $deps ne '') {
+ $need_ldconfig=1;
+ push(@lib_files, $lib_file) if not compat(11);
+ # Prevent duplicate lines from entering the file.
+ my $line="$library $major $deps";
+ if (! $seen{$line}) {
+ $seen{$line}=1;
+ push(@deb_lines, $line);
+ if (defined($udeb_name)) {
+ my $udeb_deps = $deps;
+ $udeb_deps =~ s/\Q$package\E/$udeb_name/e;
+ $line="udeb: $library $major $udeb_deps";
+ push @udeb_lines, $line;
+ # Track which libraries have been used in the udeb to ensure
+ # we spot missing libraries.
+ delete($known_udeb_solibs{$udeb_name}{"${library}\x1f${major}"})
+ if defined($udeb_name);
+ }
+ }
+ }
+ }
+
+ if (defined($udeb_name) and not $shlibs_udeb) {
+ my $issues = 0;
+ for my $lib_key (sort(keys(%{$known_udeb_solibs{$udeb_name}}))) {
+ my ($library, $major) = split(qr/\x1f/, $lib_key);
+ warning("$udeb_name contains SO library $library (version $major) but $package does not contain a similar library!?");
+ $issues = 1;
+ }
+ if ($issues) {
+ $ok = 0;
+ warning("Rejecting the generated shlibs file for $udeb_name!");
+ warning("Hint: Either add the missing libraries to $package, remove them from $udeb_name, or");
+ warning("Hint: (if this difference is expected) pass \"--add-udeb=$udeb_name\" to dh_makeshlibs.");
+ warning("Hint: In the latter case, you *may* also need to combine it with \"-p$package\"");
+ warning("Hint: Alternatively, if you have merged the shared lib package into $udeb_name and it has no");
+ warning("Hint: other packages need to know of this library, then use \"--no-add-udeb\"");
+ }
+ }
+
+ if ($shlibs_file) {
+ install_dir("$tmp/DEBIAN");
+ install_file($shlibs_file, "$tmp/DEBIAN/shlibs");
+ } elsif (@deb_lines or @udeb_lines) {
+ install_dir("$tmp/DEBIAN");
+ if ($dh{VERBOSE}) {
+ verbose_print('echo ' . escape_shell($_) . ' >> ' . escape_shell("$tmp/DEBIAN/shlibs"))
+ for @deb_lines, @udeb_lines;
+ }
+ if (not $dh{NO_ACT}) {
+ open(my $shlibs_fd, '>', "$tmp/DEBIAN/shlibs") or error("open($tmp/DEBIAN/shlibs): $!");
+ # Write the shlibs file with the udeb: lines last.
+ print {$shlibs_fd} "$_\n" for @deb_lines, @udeb_lines;
+ close($shlibs_fd) or error("close($tmp/DEBIAN/shlibs");
+ }
+ }
+
+ if (-e "$tmp/DEBIAN/shlibs") {
+ reset_perm_and_owner(0644, "$tmp/DEBIAN/shlibs");
+ }
+
+ # dpkg-gensymbols files
+ my $symbols=pkgfile($package, "symbols");
+ if (-e $symbols) {
+ my @liblist;
+ if (! compat(7)) {
+ @liblist=map { "-e$_" } @lib_files;
+ }
+ # -I is used rather than using dpkg-gensymbols
+ # own search for symbols files, since that search
+ # is not 100% compatible with debhelper. (For example,
+ # this supports --ignore being used.)
+ $ok = doit_noerror(
+ "dpkg-gensymbols",
+ "-p$package",
+ "-I$symbols",
+ "-P$tmp",
+ @liblist,
+ @{$dh{U_PARAMS}}
+ ) && $ok;
+
+ if (-f "$tmp/DEBIAN/symbols" and -s _ == 0) {
+ rm_files("$tmp/DEBIAN/symbols");
+ } elsif ($unversioned_so) {
+ # There are a few "special" libraries (e.g. nss/nspr)
+ # which do not have versioned SONAMES. However the
+ # maintainer provides a symbols file for them and we can
+ # then use that to add an ldconfig trigger.
+ $need_ldconfig = 1;
+ }
+ }
+
+ # Historically, --no-scripts would disable the creation of
+ # maintscripts for calling ldconfig.
+ if (! $dh{NOSCRIPTS} && $need_ldconfig) {
+ autotrigger($package, 'activate-noawait', 'ldconfig');
+ }
+}
+
+unless ($ok) {
+ error "failing due to earlier errors";
+}
+
+=head1 SEE ALSO
+
+L<debhelper(7)>
+
+This program is a part of debhelper.
+
+=head1 AUTHOR
+
+Joey Hess <joeyh@debian.org>
+
+=cut