diff options
Diffstat (limited to 'dh_install')
-rwxr-xr-x | dh_install | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/dh_install b/dh_install new file mode 100755 index 0000000..665f77c --- /dev/null +++ b/dh_install @@ -0,0 +1,393 @@ +#!/usr/bin/perl + +=head1 NAME + +dh_install - install files into package build directories + +=cut + +use strict; +use warnings; +use Debian::Debhelper::Dh_Lib; + +our $VERSION = DH_BUILTIN_VERSION; + +=head1 SYNOPSIS + +B<dh_install> [B<-X>I<item>] [B<--autodest>] [B<--sourcedir=>I<dir>] [S<I<debhelper options>>] [S<I<file|dir> ... I<destdir>>] + +=head1 DESCRIPTION + +B<dh_install> is a debhelper program that handles installing files into package +build directories. There are many B<dh_install>I<*> commands that handle installing +specific types of files such as documentation, examples, man pages, and so on, +and they should be used when possible as they often have extra intelligence for +those particular tasks. B<dh_install>, then, is useful for installing everything +else, for which no particular intelligence is needed. It is a replacement for +the old B<dh_movefiles> command. + +This program may be used in one of two ways. If you just have a file or two +that the upstream Makefile does not install for you, you can run B<dh_install> +on them to move them into place. On the other hand, maybe you have a large +package that builds multiple binary packages. You can use the upstream +F<Makefile> to install it all into F<debian/tmp>, and then use B<dh_install> to copy +directories and files from there into the proper package build directories. + +From debhelper compatibility level 7 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>.install + +List the files to install into each package and the directory they should be +installed to. The format is a set of lines, where each line lists a file or +files to install, and at the end of the line tells the directory it should be +installed in. The name of the files (or directories) to install should be given +relative to the current directory, while the installation directory is given +relative to the package build directory. You may use wildcards in the names of +the files to install. + +Note that if you list exactly one filename or wildcard-pattern on a line by +itself, with no explicit destination, then B<dh_install> +will automatically guess the destination to use, the same as if the +--autodest option were used. + +Supports substitution variables in compat 13 and later as +documented in L<debhelper(7)>. + +=item debian/not-installed + +Used with the deprecated B<--list-missing> and B<--fail-missing> options. +Please refer to L<dh_missing(1)> for the documentation of this file. + +=back + +=head1 OPTIONS + +=over 4 + +=item B<--list-missing> + +B<Deprecated>: Please use B<dh_missing --list-missing> instead. If you use +this option, B<dh_install> will call B<dh_missing> with that option after it has +processed all the files. Please see L<dh_missing(1)> for the documentation of +this option. + +This option is removed in compat 12. + +=item B<--fail-missing> + +B<Deprecated>: Please use B<dh_missing --fail-missing> instead. If you use +this option, B<dh_install> will call B<dh_missing> with that option after it has +processed all the files. Please see L<dh_missing(1)> for the documentation of +this option. + +This option is removed in compat 12. + +=item B<--sourcedir=>I<dir> + +Look in the specified directory for files to be installed. + +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_install> automatically looks for files in F<debian/tmp> in debhelper +compatibility level 7 and above. + +=item B<--autodest> + +Guess as the destination directory to install things to. If this is +specified, you should not list destination directories in +F<debian/package.install> files or on the command line. Instead, B<dh_install> +will guess as follows: + +Strip off F<debian/tmp> (or the sourcedir if one is given) from the front of +the filename, if it is present, and install into the dirname of the +filename. So if the filename is F<debian/tmp/usr/bin>, then that directory +will be copied to F<debian/package/usr/>. If the filename is +F<debian/tmp/etc/passwd>, it will be copied to F<debian/package/etc/>. + +=item I<file|dir> ... I<destdir> + +Lists files (or directories) to install and where to install them to. +The files will be installed into the first package F<dh_install> acts on. + +=back + +=cut + +init(options => { + "autodest" => \$dh{AUTODEST}, + "list-missing" => \$dh{LIST_MISSING}, + "fail-missing" => \$dh{FAIL_MISSING}, + "sourcedir=s" => \$dh{SOURCEDIR}, +}); + +my $srcdir = '.'; +if (defined($dh{SOURCEDIR})) { + $srcdir = $dh{SOURCEDIR}; + $srcdir =~ s{/+$}{}; + error("Invalid --sourcedir - must not be empty nor /") if not $srcdir; +} + +my $missing_files = 0; + +if ($dh{LIST_MISSING} || $dh{FAIL_MISSING}) { + deprecated_functionality('Please use dh_missing --list-missing/--fail-missing instead', 12); +} + +# Support for -X flag. +my $exclude = ''; +if ($dh{EXCLUDE_FIND}) { + $exclude = '! \( '.$dh{EXCLUDE_FIND}.' \)'; +} + +# PROMISE: DH NOOP WITHOUT pkgfile-logged(install) cli-options() + +foreach my $package (getpackages()) { + my (@installed, %dest2sources); + my $default_source_dir = default_sourcedir($package); + my @search_dirs = ($srcdir); + push(@search_dirs, $default_source_dir) if not compat(6); + + # Look at the install files for all packages to handle + # list-missing/fail-missing, but skip really installing for + # packages that are not being acted on. + my $skip_install = process_pkg($package) ? 0 : 1; + + my $tmp=tmpdir($package); + my $file=pkgfile($package,"install"); + + my @install; + if ($file) { + @install=filedoublearray($file); # no globbing here; done below + } + + + # With autodest, we can just pretend every pattern was on its own line + @install = map { [$_] } map { @$_ } @install if $dh{AUTODEST}; + + if (($package eq $dh{FIRSTPACKAGE} || $dh{PARAMS_ALL}) && @ARGV) { + if ($dh{AUTODEST}) { + # Same as above, with autodest, we can just isolate each entry + # - the split is for bug-backwards compatibility (#867866). + push(@install, map { [$_] } map { split } @ARGV); + } else { + # Bug backwards compatibility (#867866). The new "glob_expand" + # interface is smart enough to not split on spaces, but dh_install + # used to do that... *except* for the "DEST" since it was never + # passed to the glob function. + my @a = @ARGV; + my $dest = pop(@a) if @a > 1; + my @srcs = map { split } @a; + push(@srcs, $dest) if defined($dest); + push(@install, \@srcs); + } + } + + + my $glob_error_handler = sub { + # Do not require a match for packages that not acted on + # (directly). After all, the files might not have been + # generated/compiled. + return if $skip_install; + ++$missing_files; + goto \&glob_expand_error_handler_warn_and_discard; + }; + + foreach my $set (@install) { + my ($dest, @filelist, @patterns); + + if (@$set > 1) { + $dest=pop @$set; + } + # Skip excluded patterns. We will need two exclude checks per pattern; + # 1) exclude the entire pattern as people expect this to work (#814856) + # 2) exclude files matched by the pattern as people could have just + # excluded a single file of a "dir/*"-pattern. + # This line below filters entire patterns + @patterns = grep { not excludefile($_) } @{$set}; + next if not @patterns; + foreach my $glob (@patterns) { + my @found = glob_expand(\@search_dirs, $glob_error_handler, $glob); + push(@filelist, map { tr{/}{/}s; $_ } @found); + } + + if (! @filelist && ! $skip_install) { + warning("$package missing files: @$set"); + ++$missing_files; + next; + } + + # Do a quick bulk handling of excluded files and update @installed. + # - this is for filtering files matched by the pattern + @filelist = grep { not excludefile($_) } @filelist if $exclude; + push(@installed, @filelist); + + # ... because then we can short-curcit here. + next if $skip_install or $missing_files; + + if (not $exclude) { + my @unoptimized; + for my $src (@filelist) { + my $d = $dest // compute_dest($default_source_dir, $src); + my $basename = basename($src); + if (exists($dest2sources{$d}{$basename})) { + # If there is a clash, silently undo the optimizations. + # See #866405 and #868169. + my $replaced = delete($dest2sources{$d}{$basename}); + # Associate the $replaced the destination + # directory. We cannot be sure that compute_dest will + # get it right nor can we blindly set $dest. + # + # It is technically unnecessary for $src, but we + # might as well do it to possibly save a + # compute_dest call. + push(@unoptimized, [$replaced, $d], [$src, $d]); + next; + } + $dest2sources{$d}{$basename} = $src; + } + next if not @unoptimized; + @filelist = @unoptimized; + } + + foreach my $src (@filelist) { + + my $target_dest; + + if (ref($src)) { + # On a failed optimization, we will have the + # destination directory. + ($src, $target_dest) = @{$src}; + } else { + $target_dest = $dest; + if (! defined $target_dest) { + # Guess at destination directory. + $target_dest = compute_dest($default_source_dir, $src); + } + } + + # Make sure the destination directory exists. + install_dir("$tmp/$target_dest"); + + if (-d $src && $exclude) { + my $basename = basename($src); + my $dir = ($basename eq '.') ? $src : "$src/.."; + my $pwd=`pwd`; + chomp $pwd; + complex_doit("cd '$dir' && " . + "find '$basename' $exclude \\( -type f -or -type l \\) -print0 | LC_ALL=C sort -z | " . + "xargs -0 -I {} cp --reflink=auto --parents -dp {} $pwd/$tmp/$target_dest/"); + # cp is annoying so I need a separate pass + # just for empty directories + complex_doit("cd '$dir' && " . + "find '$basename' $exclude \\( -type d -and -empty \\) -print0 | LC_ALL=C sort -z | " . + "xargs -0 -I {} cp --reflink=auto --parents -a {} $pwd/$tmp/$target_dest/"); + } + else { + doit("cp", '--reflink=auto', "-a", $src, "$tmp/$target_dest/"); + } + } + } + + for my $dest (sort(keys(%dest2sources))) { + my @srcs = sort(values(%{$dest2sources{$dest}})); + # Make sure the destination directory exists. + install_dir("$tmp/$dest"); + xargs(\@srcs, "cp", '--reflink=auto', "-a", XARGS_INSERT_PARAMS_HERE, "$tmp/$dest/"); + } + log_installed_files($package, @installed); +} + +if ($missing_files) { + # There were files we could not install (e.g. patterns that matched nothing) + error("missing files, aborting"); +} + +if ($dh{LIST_MISSING} || $dh{FAIL_MISSING}) { + my @options; + foreach (@{$dh{EXCLUDE}}) { + push(@options, '--exclude', $_); + } + push(@options, '--sourcedir', $dh{SOURCEDIR}) if defined($dh{SOURCEDIR}); + push @options, "--list-missing" if $dh{LIST_MISSING}; + push @options, "--fail-missing" if $dh{FAIL_MISSING}; + doit("dh_missing", @options); +} + +sub compute_dest { + my ($source_dir, $dest) = @_; + + $dest =~ s/^(.*\/)?\Q$srcdir\E\///; + $dest =~ s/^(.*\/)?\Q$source_dir\E\///; + $dest = dirname("/".$dest); + + return $dest; +} + +=head1 EXAMPLES + +Here are some small examples of configuration files for dh_install. + + # Install my-prog into usr/bin (as "usr/bin/my-prog") + my-prog usr/bin + + # Install a plugins directory into usr/share/my-prog + # (as "usr/share/my-prog/plugins/") + plugins usr/share/my-prog + + # Install a file with spaces in into usr/share/my-prog/data + # (as "usr/share/my-prog/data/my datafile with spaces.txt") + # ASSUMES COMPAT 13, where substitution patterns are available + my${Space}datafile${Space}with${Space}spaces.txt usr/share/my-prog/data + + # Install a library into the multi-arch lib directory + # ASSUMES COMPAT 13, where substitution patterns are available + build/output/libfrop*.so.* usr/lib/${DEB_HOST_MULTIARCH} + +=head1 LIMITATIONS + +B<dh_install> cannot rename files or directories, it can only install them +with the names they already have into wherever you want in the package +build tree. + +There is also no way to filter out results based on build profiles or +architecture. For documentation content, consider using B<dh_installdocs> or +B<dh_installexamples> as those helpers account for the B<nodoc> build +profile. + +However, renaming and filtering can be achieved by using B<dh-exec> +with compatibility level 9 or later. An example debian/I<package>.install +file using B<dh-exec> could look like: + + #!/usr/bin/dh-exec + debian/default.conf => /etc/my-package/start.conf + build/foo /usr/bin <!pkg.bar.nofoo> + +Please remember the following three things: + +=over 4 + +=item * The package must be using compatibility level 9 or later (see L<debhelper(7)>) + +=item * The package will need a build-dependency on dh-exec. + +=item * The install file must be marked as executable. + +=back + +=head1 SEE ALSO + +L<debhelper(7)> + +This program is a part of debhelper. + +=head1 AUTHOR + +Joey Hess <joeyh@debian.org> + +=cut |