diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 21:06:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 21:06:40 +0000 |
commit | d827c6cf1631209f5042a9d1d8a7ecc24223c8a0 (patch) | |
tree | 91a431d301efd0e524bdfb0c46e97d591a9d7b03 /dh_installman | |
parent | Initial commit. (diff) | |
download | debhelper-upstream.tar.xz debhelper-upstream.zip |
Adding upstream version 13.11.4.upstream/13.11.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rwxr-xr-x | dh_installman | 430 | ||||
-rwxr-xr-x | dh_installmanpages | 208 |
2 files changed, 638 insertions, 0 deletions
diff --git a/dh_installman b/dh_installman new file mode 100755 index 0000000..015d439 --- /dev/null +++ b/dh_installman @@ -0,0 +1,430 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installman - install man pages into package build directories + +=cut + +use strict; +use warnings; +use File::Find; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installman> [S<I<debhelper options>>] [S<I<manpage> ...>] + +=head1 DESCRIPTION + +B<dh_installman> is a debhelper program that handles installing man +pages into the correct locations in package build directories. + +In compat 10 and earlier, this program was primarily for when +upstream's build system does not properly install them as a part of +its install step (or it does not have an install step). In compat 11 +and later, it also supports the default searchdir plus --sourcedir +like dh_install(1) and has the advantage that it respects the nodoc +build profile (unlike dh_install(1)). + +Even if you prefer to use L<dh_install(1)> for installing the manpages, +B<dh_installman> can still be useful for converting the manpage encoding +to UTF-8 and for converting F<.so> links (as described below). However, +that part happens automatically without any explicit configuration. + + +You tell B<dh_installman> what man pages go in your packages, and it figures out +where to install them based on the section field in their B<.TH> or +B<.Dt> line. If you have a properly formatted B<.TH> or B<.Dt> line, +your man page will be installed into the right directory, with the +right name (this includes proper handling of pages with a subsection, +like B<3perl>, which are placed in F<man3>, and given an extension of +F<.3perl>). If your B<.TH> or B<.Dt> line is incorrect or missing, the +program may guess wrong based on the file extension. + +It also supports translated man pages, by looking for extensions +like F<.ll.8> and F<.ll_LL.8>, or by use of the B<--language> switch. + +If B<dh_installman> seems to install a man page into the wrong section or with +the wrong extension, this is because the man page has the wrong section +listed in its B<.TH> or B<.Dt> line. Edit the man page and correct the +section, and B<dh_installman> will follow suit. See L<man(7)> for details +about the B<.TH> section, and L<mdoc(7)> for the B<.Dt> section. If +B<dh_installman> seems to install a man page into a directory +like F</usr/share/man/pl/man1/>, that is because your program has a +name like F<foo.pl>, and B<dh_installman> assumes that means it is translated +into Polish. Use B<--language=C> to avoid this. + +After the man page installation step, B<dh_installman> will check to see if +any of the man pages in the temporary directories of any of the packages it +is acting on contain F<.so> links. If so, it changes them to symlinks. + +Also, B<dh_installman> will use man to guess the character encoding of each +manual page and convert it to UTF-8. If the guesswork fails for some +reason, you can override it using an encoding declaration. See +L<manconv(1)> for details. + +From debhelper compatibility level 11 on, B<dh_install> will fall back to +looking in F<debian/tmp> for files, if it does not find them in the current +directory (or wherever you've told it to look using B<--sourcedir>). + +=head1 FILES + +=over 4 + +=item debian/I<package>.manpages + +Lists man pages to be installed. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<-A>, B<--all> + +Install all files specified by command line parameters in ALL packages +acted on. + +=item B<--language=>I<ll> + +Use this to specify that the man pages being acted on are written in the +specified language. + +=item B<--sourcedir=>I<dir> + +Look in the specified directory for files to be installed. This option +requires compat 11 or later (it is silently ignored in compat 10 or earlier). + +Note that this is not the same as the B<--sourcedirectory> option used +by the B<dh_auto_>I<*> commands. You rarely need to use this option, since +B<dh_installman> automatically looks for files in F<debian/tmp> in debhelper +compatibility level 11 and above. + +=item I<manpage> ... + +Install these man pages into the first package acted on. (Or in all +packages if B<-A> is specified). + +=back + +=head1 EXAMPLES + +An example F<debian/manpages> file could look like this: + + doc/man/foo.1 + # Translations + doc/man/foo.da.1 + doc/man/foo.de.1 + doc/man/foo.fr.1 + # NB: The following line is considered a polish translation + # of "foo.1" (and not a manpage written in perl called "foo.pl") + doc/man/foo.pl.1 + # ... + +=head1 NOTES + +An older version of this program, L<dh_installmanpages(1)>, is still used +by some packages, and so is still included in debhelper. +It is, however, deprecated, due to its counterintuitive and inconsistent +interface. Use this program instead. + +=cut + +init(options => { + "language=s" => \$dh{LANGUAGE}, + "sourcedir=s" => \$dh{SOURCEDIR}, +}); + + +# PROMISE: DH NOOP WITHOUT pkgfile-logged(manpages) tmp(usr/share/man) cli-options() + +my (@sofiles, @sodests); +my @all_packages = getpackages(); + +my $default_error_handler = compat(10) ? \&glob_expand_error_handler_reject_nomagic_warn_discard : \&glob_expand_error_handler_reject; +my $nodocs = is_build_profile_active('nodoc') || get_buildoption('nodoc') ? 1 : 0; +# We cannot assume documentation is built under nodoc, but if it is we must flag it as handled +# or dh_missing might make noise. +$default_error_handler = \&glob_expand_error_handler_silently_ignore if $nodocs; + +on_items_in_parallel(\@all_packages, sub { + + foreach my $package (@_) { + next if is_udeb($package); + + my $tmp = tmpdir($package); + my $file = pkgfile($package, "manpages"); + my @manpages; + my @search_dirs = ('.'); + my $skip_install = process_pkg($package) ? 0 : 1; + my $error_handler = $skip_install ? \&glob_expand_error_handler_silently_ignore : $default_error_handler; + @search_dirs = ($dh{SOURCEDIR} // '.', default_sourcedir($package)) if not compat(10); + + @manpages = filearray($file, \@search_dirs, $error_handler) if $file; + + if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) { + push @manpages, @ARGV; + } + + log_installed_files($package, @manpages); + + next if $skip_install or $nodocs; + + foreach my $page (@manpages) { + my $basename = basename($page); + + # Support compressed pages. + my $gz = ''; + if ($basename =~ m/(.*)(\.gz)/) { + $basename = $1; + $gz = $2; + } + + my ($fd, $section); + # See if there is a .TH or .Dt entry in the man page. If so, + # we'll pull the section field from that. + if ($gz) { + $fd = open_gz($page) or error("open $page failed: $!"); + } + else { + open($fd, '<', $page) or error("open $page failed: $!"); + } + while (<$fd>) { + if (/^\.TH\s+\S+\s+"?(\d+[^"\s]*)"?/ || + /^\.Dt\s+\S+\s+(\d+[^\s]*)/) { + $section = $1; + if ($section =~ m/^\d+[.]\d+/) { + warning("Ignoring section defined in TH/Dt for ${page} as it looks like a version number: ${section}"); + $section = undef; + } + last; + } + } + close($fd); + # Failing that, we can try to get it from the filename. + if (!$section) { + ($section) = $basename =~ m/\.([1-9]\w*)$/; + } + + # Now get the numeric component of the section. + my ($realsection) = $section =~ m/^(\d+)/ if defined $section; + if (!$realsection or ($realsection < 0 or $realsection > 9)) { + warning("Section for ${page} is computed as \"${section}\", which is not a valid section") + if defined($section); + error("Could not determine section for $page"); + } + + # Get the man page's name -- everything up to the last dot. + my ($instname) = $basename =~ m/^(.*)\./; + + my $destdir = "$tmp/usr/share/man/man$realsection/"; + my $langcode; + if (!defined $dh{LANGUAGE} || !exists $dh{LANGUAGE}) { + if (not compat(10) and $page =~ m{/man/(?:([a-z][a-z](?:_[A-Z][A-Z])?)(?:\.[^/]+)?)?/man[1-9]/}) { + # If it looks like it was installed in a proper man dir, assume the language + # from that is correct. + $langcode = $1; + } else { + # Translated man pages are typically specified by adding the + # language code to the filename, so detect that and + # redirect to appropriate directory, stripping the code. + ($langcode) = $basename =~ m/\.([a-z][a-z](?:_[A-Z][A-Z])?)\.(?:[1-9]|man)/; + # Avoid false positives such as /usr/share/man/man8/libnss_myhostname.so.2.8 + if (defined $langcode && $langcode eq 'so' && $basename =~ /^lib.*\.so(\.[0-9]+)*$/) { + $langcode = ''; + } + } + } elsif ($dh{LANGUAGE} ne 'C') { + $langcode = $dh{LANGUAGE}; + } + + if (defined $langcode && $langcode ne '') { + # Strip the language code from the instname. + $instname =~ s/\.$langcode$//; + } + + if (defined $langcode && $langcode ne '') { + $destdir = "$tmp/usr/share/man/$langcode/man$realsection/"; + } + $destdir =~ tr:/:/:s; # just for looks + my $instpage = "$destdir$instname.$section"; + + next if -l $instpage; + + install_dir($destdir); + if ($gz) { + doit({ stdout => $instpage }, 'zcat', $page); + } + else { + install_file($page, $instpage); + } + } + + # Now the .so conversion. + @sofiles = @sodests = (); + foreach my $dir (qw{usr/share/man}) { + if (-e "$tmp/$dir") { + find(\&find_so_man, "$tmp/$dir"); + } + } + foreach my $sofile (@sofiles) { + my $sodest = shift(@sodests); + rm_files($sofile); + make_symlink_raw_target($sodest, $sofile); + } + } + +}); + +# Now utf-8 conversion. +my $has_man_recode = 0; +$has_man_recode = 1 if has_man_db_tool('man-recode'); + +if ($has_man_recode || has_man_db_tool('man')) { + my (@manpages_to_reencode, @issues); + for my $package (@{$dh{DOPACKAGES}}) { + next if is_udeb($package); + my $tmp = tmpdir($package); + foreach my $dir (qw{usr/share/man}) { + next unless -e "$tmp/$dir"; + my %seen; + my $wanted = sub { + my $path = $File::Find::name; + return if -l $path || !-f _; + if ($path =~ m/\.dh-new$/) { + push(@issues, $path); + return; + } + my $uncompressed_name = $path; + $uncompressed_name =~ s/\.(?:gz|Z)$//; + if (exists($seen{$uncompressed_name})) { + my $msg = "Multiple definitions for manpage ${uncompressed_name} via different compressions."; + my @values = sort ($path, $seen{$uncompressed_name}); + my $warn_msg = $msg . ' Picking ' . $values[0] . ' as the canonical definition.'; + my $error_msg = $msg . ' Please ensure there is at most one definition.'; + deprecated_functionality($warn_msg, 13, $error_msg); + $path = $values[0]; + warn("Removing conflicting definition of ${uncompressed_name} (" . $values[1] + . ') to ensure deterministic behaviour.'); + rm_files($values[1]); + } + $seen{$uncompressed_name} = $path; + }; + find({ + no_chdir => 1, + wanted => $wanted, + }, "$tmp/$dir"); + push(@manpages_to_reencode, sort(values(%seen))); + } + + if (@issues) { + warning("Removing temporary manpages from another dh_installman instance"); + rm_files(@issues); + warning("Possibly race-condition detected or left-overs from an interrupted dh_installman (e.g. with ^C)"); + error("Please ensure there are no parallel dh_installman's running (for this pkg) and then re-run dh_installman"); + } + } + if (@manpages_to_reencode) { + on_items_in_parallel(\@manpages_to_reencode, \&reencode_manpages); + } +} else { + # Should only occur during debhelper building itself (to avoid a B-D on man-db). + warning("man is not available. Skipping re-encode of UTF-8 manpages") +} + +# Check if a file is a .so man page, for use by File::Find. +sub find_so_man { + # The -s test is because a .so file tends to be small. We don't want + # to open every man page. 1024 is arbitrary. + if (! -f $_ || -s _ > 1024 || -s _ == 0) { + return; + } + + # Test first line of file for the .so thing. + my $fd; + if (/\.gz$/) { + $fd = open_gz($_) or error("open $_ failed: $!"); + } + else { + open($fd, '<', $_) || error("open $_ failed: $!"); + } + my $l = <$fd>; + close($fd); + + if (! defined $l) { + error("failed to read $_"); + } + + if ($l=~m/\.so\s+(.*)\s*/) { + my $solink=$1; + # This test is here to prevent links like ... man8/../man8/foo.8 + if (basename($File::Find::dir) eq + dirname($solink)) { + $solink=basename($solink); + } + # A so link with a path is relative to the base of the man + # page hierarchy, but without a path, is relative to the + # current section. + elsif ($solink =~ m!/!) { + $solink="../$solink"; + } + + if (-e $solink || -e "$solink.gz") { + push @sofiles,"$File::Find::dir/$_"; + push @sodests,$solink; + } + } +} + +sub has_man_db_tool { + my ($tool) = @_; + open(my $old_stderr, '>&', *STDERR) or error("dup(STDERR, tmp_fd): $!"); + # Ignore the error; it is intended as noise-reduction. As long as we can restore + # the stderr later, the log will just be slightly more noisy than planned. + open(*STDERR, '>', '/dev/null') or warn("redirect stderr to /dev/null failed: $!"); + + my $res = defined(`$tool --version`); + open(*STDERR, '>&', $old_stderr) or error("dup(tmp_fd, STDERR): $!"); + close($old_stderr); + return $res; +} + +sub reencode_manpages { + my (@manpages) = @_; + if ($has_man_recode) { + xargs(\@manpages, 'man-recode', '--to-code', 'UTF-8', '--suffix', '.dh-new'); + } + for my $manpage (@manpages) { + my $manpage_tmp = "${manpage}.dh-new"; + $manpage_tmp =~ s/\.(?:gz|Z)\.dh-new$/.dh-new/; + if (not $has_man_recode) { + my $manpage_cmd = ($manpage =~ m{^/}) ? $manpage : "./${manpage}"; + doit({ stdout => $manpage_tmp }, 'man', '-l', '--recode', 'UTF-8', $manpage_cmd); + } + # recode uncompresses compressed pages + my $orig = $manpage; + rm_files($orig) if $manpage =~ s/\.(gz|Z)$//; + rename_path($manpage_tmp, $manpage); + } + # Bulk reset permissions of all re-encoded files + xargs(\@manpages, 'chmod', '0644', '--'); +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut diff --git a/dh_installmanpages b/dh_installmanpages new file mode 100755 index 0000000..22c669e --- /dev/null +++ b/dh_installmanpages @@ -0,0 +1,208 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_installmanpages - old-style man page installer (deprecated) + +=cut + +use strict; +use warnings; +use File::Find; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_installmanpages> [S<I<debhelper options>>] [S<I<file> ...>] + +=head1 DESCRIPTION + +B<dh_installmanpages> is a debhelper program that is responsible for +automatically installing man pages into F<usr/share/man/> +in package build directories. + +This is a DWIM-style program, with an interface unlike the rest of +debhelper. It is deprecated, and you are encouraged to use +L<dh_installman(1)> instead. + +B<dh_installmanpages> scans the current directory and all subdirectories for +filenames that look like man pages. (Note that only real files are looked +at; symlinks are ignored.) It uses L<file(1)> to verify that the files are +in the correct format. Then, based on the files' extensions, it installs +them into the correct man directory. + +All filenames specified as parameters will be skipped by B<dh_installmanpages>. +This is useful if by default it installs some man pages that you do not +want to be installed. + +After the man page installation step, B<dh_installmanpages> will check to see +if any of the man pages are F<.so> links. If so, it changes them to symlinks. + +=head1 OPTIONS + +=over 4 + +=item I<file> ... + +Do not install these files as man pages, even if they look like valid man +pages. + +=back + +=head1 BUGS + +B<dh_installmanpages> will install the man pages it finds into B<all> packages +you tell it to act on, since it can't tell what package the man +pages belong in. This is almost never what you really want (use B<-p> to work +around this, or use the much better L<dh_installman(1)> program instead). + +Files ending in F<.man> will be ignored. + +Files specified as parameters that contain spaces in their filenames will +not be processed properly. + +=cut + +warning("This program is deprecated, switch to dh_installman."); + +init(); + +# Check if a file is a man page, for use by File::Find. +my @manpages; +my @allpackages; +sub find_man { + # Does its filename look like a man page? + # .ex files are examples installed by deb-make, + # we don't want those, or .in files, which are + # from configure, nor do we want CVS .#* files. + if (! (-f $_ && /^.*\.[1-9].*$/ && ! /\.(ex|in)$/ && ! /^\.#/)) { + return; + } + + # It's not in a tmp directory is it? + if ($File::Find::dir=~m:debian/.*tmp.*:) { + return; + } + foreach my $dir (@allpackages) { + if ($File::Find::dir=~m:debian/\Q$dir\E:) { + return; + } + } + + # And file does think it's a real man page? + my $type=`file -z $_`; + if ($type !~ m/:.*roff/) { + return; + } + + # Good enough. + push @manpages,"$File::Find::dir/$_"; +} + +# Check if a file is a .so man page, for use by File::Find. +my @sofiles; +my @sodests; +sub find_so_man { + # The -s test is because a .so file tends to be small. We don't want + # to open every man page. 1024 is arbitrary. + if (! -f $_ || -s $_ > 1024) { + return; + } + + # Test first line of file for the .so thing. + open(my $fd, '<', $_); + my $l = <$fd>; + close($fd); + if ($l=~m/\.so\s+(.*)/) { + my $solink=$1; + # This test is here to prevent links like ... man8/../man8/foo.8 + if (basename($File::Find::dir) eq + dirname($solink)) { + $solink=basename($solink); + } + else { + $solink="../$solink"; + } + + push @sofiles,"$File::Find::dir/$_"; + push @sodests,$solink; + } +} + +foreach my $package (@{$dh{DOPACKAGES}}) { + next if is_udeb($package); + + my $tmp=tmpdir($package); + + # Find all filenames that look like man pages. + @manpages=(); + @allpackages=getpackages() if not @allpackages; + find(\&find_man,'.'); # populates @manpages + + foreach my $page (@manpages) { + $page=~s:^\./::; # just for looks + + my $basename=basename($page); + + # Skip all files listed on command line. + my $install=1; + foreach my $skip (@ARGV) { + # Look at basename of what's on connect line + # for backwards compatibility. + if ($basename eq basename($skip)) { + $install=undef; + last; + } + } + + if ($install) { + my $extdir="share"; + + my ($section)=$basename=~m/\.([1-9])/; + + my $destdir="$tmp/usr/$extdir/man/man$section/"; + + # Handle translated man pages. + my $instname=$basename; + my ($langcode)=$basename=~m/\.([a-z][a-z])\.([1-9])/; + if (defined $langcode && $langcode ne '') { + $destdir="$tmp/usr/$extdir/man/$langcode/man$section/"; + $instname=~s/\.$langcode\./\./; + } + + $destdir=~tr:/:/:s; # just for looks + + if (! -e "$destdir/$basename" && !-l "$destdir/$basename") { + install_dir($destdir); + install_file($page,$destdir.$instname); + } + } + } + + # Now the .so conversion. + @sofiles=@sodests=(); + foreach my $dir (qw{usr/share/man}) { + if (-e "$tmp/$dir") { + find(\&find_so_man, "$tmp/$dir"); + } + } + foreach my $sofile (@sofiles) { + my $sodest=shift(@sodests); + doit "rm","-f",$sofile; + doit "ln","-sf",$sodest,$sofile; + } +} + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut |