prepstrip/ecompressdir: parallelize operations
[portage.git] / bin / ebuild-helpers / ecompressdir
1 #!/bin/bash
2 # Copyright 1999-2011 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 source "${PORTAGE_BIN_PATH:-/usr/lib/portage/bin}"/helper-functions.sh
6
7 if [[ -z $1 ]] ; then
8         helpers_die "${0##*/}: at least one argument needed"
9         exit 1
10 fi
11
12 [[ " ${FEATURES} " == *" force-prefix "* ]] || \
13         case "$EAPI" in 0|1|2) ED=${D} EPREFIX= ;; esac
14
15 case $1 in
16         --ignore)
17                 shift
18                 for skip in "$@" ; do
19                         [[ -d ${ED}${skip} || -f ${ED}${skip} ]] \
20                                 && >> "${ED}${skip}.ecompress.skip"
21                 done
22                 exit 0
23                 ;;
24         --queue)
25                 shift
26                 set -- "${@/%/.ecompress.dir}"
27                 set -- "${@/#/${ED}}"
28                 ret=0
29                 for x in "$@" ; do
30                         >> "$x"
31                         ((ret|=$?))
32                 done
33                 [[ $ret -ne 0 ]] && helpers_die "${0##*/} failed"
34                 exit $ret
35                 ;;
36         --dequeue)
37                 [[ -n $2 ]] && vecho "${0##*/}: --dequeue takes no additional arguments" 1>&2
38                 find "${ED}" -name '*.ecompress.dir' -print0 \
39                         | sed -e 's:\.ecompress\.dir::g' -e "s:${ED}:/:g" \
40                         | ${XARGS} -0 ecompressdir
41                 find "${ED}" -name '*.ecompress.skip' -print0 | ${XARGS} -0 rm -f
42                 exit 0
43                 ;;
44         --*)
45                 helpers_die "${0##*/}: unknown arguments '$*'"
46                 exit 1
47                 ;;
48 esac
49
50 # figure out the new suffix
51 suffix=$(ecompress --suffix)
52
53 # funk_up_dir(action, suffix, binary)
54 #       - action: compress or decompress
55 #       - suffix: the compression suffix to work with
56 #       - binary: the program to execute that'll compress/decompress
57 # The directory we act on is implied in the ${dir} variable
58 funk_up_dir() {
59         local act=$1 suffix=$2 binary=$3
60
61         local negate=""
62         [[ ${act} == "compress" ]] && negate="!"
63
64         # first we act on all the files
65         find "${dir}" -type f ${negate} -iname '*'${suffix} -print0 | ${XARGS} -0 ${binary}
66         ((ret|=$?))
67
68         find "${dir}" -type l -print0 | \
69         while read -r -d $'\0' brokenlink ; do
70                 [[ -e ${brokenlink} ]] && continue
71                 olddest=$(readlink "${brokenlink}")
72                 # Ignore temporarily broken symlinks due to
73                 # _relocate_skip_dirs (bug #399595), and handle
74                 # absolute symlinks to files that aren't merged
75                 # yet (bug #405327).
76                 if [[ ${olddest} == /* ]] ; then
77                         [ -e "${D}${olddest}" ] && continue
78                         skip_dir_dest=${T}/ecompress-skip/${olddest#${EPREFIX}}
79                 else
80                         skip_dir_dest=${T}/ecompress-skip/${actual_dir#${ED}}/${brokenlink%/*}/${olddest}
81                 fi
82                 [[ -e ${skip_dir_dest} ]] && continue
83                 [[ ${act} == "compress" ]] \
84                         && newdest="${olddest}${suffix}" \
85                         || newdest="${olddest%${suffix}}"
86                 rm -f "${brokenlink}"
87                 [[ ${act} == "compress" ]] \
88                         && ln -snf "${newdest}" "${brokenlink}${suffix}" \
89                         || ln -snf "${newdest}" "${brokenlink%${suffix}}"
90                 ((ret|=$?))
91         done
92 }
93
94 # _relocate_skip_dirs(srctree, dsttree)
95 # Move all files and directories we want to skip running compression
96 # on from srctree to dsttree.
97 _relocate_skip_dirs() {
98         local srctree="$1" dsttree="$2"
99
100         [[ -d ${srctree} ]] || return 0
101
102         find "${srctree}" -name '*.ecompress.skip' -print0 | \
103         while read -r -d $'\0' src ; do
104                 src=${src%.ecompress.skip}
105                 dst="${dsttree}${src#${srctree}}"
106                 parent=${dst%/*}
107                 mkdir -p "${parent}"
108                 mv "${src}" "${dst}"
109                 mv "${src}.ecompress.skip" "${dst}.ecompress.skip"
110         done
111 }
112 hide_skip_dirs()    { _relocate_skip_dirs "${ED}" "${T}"/ecompress-skip/ ; }
113 restore_skip_dirs() { _relocate_skip_dirs "${T}"/ecompress-skip/ "${ED}" ; }
114
115 ret=0
116
117 rm -rf "${T}"/ecompress-skip
118
119 decompressors=(
120         ".Z"    "gunzip -f"
121         ".gz"   "gunzip -f"
122         ".bz2"  "bunzip2 -f"
123         ".xz"   "unxz -f"
124         ".lzma" "unxz -f"
125 )
126
127 multijob_init
128
129 for dir in "$@" ; do
130         dir=${dir#/}
131         dir="${ED}${dir}"
132         if [[ ! -d ${dir} ]] ; then
133                 vecho "${0##*/}: /${dir#${ED}} does not exist!"
134                 continue
135         fi
136         cd "${dir}"
137         actual_dir=${dir}
138         dir=. # use relative path to avoid 'Argument list too long' errors
139
140         # hide all the stuff we want to skip
141         hide_skip_dirs "${dir}"
142
143         # since we've been requested to compress the whole dir,
144         # delete any individual queued requests
145         rm -f "${actual_dir}.ecompress.dir"
146         find "${dir}" -type f -name '*.ecompress.file' -print0 | ${XARGS} -0 rm -f
147
148         # not uncommon for packages to compress doc files themselves
149         for (( d = 0; d < ${#decompressors[@]}; d += 2 )) ; do
150                 # It's faster to parallelize at this stage than to try to
151                 # parallelize the compressors.  This is because the find|xargs
152                 # ends up launching less compressors overall, so the overhead
153                 # of forking children ends up dominating.
154                 (
155                 multijob_child_init
156                 funk_up_dir "decompress" "${decompressors[i]}" "${decompressors[i+1]}"
157                 ) &
158                 multijob_post_fork
159                 : $(( ret |= $? ))
160         done
161
162         # forcibly break all hard links as some compressors whine about it
163         find "${dir}" -type f -links +1 -exec env file="{}" sh -c \
164                 'cp -p "${file}" "${file}.ecompress.break" ; mv -f "${file}.ecompress.break" "${file}"' \;
165
166         multijob_finish
167         : $(( ret |= $? ))
168
169         # now lets do our work
170         if [[ -n ${suffix} ]] ; then
171                 vecho "${0##*/}: $(ecompress --bin) /${actual_dir#${ED}}"
172                 funk_up_dir "compress" "${suffix}" "ecompress"
173         fi
174
175         # finally, restore the skipped stuff
176         restore_skip_dirs
177 done
178
179 [[ $ret -ne 0 ]] && helpers_die "${0##*/} failed"
180 exit ${ret}