diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 12:53:53 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 12:53:53 +0000 |
commit | 90169463f86997737ed5b9c0ea2b311cd3b056b7 (patch) | |
tree | 281a0f8d9850ea58cf2a3ddb8bf087fb52520925 /dh_makeshlibs | |
parent | Initial commit. (diff) | |
download | debhelper-upstream/13.15.3.tar.xz debhelper-upstream/13.15.3.zip |
Adding upstream version 13.15.3.upstream/13.15.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dh_makeshlibs')
-rwxr-xr-x | dh_makeshlibs | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/dh_makeshlibs b/dh_makeshlibs new file mode 100755 index 0000000..32c634d --- /dev/null +++ b/dh_makeshlibs @@ -0,0 +1,511 @@ +#!/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"); + my $ma = package_multiarch($package); + my $ma_quoted = quotemeta($ma); + my $skip_dir_parent = qr{ + /usr/lib(?:/${ma_quoted})?/?$ + }x; + # Maybe this should be an allow list instead (#204975) + my $skip_dir_basename = qr{ + jni + | python(?:\d+[.][^/]++)? + | perl(?:5|-base)? + | ruby(?:gems-integration)? + }x; + + 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; + if ( -d and $File::Find::dir =~ $skip_dir_parent and $_ =~ $skip_dir_basename) { + $File::Find::prune = 1; + 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'); + } + + next if ! -f "$tmp/DEBIAN/symbols" and ! -f "$tmp/DEBIAN/shlibs"; + + my $t64_compat = Debian::Debhelper::Dh_Lib::t64_compat_name($package); + # Handle Provides: for the t64 transition + next if $t64_compat eq '' and $package !~ /^lib.*t64(?:-nss)?$/; + if ($t64_compat eq '') { + $t64_compat = $package; + $t64_compat =~ s/t64//; + if ($t64_compat eq $package) { + error("Failed to derive a t64 compat name for ${package}. Please file a bug against debhelper or add" + . ' the X-Time64-Compat header to d/control, in which you can provide the compat package name' + . ' you want.'); + } + } + + require Dpkg::Arch; + require Dpkg::BuildFlags; + my $arch = package_binary_arch($package); + my $bf = Dpkg::BuildFlags->new(); + if (Dpkg::Arch::debarch_to_cpubits($arch) != 32 or !$bf->get_feature("abi", "time64")) { + addsubstvar($package, "t64:Provides", $t64_compat, '= ${binary:Version}'); + } else { + # Avoid a "unknown" substvar from dpkg-gencontrol. + my $ext = pkgext($package); + my $substvars = "debian/${ext}substvars"; + ensure_substvars_are_present($substvars, 't64:Provides'); + } +} + +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 |