dev-python/packaging: x86 stable wrt bug #716404
[gentoo.git] / eclass / unpacker.eclass
1 # Copyright 1999-2017 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 # @ECLASS: unpacker.eclass
5 # @MAINTAINER:
6 # base-system@gentoo.org
7 # @BLURB: helpers for extraneous file formats and consistent behavior across EAPIs
8 # @DESCRIPTION:
9 # Some extraneous file formats are not part of PMS, or are only in certain
10 # EAPIs.  Rather than worrying about that, support the crazy cruft here
11 # and for all EAPI versions.
12
13 # Possible todos:
14 #  - merge rpm unpacking
15 #  - support partial unpacks?
16
17 if [[ -z ${_UNPACKER_ECLASS} ]]; then
18 _UNPACKER_ECLASS=1
19
20 # @ECLASS-VARIABLE: UNPACKER_BZ2
21 # @DEFAULT_UNSET
22 # @DESCRIPTION:
23 # Utility to use to decompress bzip2 files.  Will dynamically pick between
24 # `pbzip2` and `bzip2`.  Make sure your choice accepts the "-dc" options.
25 # Note: this is meant for users to set, not ebuilds.
26
27 # @ECLASS-VARIABLE: UNPACKER_LZIP
28 # @DEFAULT_UNSET
29 # @DESCRIPTION:
30 # Utility to use to decompress lzip files.  Will dynamically pick between
31 # `plzip`, `pdlzip` and `lzip`.  Make sure your choice accepts the "-dc" options.
32 # Note: this is meant for users to set, not ebuilds.
33
34 # for internal use only (unpack_pdv and unpack_makeself)
35 find_unpackable_file() {
36         local src=$1
37         if [[ -z ${src} ]] ; then
38                 src=${DISTDIR}/${A}
39         else
40                 if [[ ${src} == ./* ]] ; then
41                         : # already what we want
42                 elif [[ -e ${DISTDIR}/${src} ]] ; then
43                         src=${DISTDIR}/${src}
44                 elif [[ -e ${PWD}/${src} ]] ; then
45                         src=${PWD}/${src}
46                 elif [[ -e ${src} ]] ; then
47                         src=${src}
48                 fi
49         fi
50         [[ ! -e ${src} ]] && return 1
51         echo "${src}"
52 }
53
54 unpack_banner() {
55         echo ">>> Unpacking ${1##*/} to ${PWD}"
56 }
57
58 # @FUNCTION: unpack_pdv
59 # @USAGE: <file to unpack> <size of off_t>
60 # @DESCRIPTION:
61 # Unpack those pesky pdv generated files ...
62 # They're self-unpacking programs with the binary package stuffed in
63 # the middle of the archive.  Valve seems to use it a lot ... too bad
64 # it seems to like to segfault a lot :(.  So lets take it apart ourselves.
65 #
66 # You have to specify the off_t size ... I have no idea how to extract that
67 # information out of the binary executable myself.  Basically you pass in
68 # the size of the off_t type (in bytes) on the machine that built the pdv
69 # archive.
70 #
71 # One way to determine this is by running the following commands:
72 #
73 # @CODE
74 #       strings <pdv archive> | grep lseek
75 #       strace -elseek <pdv archive>
76 # @CODE
77 #
78 # Basically look for the first lseek command (we do the strings/grep because
79 # sometimes the function call is _llseek or something) and steal the 2nd
80 # parameter.  Here is an example:
81 #
82 # @CODE
83 #       $ strings hldsupdatetool.bin | grep lseek
84 #       lseek
85 #       $ strace -elseek ./hldsupdatetool.bin
86 #       lseek(3, -4, SEEK_END)                                  = 2981250
87 # @CODE
88 #
89 # Thus we would pass in the value of '4' as the second parameter.
90 unpack_pdv() {
91         local src=$(find_unpackable_file "$1")
92         local sizeoff_t=$2
93
94         [[ -z ${src} ]] && die "Could not locate source for '$1'"
95         [[ -z ${sizeoff_t} ]] && die "No idea what off_t size was used for this pdv :("
96
97         unpack_banner "${src}"
98
99         local metaskip=$(tail -c ${sizeoff_t} "${src}" | hexdump -e \"%i\")
100         local tailskip=$(tail -c $((${sizeoff_t}*2)) "${src}" | head -c ${sizeoff_t} | hexdump -e \"%i\")
101
102         # grab metadata for debug reasons
103         local metafile="${T}/${FUNCNAME}.meta"
104         tail -c +$((${metaskip}+1)) "${src}" > "${metafile}"
105
106         # rip out the final file name from the metadata
107         local datafile=$(tail -c +$((${metaskip}+1)) "${src}" | strings | head -n 1)
108         datafile=$(basename "${datafile}")
109
110         # now lets uncompress/untar the file if need be
111         local tmpfile="${T}/${FUNCNAME}"
112         tail -c +$((${tailskip}+1)) ${src} 2>/dev/null | head -c 512 > "${tmpfile}"
113
114         local iscompressed=$(file -b "${tmpfile}")
115         if [[ ${iscompressed:0:8} == "compress" ]] ; then
116                 iscompressed=1
117                 mv "${tmpfile}"{,.Z}
118                 gunzip "${tmpfile}"
119         else
120                 iscompressed=0
121         fi
122         local istar=$(file -b "${tmpfile}")
123         if [[ ${istar:0:9} == "POSIX tar" ]] ; then
124                 istar=1
125         else
126                 istar=0
127         fi
128
129         #for some reason gzip dies with this ... dd cant provide buffer fast enough ?
130         #dd if=${src} ibs=${metaskip} count=1 \
131         #       | dd ibs=${tailskip} skip=1 \
132         #       | gzip -dc \
133         #       > ${datafile}
134         if [ ${iscompressed} -eq 1 ] ; then
135                 if [ ${istar} -eq 1 ] ; then
136                         tail -c +$((${tailskip}+1)) "${src}" 2>/dev/null \
137                                 | head -c $((${metaskip}-${tailskip})) \
138                                 | tar -xzf -
139                 else
140                         tail -c +$((${tailskip}+1)) "${src}" 2>/dev/null \
141                                 | head -c $((${metaskip}-${tailskip})) \
142                                 | gzip -dc \
143                                 > ${datafile}
144                 fi
145         else
146                 if [ ${istar} -eq 1 ] ; then
147                         tail -c +$((${tailskip}+1)) "${src}" 2>/dev/null \
148                                 | head -c $((${metaskip}-${tailskip})) \
149                                 | tar --no-same-owner -xf -
150                 else
151                         tail -c +$((${tailskip}+1)) "${src}" 2>/dev/null \
152                                 | head -c $((${metaskip}-${tailskip})) \
153                                 > ${datafile}
154                 fi
155         fi
156         true
157         #[ -s "${datafile}" ] || die "failure unpacking pdv ('${metaskip}' '${tailskip}' '${datafile}')"
158         #assert "failure unpacking pdv ('${metaskip}' '${tailskip}' '${datafile}')"
159 }
160
161 # @FUNCTION: unpack_makeself
162 # @USAGE: [file to unpack] [offset] [tail|dd]
163 # @DESCRIPTION:
164 # Unpack those pesky makeself generated files ...
165 # They're shell scripts with the binary package tagged onto
166 # the end of the archive.  Loki utilized the format as does
167 # many other game companies.
168 #
169 # If the file is not specified, then ${A} is used.  If the
170 # offset is not specified then we will attempt to extract
171 # the proper offset from the script itself.
172 unpack_makeself() {
173         local src_input=${1:-${A}}
174         local src=$(find_unpackable_file "${src_input}")
175         local skip=$2
176         local exe=$3
177
178         [[ -z ${src} ]] && die "Could not locate source for '${src_input}'"
179
180         unpack_banner "${src}"
181
182         if [[ -z ${skip} ]] ; then
183                 local ver=$(grep -m1 -a '#.*Makeself' "${src}" | awk '{print $NF}')
184                 local skip=0
185                 exe=tail
186                 case ${ver} in
187                         1.5.*|1.6.0-nv*)        # tested 1.5.{3,4,5} ... guessing 1.5.x series is same
188                                 skip=$(grep -a ^skip= "${src}" | cut -d= -f2)
189                                 ;;
190                         2.0|2.0.1)
191                                 skip=$(grep -a ^$'\t'tail "${src}" | awk '{print $2}' | cut -b2-)
192                                 ;;
193                         2.1.1)
194                                 skip=$(grep -a ^offset= "${src}" | awk '{print $2}' | cut -b2-)
195                                 (( skip++ ))
196                                 ;;
197                         2.1.2)
198                                 skip=$(grep -a ^offset= "${src}" | awk '{print $3}' | head -n 1)
199                                 (( skip++ ))
200                                 ;;
201                         2.1.3)
202                                 skip=`grep -a ^offset= "${src}" | awk '{print $3}'`
203                                 (( skip++ ))
204                                 ;;
205                         2.1.4|2.1.5|2.1.6|2.2.0|2.4.0)
206                                 skip=$(grep -a offset=.*head.*wc "${src}" | awk '{print $3}' | head -n 1)
207                                 skip=$(head -n ${skip} "${src}" | wc -c)
208                                 exe="dd"
209                                 ;;
210                         *)
211                                 eerror "I'm sorry, but I was unable to support the Makeself file."
212                                 eerror "The version I detected was '${ver}'."
213                                 eerror "Please file a bug about the file ${src##*/} at"
214                                 eerror "https://bugs.gentoo.org/ so that support can be added."
215                                 die "makeself version '${ver}' not supported"
216                                 ;;
217                 esac
218                 debug-print "Detected Makeself version ${ver} ... using ${skip} as offset"
219         fi
220         case ${exe} in
221                 tail)   exe=( tail -n +${skip} "${src}" );;
222                 dd)             exe=( dd ibs=${skip} skip=1 if="${src}" );;
223                 *)              die "makeself cant handle exe '${exe}'"
224         esac
225
226         # lets grab the first few bytes of the file to figure out what kind of archive it is
227         local filetype tmpfile="${T}/${FUNCNAME}"
228         "${exe[@]}" 2>/dev/null | head -c 512 > "${tmpfile}"
229         filetype=$(file -b "${tmpfile}") || die
230         case ${filetype} in
231                 *tar\ archive*)
232                         "${exe[@]}" | tar --no-same-owner -xf -
233                         ;;
234                 bzip2*)
235                         "${exe[@]}" | bzip2 -dc | tar --no-same-owner -xf -
236                         ;;
237                 gzip*)
238                         "${exe[@]}" | tar --no-same-owner -xzf -
239                         ;;
240                 compress*)
241                         "${exe[@]}" | gunzip | tar --no-same-owner -xf -
242                         ;;
243                 XZ*)
244                         "${exe[@]}" | unxz | tar --no-same-owner -xf -
245                         ;;
246                 *)
247                         eerror "Unknown filetype \"${filetype}\" ?"
248                         false
249                         ;;
250         esac
251         assert "failure unpacking (${filetype}) makeself ${src##*/} ('${ver}' +${skip})"
252 }
253
254 # @FUNCTION: unpack_deb
255 # @USAGE: <one deb to unpack>
256 # @DESCRIPTION:
257 # Unpack a Debian .deb archive in style.
258 unpack_deb() {
259         [[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>"
260
261         local deb=$(find_unpackable_file "$1")
262
263         unpack_banner "${deb}"
264
265         # on AIX ar doesn't work out as their ar used a different format
266         # from what GNU ar (and thus what .deb files) produce
267         if [[ -n ${EPREFIX} ]] ; then
268                 {
269                         read # global header
270                         [[ ${REPLY} = "!<arch>" ]] || die "${deb} does not seem to be a deb archive"
271                         local f timestamp uid gid mode size magic
272                         while read f timestamp uid gid mode size magic ; do
273                                 [[ -n ${f} && -n ${size} ]] || continue # ignore empty lines
274                                 if [[ ${f} = "data.tar"* ]] ; then
275                                         head -c "${size}" > "${f}"
276                                 else
277                                         head -c "${size}" > /dev/null # trash it
278                                 fi
279                         done
280                 } < "${deb}"
281         else
282                 ar x "${deb}"
283         fi
284
285         unpacker ./data.tar*
286
287         # Clean things up #458658.  No one seems to actually care about
288         # these, so wait until someone requests to do something else ...
289         rm -f debian-binary {control,data}.tar*
290 }
291
292 # @FUNCTION: unpack_cpio
293 # @USAGE: <one cpio to unpack>
294 # @DESCRIPTION:
295 # Unpack a cpio archive, file "-" means stdin.
296 unpack_cpio() {
297         [[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>"
298
299         # needed as cpio always reads from stdin
300         local cpio_cmd=( cpio --make-directories --extract --preserve-modification-time )
301         if [[ $1 == "-" ]] ; then
302                 unpack_banner "stdin"
303                 "${cpio_cmd[@]}"
304         else
305                 local cpio=$(find_unpackable_file "$1")
306                 unpack_banner "${cpio}"
307                 "${cpio_cmd[@]}" <"${cpio}"
308         fi
309 }
310
311 # @FUNCTION: unpack_zip
312 # @USAGE: <zip file>
313 # @DESCRIPTION:
314 # Unpack zip archives.
315 # This function ignores all non-fatal errors (i.e. warnings).
316 # That is useful for zip archives with extra crap attached
317 # (e.g. self-extracting archives).
318 unpack_zip() {
319         [[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>"
320
321         local zip=$(find_unpackable_file "$1")
322         unpack_banner "${zip}"
323         unzip -qo "${zip}"
324
325         [[ $? -le 1 ]] || die "unpacking ${zip} failed (arch=unpack_zip)"
326 }
327
328 # @FUNCTION: _unpacker
329 # @USAGE: <one archive to unpack>
330 # @INTERNAL
331 # @DESCRIPTION:
332 # Unpack the specified archive.  We only operate on one archive here
333 # to keep down on the looping logic (that is handled by `unpacker`).
334 _unpacker() {
335         [[ $# -eq 1 ]] || die "Usage: ${FUNCNAME} <file>"
336
337         local a=$1
338         local m=$(echo "${a}" | tr '[:upper:]' '[:lower:]')
339         a=$(find_unpackable_file "${a}")
340
341         # first figure out the decompression method
342         local comp=""
343         case ${m} in
344         *.bz2|*.tbz|*.tbz2)
345                 local bzcmd=${PORTAGE_BZIP2_COMMAND:-$(type -P pbzip2 || type -P bzip2)}
346                 local bzuncmd=${PORTAGE_BUNZIP2_COMMAND:-${bzcmd} -d}
347                 : ${UNPACKER_BZ2:=${bzuncmd}}
348                 comp="${UNPACKER_BZ2} -c"
349                 ;;
350         *.z|*.gz|*.tgz)
351                 comp="gzip -dc" ;;
352         *.lzma|*.xz|*.txz)
353                 comp="xz -dc" ;;
354         *.lz)
355                 : ${UNPACKER_LZIP:=$(type -P plzip || type -P pdlzip || type -P lzip)}
356                 comp="${UNPACKER_LZIP} -dc" ;;
357         esac
358
359         # then figure out if there are any archiving aspects
360         local arch=""
361         case ${m} in
362         *.tgz|*.tbz|*.tbz2|*.txz|*.tar.*|*.tar)
363                 arch="tar --no-same-owner -xof" ;;
364         *.cpio.*|*.cpio)
365                 arch="unpack_cpio" ;;
366         *.deb)
367                 arch="unpack_deb" ;;
368         *.run)
369                 arch="unpack_makeself" ;;
370         *.sh)
371                 # Not all shell scripts are makeself
372                 if head -n 30 "${a}" | grep -qs '#.*Makeself' ; then
373                         arch="unpack_makeself"
374                 fi
375                 ;;
376         *.bin)
377                 # Makeself archives can be annoyingly named
378                 if head -c 100 "${a}" | grep -qs '#.*Makeself' ; then
379                         arch="unpack_makeself"
380                 fi
381                 ;;
382         *.zip)
383                 arch="unpack_zip" ;;
384         esac
385
386         # finally do the unpack
387         if [[ -z ${arch}${comp} ]] ; then
388                 unpack "$1"
389                 return $?
390         fi
391
392         [[ ${arch} != unpack_* ]] && unpack_banner "${a}"
393
394         if [[ -z ${arch} ]] ; then
395                 # Need to decompress the file into $PWD #408801
396                 local _a=${a%.*}
397                 ${comp} "${a}" > "${_a##*/}"
398         elif [[ -z ${comp} ]] ; then
399                 ${arch} "${a}"
400         else
401                 ${comp} "${a}" | ${arch} -
402         fi
403
404         assert "unpacking ${a} failed (comp=${comp} arch=${arch})"
405 }
406
407 # @FUNCTION: unpacker
408 # @USAGE: [archives to unpack]
409 # @DESCRIPTION:
410 # This works in the same way that `unpack` does.  If you don't specify
411 # any files, it will default to ${A}.
412 unpacker() {
413         local a
414         [[ $# -eq 0 ]] && set -- ${A}
415         for a ; do _unpacker "${a}" ; done
416 }
417
418 # @FUNCTION: unpacker_src_unpack
419 # @DESCRIPTION:
420 # Run `unpacker` to unpack all our stuff.
421 unpacker_src_unpack() {
422         unpacker
423 }
424
425 # @FUNCTION: unpacker_src_uri_depends
426 # @USAGE: [archives that we will unpack]
427 # @RETURN: Dependencies needed to unpack all the archives
428 # @DESCRIPTION:
429 # Walk all the specified files (defaults to $SRC_URI) and figure out the
430 # dependencies that are needed to unpack things.
431 #
432 # Note: USE flags are not yet handled.
433 unpacker_src_uri_depends() {
434         local uri deps d
435
436         if [[ $# -eq 0 ]] ; then
437                 # Disable path expansion for USE conditionals. #654960
438                 set -f
439                 set -- ${SRC_URI}
440                 set +f
441         fi
442
443         for uri in "$@" ; do
444                 case ${uri} in
445                 *.cpio.*|*.cpio)
446                         d="app-arch/cpio" ;;
447                 *.deb)
448                         # platforms like AIX don't have a good ar
449                         d="kernel_AIX? ( app-arch/deb2targz )" ;;
450                 *.rar|*.RAR)
451                         d="app-arch/unrar" ;;
452                 *.7z)
453                         d="app-arch/p7zip" ;;
454                 *.xz)
455                         d="app-arch/xz-utils" ;;
456                 *.zip)
457                         d="app-arch/unzip" ;;
458                 *.lz)
459                         d="|| ( app-arch/plzip app-arch/pdlzip app-arch/lzip )" ;;
460                 esac
461                 deps+=" ${d}"
462         done
463
464         echo "${deps}"
465 }
466
467 EXPORT_FUNCTIONS src_unpack
468
469 fi