projects/aports·blob v2.22.0 · refs

2046 lines · 50.0 KiB

1#!/bin/sh
2
3# abuild - build apk packages (light version of makepkg)
4# Copyright (c) 2008 Natanael Copa <[email protected]>
5#
6# Distributed under GPL-2
7#
8
9program_version=@VERSION@
10sysconfdir=@sysconfdir@
11abuildrepo_base=@abuildrepo@
12datadir=@datadir@
13
14abuild_path=$(readlink -f $0)
15
16if ! [ -f "$datadir/functions.sh" ]; then
17 echo "$datadir/functions.sh: not found" >&2
18 exit 1
19fi
20. "$datadir/functions.sh"
21
22# defaults
23BUILD_BASE="build-base"
24
25: ${FAKEROOT:="fakeroot"}
26: ${SUDO_APK:="abuild-apk"}
27: ${APK:="apk"}
28: ${ADDUSER:="abuild-adduser"}
29: ${ADDGROUP:="abuild-addgroup"}
30
31apk_opt_wait="--wait 30"
32
33# run optional log command for remote logging
34logcmd() {
35 ${ABUILD_LOG_CMD:-true} "$@"
36 return 0
37}
38
39# we override the default msg, warning and error as we want the pkgname
40msg() {
41 [ -n "$quiet" ] && return 0
42 local prompt="$GREEN>>>${NORMAL}"
43 local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
44 local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
45 printf "${prompt} ${name}${fake}: %s\n" "$1" >&2
46}
47
48warning() {
49 local prompt="${YELLOW}>>> WARNING:${NORMAL}"
50 local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
51 local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
52 printf "${prompt} ${name}${fake}: %s\n" "$1" >&2
53}
54
55error() {
56 local prompt="${RED}>>> ERROR:${NORMAL}"
57 local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
58 local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
59 printf "${prompt} ${name}${fake}: %s\n" "$1" >&2
60 logcmd "ERROR: $pkgname: $1"
61}
62
63cross_compiling() {
64 test "$CBUILD" != "$CHOST" -a -n "$CBUILDROOT"
65}
66
67cleanup() {
68 local i=
69 set_xterm_title ""
70 if [ -n "$CLEANUP_FILES" ]; then
71 rm -f $CLEANUP_FILES
72 fi
73 for i; do
74 case $i in
75 pkgdir) msg "Cleaning up pkgdir"; rm -rf "$pkgbasedir";;
76 srcdir) msg "Cleaning up srcdir"; rm -rf "$srcdir";;
77 deps)
78 if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
79 msg "Uninstalling dependencies..."
80 $SUDO_APK del --quiet $apk_opt_wait $uninstall_after
81 if cross_compiling; then
82 $SUDO_APK del --root "$CBUILDROOT" \
83 --quiet $apk_opt_wait \
84 $uninstall_after
85 fi
86 fi
87 ;;
88 esac
89 done
90}
91
92die() {
93 error "$@"
94 cleanup $ERROR_CLEANUP
95 exit 1
96}
97
98spell_error() {
99 die "APKBUILD contains '$1'. It should be '$2'"
100}
101
102# check if apkbuild is basicly sane
103default_sanitycheck() {
104 local i= j= suggestion=
105 msg "Checking sanity of $APKBUILD..."
106 [ -z "$pkgname" ] && die "Missing pkgname in APKBUILD"
107 [ -z "${pkgname##* *}" ] && die "pkgname contains spaces"
108 [ -z "$pkgver" ] && die "Missing pkgver in APKBUILD"
109 if [ "$pkgver" != "volatile" ] && [ -z "$nodeps" ]; then
110 $APK version --check --quiet "$pkgver" ||\
111 die "$pkgver is not a valid version"
112 fi
113 [ -z "$pkgrel" ] && die "Missing pkgrel in APKBUILD"
114 [ -z "$pkgdesc" ] && die "Missing pkgdesc in APKBUILD"
115 [ -z "$url" ] && die "Missing url in APKBUILD"
116 [ -z "$license" ] && die "Missing license in APKBULID"
117 if [ $(echo "$pkgdesc" | wc -c) -gt 128 ]; then
118 die "pkgdesc is too long"
119 fi
120 is_function package || warning "Missing package() function in APKBUILD"
121
122 if [ -n "$replaces_priority" ] \
123 && ! echo $replaces_priority | egrep -q '^[0-9]+$'; then
124 die "replaces_priority must be a number"
125 fi
126 # check so no package names starts with -
127 for i in $pkgname $subpackages; do
128 case $i in
129 -*) die "${i%:*} is not a valid package name";;
130 esac
131 done
132
133 # check if CARCH, CBUILD, CHOST and CTARGET is set
134 [ -z "$CHOST" ] && die "Please set CHOST in /etc/abuild.conf"
135
136 if [ -z "$CARCH" ]; then
137 die "Please fix CHOST, or set CARCH in abuild.conf"
138 fi
139
140 for i in $install; do
141 local n=${i%.*}
142 local suff=${i##*.}
143 case "$suff" in
144 pre-install|post-install|pre-upgrade|post-upgrade|pre-deinstall|post-deinstall);;
145 *) die "$i: unknown install script suffix"
146 esac
147 if ! subpackages_has "$n" && [ "$n" != "$pkgname" ]; then
148 die "$i: install script does not match pkgname or any subpackage"
149 fi
150 [ -e "$startdir/$i" ] || die "install script $i is missing"
151 for j in chown chmod chgrp; do
152 if grep -q $j "$startdir"/$i; then
153 warning "$i: found $j"
154 warning2 "Permissions should be fixed in APKBUILD package()"
155 fi
156 done
157 done
158
159 for i in $triggers; do
160 local f=${i%=*}
161 local p=${f%.trigger}
162 [ "$f" = "$i" ] && die "$f: triggers must contain '='"
163 [ "$p" = "$f" ] && die "$f: triggers scripts must have .trigger suffix"
164 if ! subpackages_has "$p" && [ "$p" != "$pkgname" ]; then
165 die "$p: trigger script does not match pkgname or any subpackage"
166 fi
167
168 [ -e "$startdir"/$f ] || die "trigger script $f is missing"
169 done
170 if [ -n "$source" ]; then
171 for i in $source; do
172 if install_has "$i"; then
173 warning "You should not have \$install in source"
174 continue
175 fi
176 case "$i" in
177 *::*) i=${i%%::*};;
178 https://*) makedepends_has wget && warning "wget no longer need to be in makedepends when source has https://" ;;
179 esac
180 list_has ${i##*/} $md5sums $sha256sums $sha512sums \
181 || die "${i##*/} is missing in checksums"
182
183 # verify that our source does not have git tag version
184 # name as tarball (typicallly github)
185 if is_remote "$i" && [ "${i#*::}" = "$i" ]; then
186 case ${i##*/} in
187 v$pkgver.tar.*|$pkgver.tar.*)
188 die "source ${i##*/} needs to be renamed to avoid possible collisions"
189 ;;
190 esac
191 fi
192 done
193 fi
194
195 # verify that things listed in checksum also is listed in source
196 local algo=
197 for algo in md5 sha256 sha512; do
198 eval set -- \$${algo}sums
199 while [ $# -gt 1 ]; do
200 local file="$2"
201 shift 2
202 source_has $file || die "$file exists in ${algo}sums but is missing in source"
203 done
204 done
205
206 # common spelling errors
207 [ -n "$depend" ] && spell_error depend depends
208 [ -n "$makedepend" ] && spell_error makedepend makedepends
209 [ -n "$pkguser" ] && spell_error pkguser pkgusers
210 [ -n "$pkggroup" ] && spell_error pkggroup pkggroups
211 [ -n "$subpackage" ] && spell_error subpackage subpackages
212
213 grep '^# Maintainer:' $APKBUILD >/dev/null || warning "No maintainer"
214
215 makedepends_has 'g++' && warning "g++ should not be in makedepends"
216 return 0
217}
218
219sanitycheck() {
220 default_sanitycheck
221}
222
223sumcheck() {
224 local algo="$1" sums="$2"
225 local dummy f endreturnval originalparams origin file
226
227 # get number of checksums
228 set -- $sums
229 local numsums=$(( $# / 2 ))
230
231 set -- $source
232 if [ $# -ne $numsums ]; then
233 die "Number of ${algo}sums($numsums) does not correspond to number of sources($#)"
234 fi
235 fetch || return 1
236 msg "Checking ${algo}sums..."
237 cd "$srcdir" || return 1
238 IFS=$'\n'
239 endreturnval=0
240 for src in $sums; do
241 origin=$1; shift
242 echo "$src" | ${algo}sum -c
243 if [ $? -ne 0 ]; then
244 endreturnval=1
245 is_remote $origin || continue
246 echo "Because the remote file above failed the ${algo}sum check it will be deleted."
247 echo "Rebuilding will cause it to re-download which in some cases may fix the problem."
248 file=`echo "$src" | sed 's/.*[ \t\n]\(.*\)/\1/'`
249 echo "Deleting: $file"
250 rm $file
251 fi
252 done
253 unset IFS
254 return $endreturnval
255}
256
257# for compatibility
258md5check() {
259 warning "'md5check' is deprecated. Use 'verify' instead"
260 sumcheck md5 "$md5sums"
261}
262
263# verify checksums
264verify() {
265 local verified=false algo=
266 for algo in sha512 sha256 sha1 md5; do
267 local sums=
268 eval sums=\"\$${algo}sums\"
269 if [ -z "$sums" ] || [ -z "$source" ]; then
270 continue
271 fi
272 sumcheck "$algo" "$sums" || return 1
273 verified=true
274 break
275 done
276 if [ -n "$source" ] && ! $verified; then
277 die "Use 'abuild checksum' to generate/update the checksum(s)"
278 fi
279 return 0
280}
281
282# verify upstream sources
283sourcecheck() {
284 local uri
285 for uri in $source; do
286 is_remote $uri || continue
287 case "$uri" in
288 saveas-*://*)
289 uri=${uri#saveas-}
290 uri=${uri%/*}
291 ;;
292 *::*)
293 uri=${uri##*::}
294 ;;
295 esac
296 wget -q -s "$uri" || return 1
297 done
298 return 0
299}
300
301# convert curl options to wget options and call wget instead of curl
302wget_fallback() {
303 local wget_opts= outfile= opt=
304 while getopts "C:Lko:s" opt; do
305 case $opt in
306 'L') ;; # --location. wget does this by default
307 'f') ;; # --fail. wget does this by default
308 'C') wget_opts="$wget_opts -c";; # --continue-at
309 's') wget_opts="$wget_opts -q";; # --silent
310 'o') wget_opts="$wget_opts -O $OPTARG";; # --output
311 'k') wget_opts="$wget_opts --no-check-certificate";; #gnu wget
312 esac
313 done
314 shift $(( $OPTIND - 1 ))
315 wget $wget_opts "$1"
316}
317
318uri_fetch() {
319 local uri="$1"
320 local d="${uri##*/}" # $(basename $uri)
321 local opts
322 [ -n "$quiet" ] && opts="-s"
323
324 local lockfile="$SRCDEST/$d".lock
325
326 # fix saveas-*://* URIs
327 case "$uri" in
328 # remove 'saveas-' from beginning and
329 # '/filename' from end of URI
330 saveas-*://*) uri="${uri:7:$(expr ${#uri} - 7 - ${#d} - 1)}";;
331
332 *::*)
333 d=${uri%%::*}
334 uri=${uri#$d::}
335 ;;
336 esac
337
338 case "$uri" in
339 https://*) opts="-k";;
340 esac
341
342 mkdir -p "$SRCDEST"
343
344 CLEANUP_FILES="$CLEANUP_FILES $lockfile"
345 (
346 flock -n -x 9 || msg "Waiting for ${lockfile##*/}..."
347 flock -x 9
348
349 [ -f "$SRCDEST/$d" ] && exit 0 # use exit since its a subshell
350
351 if [ -f "$SRCDEST/$d.part" ]; then
352 msg "Partial download found. Trying to resume"
353 opts="$opts -C -"
354 fi
355 msg "Fetching $uri"
356
357 # fallback to wget if curl is missing. useful for bootstrapping
358 local fetcher=
359 if ! [ -x "$(which curl)" ]; then
360 fetcher=wget_fallback
361 else
362 fetcher=curl
363 opts="$opts -L -f -k"
364 fi
365
366 $fetcher $opts -o "$SRCDEST/$d.part" "$uri" \
367 && mv "$SRCDEST/$d.part" "$SRCDEST/$d"
368
369 ) 9>$lockfile
370
371 local rc=$?
372 rm -f "$lockfile"
373 return $rc
374}
375
376is_remote() {
377 case "${1#*::}" in
378 http://*|ftp://*|https://*|saveas-*://*)
379 return 0;;
380 esac
381 return 1
382}
383
384filename_from_uri() {
385 local uri="$1"
386 local filename="${uri##*/}" # $(basename $uri)
387 case "$uri" in
388 *::*) filename=${uri%%::*};;
389 esac
390 echo "$filename"
391}
392
393# try download from file from mirror first
394uri_fetch_mirror() {
395 local uri="$1"
396 if [ -n "$DISTFILES_MIRROR" ]; then
397 if is_remote "$DISTFILES_MIRROR"; then
398 uri_fetch "$DISTFILES_MIRROR"/$(filename_from_uri $uri)\
399 && return 0
400 else
401 cp "$DISTFILES_MIRROR"/$(filename_from_uri $uri) \
402 "$SRCDEST" && return 0
403 fi
404 fi
405 uri_fetch "$uri"
406}
407
408default_fetch() {
409 local s
410 mkdir -p "$srcdir"
411 for s in $source; do
412 if is_remote "$s"; then
413 uri_fetch_mirror "$s" || return 1
414 ln -sf "$SRCDEST/$(filename_from_uri $s)" "$srcdir"/
415 else
416 ln -sf "$startdir/$s" "$srcdir/"
417 fi
418 done
419}
420
421fetch() {
422 default_fetch
423}
424
425# verify that all init.d scripts are openrc runscripts
426initdcheck() {
427 local i line
428 for i in $source; do
429 case $i in
430 *.initd) line=$(head -n 1 "$srcdir"/$i);;
431 *) continue ;;
432 esac
433
434 case "$line" in
435 *sbin/openrc-run)
436 ;;
437 *sbin/runscript)
438 warning "$i is not an openrc #!/sbin/openrc-run"
439 ;;
440 *) error "$i is not an openrc #!/sbin/openrc-run"
441 return 1
442 ;;
443 esac
444 done
445}
446
447# unpack the sources
448default_unpack() {
449 local u
450 if [ -z "$force" ]; then
451 verify || return 1
452 initdcheck || return 1
453 fi
454 mkdir -p "$srcdir"
455 for u in $source; do
456 local s="$SRCDEST/$(filename_from_uri $u)"
457 case "$s" in
458 *.tar)
459 msg "Unpacking $s..."
460 tar -C "$srcdir" -xf "$s" || return 1;;
461 *.tar.gz|*.tgz)
462 msg "Unpacking $s..."
463 tar -C "$srcdir" -zxf "$s" || return 1;;
464 *.tar.bz2)
465 msg "Unpacking $s..."
466 tar -C "$srcdir" -jxf "$s" || return 1;;
467 *.tar.lzma)
468 msg "Unpacking $s..."
469 unlzma -c "$s" | tar -C "$srcdir" -x \
470 || return 1;;
471 *.tar.xz)
472 msg "Unpacking $s..."
473 unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
474 *.zip)
475 msg "Unpacking $s..."
476 unzip "$s" -d "$srcdir" || return 1;;
477 esac
478 done
479}
480
481unpack() {
482 default_unpack
483}
484
485# cleanup source and package dir
486clean() {
487 msg "Cleaning temporary build dirs..."
488 rm -rf "$srcdir"
489 rm -rf "$pkgbasedir"
490}
491
492# cleanup fetched sources
493cleancache() {
494 local s
495 for s in $source; do
496 if is_remote "$s"; then
497 s=$(filename_from_uri $s)
498 msg "Cleaning downloaded $s ..."
499 rm -f "$SRCDEST/$s"
500 fi
501 done
502}
503
504listpkgnames() {
505 local i
506 for i in $pkgname $subpackages; do
507 echo ${i%:*}
508 done
509 for i in $linguas; do
510 echo $pkgname-lang-$i
511 done
512}
513
514cleanpkg() {
515 local i
516 getpkgver || return 1
517 msg "Cleaning built packages..."
518 for i in $(listpkgnames); do
519 local p="${i%:*}-$pkgver-r$pkgrel"
520 rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
521 "$abuildrepo"/$p.apk "$abuildrepo"/*/$p.apk
522 done
523 # remove given packages from index
524 update_abuildrepo_index
525}
526
527# clean all packages except current
528cleanoldpkg() {
529 local i j
530 getpkgver || return 1
531 msg "Cleaning all packages except $pkgver-r$pkgrel..."
532 for i in $(listpkgnames); do
533 local pn=${i%:*}
534 for j in "$PKGDEST"/$pn-[0-9]*.apk ; do
535 [ "$j" = "$PKGDEST/$pn-$pkgver-r$pkgrel.apk" ] \
536 && continue
537 rm -f "$j" "$abuildrepo"/*/${j##*/}
538 done
539 done
540 update_abuildrepo_index
541 return 0
542}
543
544mkusers() {
545 local i
546 for i in $pkggroups; do
547 if ! getent group $i >/dev/null; then
548 msg "Creating group $i"
549 $ADDGROUP -S $i || return 1
550 fi
551 done
552 for i in $pkgusers; do
553 if ! getent passwd $i >/dev/null; then
554 local gopt=
555 msg "Creating user $i"
556 if getent group $i >/dev/null; then
557 gopt="-G $i"
558 fi
559 $ADDUSER -S -D -H $gopt $i || return 1
560 fi
561 done
562}
563
564# helper to update config.sub to a recent version
565update_config_sub() {
566 local changed=false
567 find . -name config.sub | while read f; do
568 if ! ./$f armv6-alpine-linux-muslgnueabihf 2>/dev/null; then
569 msg "Updating $f"
570 cp "$datadir"/${f##*/} "$f" || return 1
571 changed=true
572 else
573 msg "No update needed for $f"
574 fi
575 # pipe subshell will return status of last command
576 $changed
577 done
578 return $?
579}
580
581runpart() {
582 local part=$1
583 [ -n "$DEBUG" ] && msg "$part"
584 $part || die "$part failed"
585}
586
587# override those in your build script
588getpkgver() {
589 # this func is supposed to be overridden by volatile packages
590 if [ "$pkgver" = "volatile" ]; then
591 error "Please provide a getpkgver() function in your APKBUILD"
592 return 1
593 fi
594}
595
596prepare() {
597 :
598}
599
600build() {
601 :
602}
603
604# generate a simple tar.gz package of pkgdir
605targz() {
606 cd "$pkgdir" || return 1
607 mkdir -p "$PKGDEST"
608 tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
609}
610
611get_split_func() {
612 # get the 'func' from "sub-pkg:func"
613 local func=${1##*:}
614
615 # get 'func' from "sub-pkg-func" if there was no :func
616 [ "$func" = "$1" ] && func=${func##*-}
617 echo $func
618}
619
620postcheck() {
621 local dir="$1" name="$2" i=
622 msg "Running postcheck for $name"
623 # checking for FHS compat
624 if ! options_has "!fhs"; then
625 for i in "$dir"/srv/* "$dir"/usr/local/* "$dir"/opt/*; do
626 if [ -e "$i" ]; then
627 error "Packages must not put anything under /srv, /usr/local or /opt"
628 return 1
629 fi
630 done
631 if [ -d "$dir"/usr/var ]; then
632 error "Found /usr/var, localstatedir is most likely wrong"
633 return 1
634 fi
635 fi
636
637 # remove *.la files if libtool is not set
638 if ! options_has "libtool"; then
639 find "$dir" -name '*.la' -type f -delete
640 fi
641
642 # look for /usr/lib/charset.alias
643 if [ -e "$dir"/usr/lib/charset.alias ] \
644 && ! options_has "charset.alias"; then
645 error "Found /usr/lib/charset.alias"
646 return 1
647 fi
648 # check directory permissions
649 i=$(find "$dir" -type d -perm -777 | sed "s|^$dir|\t|")
650 if [ -n "$i" ]; then
651 warning "World writeable directories found:"
652 echo "$i"
653 fi
654 # check so we dont have any suid root binaries that are not PIE
655 i=$(find "$dir" -type f -perm +6000 \
656 | xargs scanelf --nobanner --etype ET_EXEC \
657 | sed "s|ET_EXEC $dir|\t|")
658 if [ -n "$i" ]; then
659 error "Found non-PIE files that has SUID:"
660 echo "$i"
661 return 1
662 fi
663 # test suid bit on executable
664 if ! options_has "suid"; then
665 i=$(find "$dir" \( -perm -u+s -o -perm -g+s \) -a -type f \
666 -a -perm -o+x)
667 if [ -n "$i" ]; then
668 error "Found executable files with SUID bit set:"
669 echo "$i"
670 return 1
671 fi
672 fi
673
674 # test for textrels
675 if ! options_has "textrels"; then
676 local res="$(scanelf --recursive --textrel --quiet "$dir")"
677 if [ -n "$res" ]; then
678 error "Found textrels:"
679 echo "$res"
680 return 1
681 fi
682 fi
683 return 0
684}
685
686prepare_subpackages() {
687 local i
688 cd "$startdir"
689 for i in $subpackages; do
690 local func=$(get_split_func $i)
691 # call abuild recursively, setting subpkg{dir,name}
692 msg "Running split function $func..."
693 local dir="$pkgbasedir/${i%:*}" name="${i%:*}"
694 ( subpkgdir="$dir" subpkgname="$name" \
695 $0 $func prepare_package \
696 && postcheck "$dir" "$name" ) || return 1
697 done
698 postcheck "$pkgdir" "$pkgname" || return 1
699 # post check for /usr/share/locale
700 if [ -d "$pkgdir"/usr/share/locale ]; then
701 warning "Found /usr/share/locale"
702 warning2 "Maybe add \$pkgname-lang to subpackages?"
703 fi
704}
705
706default_lang() {
707 pkgdesc="Languages for package $pkgname"
708 install_if="$pkgname=$pkgver-r$pkgrel lang"
709 arch="noarch"
710 local dir
711 for dir in ${langdir:-/usr/share/locale}; do
712 mkdir -p "$subpkgdir"/${dir%/*}
713 mv "$pkgdir"/"$dir" "$subpkgdir"/"$dir" || return 1
714 done
715}
716
717lang() {
718 default_lang
719}
720
721default_lang_subpkg() {
722 if [ -z "$lang" ]; then
723 error "lang is not set"
724 return 1
725 fi
726 pkgdesc="$pkgname language pack for $lang"
727 arch="noarch"
728 install_if="$pkgname=$pkgver-r$pkgrel lang-$lang"
729 local dir
730 for dir in ${langdir:-/usr/share/locale}; do
731 mkdir -p "$subpkgdir"/$dir
732 mv "$pkgdir"/$dir/$lang* \
733 "$subpkgdir"/$dir/ \
734 || return 1
735 done
736}
737
738lang_subpkg() {
739 default_lang_subpkg
740}
741
742prepare_language_packs() {
743 for lang in $linguas; do
744 lang="$lang" \
745 subpkgname="$pkgname-lang-$lang" \
746 subpkgdir="$pkgbasedir"/$subpkgname \
747 $0 lang_subpkg prepare_package || return 1
748 done
749}
750
751# echo '-dirty' if git is not clean
752git_dirty() {
753 if [ $(git status -s "$startdir" | wc -l) -ne 0 ]; then
754 echo "-dirty"
755 fi
756}
757
758# echo last commit hash id
759git_last_commit() {
760 git log --format=oneline -n 1 "$startdir" | awk '{print $1}'
761}
762
763get_maintainer() {
764 if [ -z "$maintainer" ]; then
765 maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
766 fi
767}
768
769prepare_metafiles() {
770 getpkgver || return 1
771 local name=${subpkgname:-$pkgname}
772 [ -z "${name##* *}" ] && die "package name contains spaces"
773 local dir=${subpkgdir:-$pkgdir}
774 local pkg="$name-$pkgver-r$pkgrel.apk"
775 local pkginfo="$controldir"/.PKGINFO
776 local sub
777
778 [ ! -d "$dir" ] && die "Missing $dir"
779 cd "$dir"
780 mkdir -p "$controldir"
781 local builddate=$(date -u "+%s")
782 local size=$(du -sk | awk '{print $1 * 1024}')
783 local parch="$CARCH"
784
785 # we need to wait with setting noarch til our build infra can handle it
786# if [ "$arch" = "noarch" ]; then
787# parch="noarch"
788# fi
789
790 echo "# Generated by $(basename $0) $program_version" >"$pkginfo"
791 if [ -n "$FAKEROOTKEY" ]; then
792 echo "# using $($FAKEROOT -v)" >> "$pkginfo"
793 fi
794 echo "# $(date -u)" >> "$pkginfo"
795 cat >> "$pkginfo" <<EOF
796pkgname = $name
797pkgver = $pkgver-r$pkgrel
798pkgdesc = $pkgdesc
799url = $url
800builddate = $builddate
801packager = ${PACKAGER:-"Unknown"}
802size = $size
803arch = $parch
804origin = $pkgname
805EOF
806 local i deps
807 deps="$depends"
808 if [ "$pkgname" != "busybox" ] && ! depends_has busybox; then
809 for i in $install $triggers; do
810 local s=${i%=*}
811 [ "$name" != "${s%.*}" ] && continue
812 if head -n 1 "$startdir/$s" | grep '^#!/bin/sh' >/dev/null ; then
813 msg "Script found. busybox added as a dependency for $pkg"
814 deps="$deps busybox"
815 break
816 fi
817 done
818 fi
819
820 # store last_commit in global var so we only call git once
821 if [ -z "$last_commit" ]; then
822 last_commit="$(git_last_commit)$(git_dirty)"
823 fi
824 echo "commit = $last_commit" >> "$pkginfo"
825
826 get_maintainer
827 if [ -n "$maintainer" ]; then
828 echo "maintainer = $maintainer" >> "$pkginfo"
829 fi
830
831 if [ -n "$replaces_priority" ]; then
832 echo "replaces_priority = $replaces_priority" >> "$pkginfo"
833 fi
834
835 echo "license = $license" >> "$pkginfo"
836 for i in $replaces; do
837 echo "replaces = $i" >> "$pkginfo"
838 done
839 for i in $deps; do
840 echo "depend = $i" >> "$pkginfo"
841 done
842 for i in $provides; do
843 echo "provides = $i" >> "$pkginfo"
844 done
845 for i in $triggers; do
846 local f=${i%=*}
847 local dirs=${i#*=}
848 [ "${f%.trigger}" != "$name" ] && continue
849 echo "triggers = ${dirs//:/ }" >> "$pkginfo"
850 done
851 if [ -n "$install_if" ]; then
852 echo "install_if = $(echo $install_if)" >> "$pkginfo"
853 fi
854
855 local metafiles=".PKGINFO"
856 for i in $install $triggers; do
857 local f=${i%=*}
858 local n=${f%.*}
859 if [ "$n" != "$name" ]; then
860 continue
861 fi
862 script=${f#$name}
863 msg "Adding $script"
864 cp "$startdir/$f" "$controldir/$script" || return 1
865 chmod +x "$controldir/$script"
866 metafiles="$metafiles $script"
867 done
868 echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
869}
870
871prepare_tracedeps() {
872 local dir=${subpkgdir:-$pkgdir}
873 local etype= soname= file= sover=
874 [ "$arch" = "noarch" ] && return 0
875 options_has "!tracedeps" && return 0
876 # lets tell all the places we should look for .so files - all rpaths
877 scanelf --quiet --recursive --rpath "$dir" \
878 | sed -e 's/[[:space:]].*//' -e 's/:/\n/' | sort -u \
879 >"$controldir"/.rpaths
880 if grep -q -x '/usr/lib' "$controldir"/.rpaths; then
881 warning "Redundant /usr/lib in rpath found"
882 fi
883 if grep '^/home/' "$controldir"/.rpaths; then
884 error "Has /home/... in rpath"
885 return 1
886 fi
887}
888
889# check if dir has arch specific binaries
890dir_has_arch_binaries() {
891 local dir="$1"
892 # if scanelf returns something, then we have binaries
893 [ -n "$(scanelf -R "$dir" | head -n 1)" ] && return 0
894
895 # look for static *.a
896 [ -n "$(find "$dir" -type f -name '*.a' | head -n 1)" ] && return 0
897
898 return 1
899}
900
901# returns true if this is the -dev package
902is_dev_pkg() {
903 test "${subpkgname%-dev}" != "$subpkgname"
904}
905
906# check that noarch is set if needed
907archcheck() {
908 options_has "!archcheck" && return 0
909 if dir_has_arch_binaries "${subpkgdir:-$pkgdir}"; then
910 [ "$arch" != "noarch" ] && return 0
911 error "Arch specific binaries found so arch must not be set to \"noarch\""
912 return 1
913 elif [ "$arch" != "noarch" ] && ! is_dev_pkg; then
914 # we dont want -dev package go to noarch
915 warning "No arch specific binaries found so arch should probably be set to \"noarch\""
916 fi
917 return 0
918}
919
920prepare_package() {
921 msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
922 stripbin
923 prepare_metafiles && prepare_tracedeps || return 1
924 archcheck
925}
926
927pkginfo_val() {
928 local key="$1"
929 local file="$2"
930 awk -F ' = ' "\$1 == \"$key\" {print \$2}" "$file"
931}
932
933# find real path to so files
934real_so_path() {
935 local so="$1"
936 shift
937 while [ $# -gt 0 ]; do
938 [ -e "$1"/$so ] && realpath "$1/$so" && return 0
939 shift
940 done
941 error "$so: path not found"
942 return 1
943}
944
945# search rpaths and /usr/lib /lib for given so files
946find_so_files() {
947 local rpaths=$(cat "$1")
948 shift
949 while [ $# -gt 0 ]; do
950 real_so_path "$1" /usr/lib /lib $rpaths || return 1
951 shift
952 done
953 return 0
954}
955
956subpkg_provides() {
957 grep -q -w "^$1" "$pkgbasedir"/.control.*/.provides-so 2>/dev/null
958}
959
960trace_apk_deps() {
961 local name="$1"
962 local dir="$2"
963 local i= found= autodeps= deppkgs= missing=
964 msg "Tracing dependencies..."
965 # add pkgconfig if usr/lib/pkgconfig is found
966 if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
967 && ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
968 msg " added pkgconfig (found /usr/lib/pkgconfig)"
969 autodeps="$autodeps pkgconfig"
970 fi
971
972 # special case for libpthread: we need depend on libgcc
973 if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
974 && ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
975 autodeps="$autodeps libgcc"
976 msg " added libgcc (due to libpthread)"
977 fi
978 [ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
979 # first check if its provided by same apkbuild
980 grep -q -w "^$i" "$dir"/.provides-so 2>/dev/null && continue
981
982 if subpkg_provides "$i" || cross_compiling \
983 || $APK info --quiet --installed "so:$i"; then
984 autodeps="$autodeps so:$i"
985 else
986 missing="$missing $i"
987 fi
988 done
989
990 # find all packages that holds the so files
991 if [ -f "$dir"/.rpaths ]; then
992 local so_files=$(find_so_files "$dir"/.rpaths $missing) \
993 || return 1
994 deppkgs=$($APK info --quiet --who-owns $so_files) || return 1
995 fi
996
997 for found in $deppkgs; do
998 if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
999 warning "You can remove '$found' from depends"
1000 continue
1001 fi
1002 autodeps="$autodeps $found"
1003 done
1004
1005 echo "# automatically detected:" >> "$dir"/.PKGINFO
1006 if [ -f "$dir"/.provides-so ]; then
1007 sed 's/^\(.*\) \([0-9].*\)/provides = so:\1=\2/' "$dir"/.provides-so \
1008 >> "$dir"/.PKGINFO
1009 fi
1010 [ -z "$autodeps" ] && return 0
1011 for i in $autodeps; do
1012 echo "depend = $i"
1013 done | sort -u >> "$dir"/.PKGINFO
1014 # display all depends
1015 sed -n '/^depend =/s/depend = /\t/p' "$dir"/.PKGINFO >&2
1016}
1017
1018find_scanelf_paths() {
1019 local datadir="$1"
1020 local paths="$datadir/lib:$datadir/usr/lib" i= rpaths=
1021 if [ -n "$ldpath" ]; then
1022 paths="$paths:$(echo "${datadir}${ldpath}" | sed "s|:|:$datadir|g")"
1023 fi
1024 # search in all rpaths
1025 for rpaths in "$pkgbasedir"/.control.*/.rpaths; do
1026 [ -f "$rpaths" ] || continue
1027 while read i; do
1028 local dir="${datadir}${i}"
1029 IFS=:
1030 if [ -d "$dir" ] && ! list_has "$dir" $paths; then
1031 paths="$paths:${dir}"
1032 fi
1033 unset IFS
1034 done < "$rpaths"
1035 done
1036 echo "$paths"
1037}
1038
1039scan_shared_objects() {
1040 local name="$1" controldir="$2" datadir="$3"
1041 local opt= i=
1042
1043 # allow spaces in paths
1044 IFS=:
1045 set -- $(find_scanelf_paths "$datadir")
1046 unset IFS
1047
1048 # sanity check, verify that each path is prefixed with datadir
1049 for i; do
1050 if [ "${i#$datadir}" = "$i" ]; then
1051 error "Internal error in scanelf paths"
1052 return 1
1053 fi
1054 done
1055
1056 if options_has "ldpath-recursive"; then
1057 opt="--recursive"
1058 fi
1059 msg "Scanning shared objects"
1060 # lets tell all the .so files this package provides in .provides-so
1061 scanelf --nobanner --soname $opt "$@" | while read etype soname file; do
1062 # if soname field is missing, soname will be the filepath
1063 sover=0
1064 if [ -z "$file" ]; then
1065 file="$soname"
1066 soname=${soname##*/}
1067 fi
1068
1069 # we only want shared libs
1070 case $soname in
1071 *.so|*.so.[0-9]*|*.c32);;
1072 *) continue;;
1073 esac
1074
1075 case "$file" in
1076 *.so.[0-9]*) sover=${file##*.so.};;
1077 *.so)
1078 # filter out sonames with version when file does not
1079 # have version
1080 case "$soname" in
1081 *.so.[0-9]*)
1082 if options_has "sover-namecheck"; then
1083 continue
1084 fi
1085 esac
1086 ;;
1087 esac
1088 list_has "$soname" $somask && continue
1089 echo "$soname $sover"
1090 # use awk to filter out dupes that has sover = 0
1091 done | awk '{ if (so[$1] == 0) so[$1] = $2; }
1092 END { for (i in so) print(i " " so[i]); }' \
1093 | sort -u > "$controldir"/.provides-so
1094
1095 # verify that we dont have any duplicates
1096 local dupes="$(cut -d' ' -f1 "$controldir"/.provides-so | uniq -d)"
1097 if [ -n "$dupes" ]; then
1098 die "provides multiple versions of same shared object: $dupes"
1099 fi
1100
1101 # now find the so dependencies
1102 scanelf --nobanner --recursive --needed "$datadir" | tr ' ' ':' \
1103 | awk -F ":" '$2 != "" && ($1 == "ET_DYN" || $1 == "ET_EXEC") {print $2}' \
1104 | sed 's:,:\n:g' | sort -u \
1105 | while read soname; do
1106 # only add files that are not self provided
1107 grep -q -w "^$soname" "$controldir"/.provides-so \
1108 || list_has "$soname" $somask \
1109 || echo $soname
1110 done > "$controldir"/.needs-so
1111}
1112
1113# read size in bytes from stdin and show as human readable
1114human_size() {
1115 awk '{ split("B KB MB GB TB PB", type)
1116 for(i=5; y < 1; i--)
1117 y = $1 / (2**(10*i))
1118 printf("%.1f %s\n", y, type[i+2]) }'
1119}
1120
1121create_apks() {
1122 local file= dir= name= ver= apk= datadir= size=
1123 getpkgver || return 1
1124 mkdir -p "$PKGDEST"
1125 if [ "$arch" != "noarch" ] && ! options_has "!tracedeps"; then
1126 for file in "$pkgbasedir"/.control.*/.PKGINFO; do
1127 dir="${file%/.PKGINFO}"
1128 name="$(pkginfo_val pkgname $file)"
1129 datadir="$pkgbasedir"/$name
1130 subpkgname=$name
1131 scan_shared_objects "$name" "$dir" "$datadir"
1132 done
1133 fi
1134 for file in "$pkgbasedir"/.control.*/.PKGINFO; do
1135 dir="${file%/.PKGINFO}"
1136 name=$(pkginfo_val pkgname $file)
1137 ver=$(pkginfo_val pkgver $file)
1138 size=$(pkginfo_val size $file | human_size)
1139 apk=$name-$ver.apk
1140 datadir="$pkgbasedir"/$name
1141 subpkgname=$name
1142
1143 trace_apk_deps "$name" "$dir" || return 1
1144 msg "Package size: ${size}"
1145 msg "Compressing data..."
1146 (
1147 cd "$datadir"
1148 # data.tar.gz
1149 set -- *
1150 if [ "$1" = '*' ]; then
1151 touch .dummy
1152 set -- .dummy
1153 fi
1154 tar --xattrs -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
1155
1156 msg "Create checksum..."
1157 # append the hash for data.tar.gz
1158 local sha256=$(sha256sum "$dir"/data.tar.gz | cut -f1 -d' ')
1159 echo "datahash = $sha256" >> "$dir"/.PKGINFO
1160
1161 # control.tar.gz
1162 cd "$dir"
1163 tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
1164 | gzip -9 > control.tar.gz
1165 abuild-sign -q control.tar.gz || exit 1
1166
1167 msg "Create $apk"
1168 # create the final apk
1169 cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
1170 )
1171 done
1172 subpkgname=
1173}
1174
1175clean_abuildrepo() {
1176 local apk
1177 cd "$abuildrepo" || return 1
1178
1179 # remove compat symlink
1180 for d in "$abuildrepo/$CARCH"; do
1181 [ -L "$d" ] && rm "$d"
1182 done
1183
1184 # remove broken links from abuildrepo
1185 for apk in *.apk */*.apk; do
1186 if [ -L "$apk" ] && [ ! -f "$apk" ]; then
1187 rm -f "$apk"
1188 fi
1189 done
1190}
1191
1192mklinks_abuildrepo() {
1193 [ -n "$REPODEST" ] && return 0
1194 local apk
1195 mkdir -p "$abuildrepo"/$CARCH
1196 cd "$abuildrepo" || return 1
1197 # create links for this package
1198 for apk in $(listpkg); do
1199 [ -f "$PKGDEST"/$apk ] || continue
1200 ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk
1201 done
1202}
1203
1204build_abuildrepo() {
1205 local d apk _build=build
1206 if ! is_function package; then
1207 # if package() is missing then build is called from rootpkg
1208 _build=true
1209 fi
1210 if ! apk_up2date || [ -n "$force" ]; then
1211 # check early if we have abuild key
1212 abuild-sign --installed || return 1
1213 logcmd "building ${abuildrepo##*/}/$pkgname-$pkgver-r$pkgrel"
1214 sanitycheck && builddeps && clean && fetch && unpack \
1215 && prepare && mkusers && $_build && rootpkg \
1216 && cleanup $CLEANUP \
1217 || return 1
1218 fi
1219 update_abuildrepo_index
1220}
1221
1222update_abuildrepo_index() {
1223 clean_abuildrepo
1224 mklinks_abuildrepo
1225
1226 cd "$abuildrepo"
1227 local index=$CARCH/APKINDEX.tar.gz
1228
1229 msg "Updating the cached abuild repository index..."
1230 local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
1231 local oldindex=
1232 if [ -f "$index" ]; then
1233 oldindex="--index $index"
1234 fi
1235 $APK index --quiet $oldindex --output "$index".unsigned \
1236 --description "$repo $(cd $startdir && git describe)" \
1237 --rewrite-arch $CARCH \
1238 $CARCH/*.apk || exit 1
1239 msg "Signing the index..."
1240 abuild-sign -q "$index".unsigned || exit 1
1241 mv "$index".unsigned "$index"
1242 chmod 644 "$index"
1243}
1244
1245# predefined splitfunc doc
1246default_doc() {
1247 depends="$depends_doc"
1248 pkgdesc="$pkgdesc (documentation)"
1249 arch=${arch_doc:-"noarch"}
1250
1251 local i
1252 for i in doc man info html sgml licenses gtk-doc ri help; do
1253 if [ -d "$pkgdir/usr/share/$i" ]; then
1254 mkdir -p "$subpkgdir/usr/share"
1255 mv "$pkgdir/usr/share/$i" "$subpkgdir/usr/share/"
1256 fi
1257 done
1258
1259 # compress man pages
1260 find "$subpkgdir"/usr/share/man/*[1-9] \
1261 -type f \! -name \*.gz \! -name \*.bz2 2>/dev/null |
1262 xargs -r gzip -9
1263
1264 rm -f "$subpkgdir/usr/share/info/dir"
1265
1266 # remove if empty, ignore error (not empty)
1267 rmdir "$pkgdir/usr/share" "$pkgdir/usr" 2>/dev/null
1268
1269 return 0
1270}
1271
1272doc() {
1273 default_doc
1274}
1275
1276# predefined splitfunc doc
1277default_dbg() {
1278 local f
1279 binfiles=$(scanelf -R "$pkgdir" | grep ET_DYN | sed "s:$pkgdir\/::g" | sed "s:ET_DYN ::g")
1280 for f in $binfiles; do
1281 srcdir=$(dirname $pkgdir/$f)
1282 srcfile=$(basename $pkgdir/$f)
1283 dstdir=$(dirname $subpkgdir/usr/lib/debug/$f.debug)
1284 dstfile=$(basename $subpkgdir/usr/lib/debug/$f.debug)
1285 if [ ! -d $dstdir ] ; then
1286 mkdir -p $dstdir
1287 fi
1288 cd $srcdir
1289 objcopy --only-keep-debug $srcfile $dstfile
1290 objcopy --add-gnu-debuglink=$dstfile $srcdir/$srcfile
1291 mv $dstfile $dstdir
1292 strip $srcfile
1293 done
1294 return 0
1295}
1296
1297dbg() {
1298 default_dbg
1299}
1300
1301# predefined splitfunc dev
1302default_dev() {
1303 local i= j=
1304 depends="$pkgname=$pkgver-r$pkgrel $depends_dev"
1305 pkgdesc="$pkgdesc (development files)"
1306
1307 for i in $origsubpackages; do
1308 [ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}=$pkgver-r$pkgrel"
1309 done
1310
1311 cd "$pkgdir" || return 0
1312 local libdirs=usr/
1313 [ -d lib/ ] && libdirs="lib/ $libdirs"
1314 for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
1315 usr/share/gettext usr/bin/*-config \
1316 usr/share/vala/vapi usr/share/gir-[0-9]*\
1317 usr/share/qt*/mkspecs \
1318 usr/lib/qt*/mkspecs \
1319 usr/lib/cmake \
1320 $(find . -name include -type d) \
1321 $(find $libdirs -name '*.[acho]' \
1322 -o -name '*.prl' 2>/dev/null); do
1323 if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
1324 d="$subpkgdir/${i%/*}" # dirname $i
1325 mkdir -p "$d"
1326 mv "$pkgdir/$i" "$d"
1327 rmdir "$pkgdir/${i%/*}" 2>/dev/null
1328 fi
1329 done
1330 # move *.so links needed when linking the apps to -dev packages
1331 for i in lib/*.so usr/lib/*.so; do
1332 if [ -L "$i" ]; then
1333 mkdir -p "$subpkgdir"/"${i%/*}"
1334 mv "$i" "$subpkgdir/$i" || return 1
1335 fi
1336 done
1337 return 0
1338}
1339
1340dev() {
1341 default_dev
1342}
1343
1344# predefined splitfunc libs
1345default_libs() {
1346 pkgdesc="$pkgdesc (libraries)"
1347 local dir= file=
1348 for dir in lib usr/lib; do
1349 for file in "$pkgdir"/$dir/lib*.so.[0-9]*; do
1350 [ -f "$file" ] || continue
1351 mkdir -p "$subpkgdir"/$dir
1352 mv "$file" "$subpkgdir"/$dir/
1353 done
1354 done
1355}
1356
1357libs() {
1358 default_libs
1359}
1360
1361is_function() {
1362 type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
1363}
1364
1365do_fakeroot() {
1366 if [ -n "$FAKEROOT" ]; then
1367 $FAKEROOT -- "$@"
1368 else
1369 "$@"
1370 fi
1371}
1372
1373# build and package in fakeroot
1374rootpkg() {
1375 local _package=package
1376 if ! is_function package; then
1377 # if package() is missing then run 'build' in fakeroot instead
1378 warning "No package() function in APKBUILD"
1379 _package=build
1380 fi
1381 cd "$startdir"
1382 rm -rf "$pkgdir"
1383 [ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
1384 do_fakeroot "$abuild_path" $color_opt \
1385 $_package \
1386 prepare_subpackages \
1387 prepare_language_packs \
1388 prepare_package \
1389 create_apks
1390}
1391
1392srcpkg() {
1393 getpkgver || return 1
1394 local p="$pkgname-$pkgver-$pkgrel"
1395 local prefix="${startdir##*/}"
1396 local i files="$prefix/APKBUILD"
1397 for i in $source; do
1398 files="$files $prefix/$(filename_from uri $i)"
1399 done
1400 mkdir -p "$PKGDEST"
1401 msg "Creating source package $p.src.tar.gz..."
1402 (cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files)
1403}
1404
1405# return true if arch is supported or noarch
1406check_arch() {
1407 list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
1408}
1409
1410# return true if libc is not masked in options
1411check_libc() {
1412 ! list_has "!libc_$CLIBC" $options
1413}
1414
1415# check if package is up to date
1416apk_up2date() {
1417 getpkgver || return 1
1418 local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
1419 local i s
1420 cd "$startdir"
1421 for i in $pkgname $subpackages; do
1422 [ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
1423 done
1424 [ -n "$keep" ] && return 0
1425
1426 for i in $source APKBUILD; do
1427 local s
1428 if is_remote "$i"; then
1429 s="$SRCDEST/$(filename_from_uri $i)"
1430 else
1431 s="$startdir/${i##*/}"
1432 fi
1433 if [ "$s" -nt "$pkg" ]; then
1434 return 1
1435 fi
1436 done
1437 return 0
1438}
1439
1440abuildindex_up2date() {
1441 local i
1442 getpkgver || return 1
1443 local dir="$abuildrepo"/$CARCH
1444 local apk="${pkgname%:*}-$pkgver-r$pkgrel.apk"
1445 local idx="$dir"/APKINDEX.tar.gz
1446 local file="$dir"/$apk
1447
1448 # check if index is missing
1449 [ -f "$idx" ] || return 1
1450
1451 # if link or file is missing, then we need update abuildrepo index
1452 [ -f "$file" ] || return 1
1453
1454 # if file exists and is newer than index, then we need update index
1455 [ "$file" -nt "$idx" ] && return 1
1456
1457 return 0
1458}
1459
1460up2date() {
1461 check_arch || return 0
1462 check_libc || return 0
1463 apk_up2date && abuildindex_up2date
1464}
1465
1466# rebuild package and abuildrepo index if needed
1467abuildindex() {
1468 up2date && return 0
1469 build_abuildrepo
1470}
1471
1472# source all APKBUILDs and output:
1473# 1) origin of package
1474# 2) all dependencies
1475# the output is i in a format easy parseable for awk
1476depparse_aports() {
1477 # lets run this in a subshell since we source all APKBUILD here
1478 (
1479 aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
1480 for i in $aportsdir/*/APKBUILD; do
1481 pkgname=
1482 subpackages=
1483 depends=
1484 makedepends=
1485 . $i
1486 dir=${i%/APKBUILD}
1487 deps=
1488 # filter out conflicts from deps and version info
1489 for j in $depends $makedepends; do
1490 case "$j" in
1491 !*) continue;;
1492 esac
1493 deps="$deps ${j%%[<>=]*}"
1494 done
1495 for j in $pkgname $subpackages; do
1496 echo "o ${j%%:*} $dir"
1497 set -- $deps
1498 echo -n "d ${j%%:*} $1"
1499 shift
1500 while [ $# -gt 0 ]; do
1501 echo -n ",$1"
1502 shift
1503 done
1504 echo
1505 done
1506 done
1507 )
1508}
1509
1510deptrace() {
1511 local deps= i=
1512 # strip versions from deps
1513 for i in "$@"; do
1514 deps="$deps ${i%%[<>=]*}"
1515 done
1516 [ -z "$deps" ] && return 0
1517 ( depparse_aports
1518 if [ -z "$upgrade" ]; then
1519 # list installed pkgs and prefix with 'i '
1520 $APK info --quiet | sort | sed 's/^/i /'
1521 fi
1522 ) | awk -v pkgs="$deps" '
1523
1524 function depgraph(pkg, a, i) {
1525 if (visited[pkg])
1526 return 0;
1527 visited[pkg] = 1;
1528 split(deps[pkg], a, ",");
1529 for (i in a)
1530 depgraph(a[i]);
1531 print pkg ":" origin[pkg];
1532
1533 }
1534
1535 $1 == "i" { visited[$2] = 1 }
1536 $1 == "o" { origin[$2] = $3 }
1537 $1 == "d" { deps[$2] = $3 }
1538 END {
1539 split(pkgs, pkgarray);
1540 for (i in pkgarray)
1541 depgraph(pkgarray[i]);
1542 }
1543 '
1544}
1545
1546# build and install dependencies
1547builddeps() {
1548 local pkg= i= missing=
1549 local hostdeps= builddeps= installed_hostdeps= installed_builddeps=
1550 [ -n "$nodeps" ] && return 0
1551 msg "Analyzing dependencies..."
1552
1553 # add depends unless it is a subpackage or package itself
1554 if cross_compiling && [ -n "$makedepends_build" -o -n "$makedepends_host" ]; then
1555 builddeps="$makedepends_build"
1556 for i in $BUILD_BASE; do
1557 [ "$pkgname" = "${i%%[<>=]*}" ] && continue
1558 subpackages_has ${i%%[<>=]*} || builddeps="$builddeps $i"
1559 done
1560 hostdeps="$makedepends_host"
1561 for i in $depends; do
1562 [ "$pkgname" = "${i%%[<>=]*}" ] && continue
1563 subpackages_has ${i%%[<>=]*} || hostdeps="$hostdeps $i"
1564 done
1565 else
1566 builddeps="$makedepends"
1567 for i in $BUILD_BASE $depends; do
1568 [ "$pkgname" = "${i%%[<>=]*}" ] && continue
1569 subpackages_has ${i%%[<>=]*} || builddeps="$builddeps $i"
1570 done
1571 fi
1572
1573 installed_builddeps=$($APK info --installed $builddeps)
1574 [ -n "$hostdeps" ] && installed_hostdeps=$($APK info --root "$CBUILDROOT" --installed $hostdeps)
1575
1576 # find which deps are missing
1577 for i in $builddeps; do
1578 if [ "${i#\!}" != "$i" ] && $APK info --quiet --installed "${i#\!}"; then
1579 error "Conflicting package installed: ${i#\!}"
1580 elif ! deplist_has $i $installed_builddeps || [ -n "$upgrade" ]; then
1581 missing="$missing $i"
1582 fi
1583 done
1584 for i in $hostdeps; do
1585 if [ "${i#\!}" != "$i" ] && $APK info --quiet --installed --root "$CBUILDROOT" "${i#\!}"; then
1586 error "Conflicting package installed: ${i#\!}"
1587 elif ! deplist_has $i $installed_hostdeps || [ -n "$upgrade" ]; then
1588 missing="$missing $i"
1589 fi
1590 done
1591
1592 if [ -z "$install_deps" ] && [ -z "$recursive" ]; then
1593 # if we dont have any missing deps we are done now
1594 [ -z "$missing" ] && return 0
1595 error "Missing dependencies: $missing Use -r to autoinstall or -R to build"
1596 return 1
1597 fi
1598
1599 uninstall_after=".makedepends-$pkgname $uninstall_after"
1600 if [ -n "$install_deps" ] && [ -z "$recursive" ]; then
1601 # make a --simulate run first to detect missing deps
1602 # apk-tools --virtual is no goot at reporting those.
1603 $SUDO_APK add --repository "$abuildrepo" $apk_opt_wait \
1604 --simulate --quiet $builddeps || return 1
1605 $SUDO_APK add --repository "$abuildrepo" $apk_opt_wait \
1606 --virtual .makedepends-$pkgname $builddeps || return 1
1607 if [ -n "$hostdeps" ]; then
1608 $SUDO_APK add --root "$CBUILDROOT" --repository "$abuildrepo" $apk_opt_wait \
1609 --simulate --quiet $hostdeps || return 1
1610 $SUDO_APK add --root "$CBUILDROOT" --repository "$abuildrepo" $apk_opt_wait \
1611 --virtual .makedepends-$pkgname $hostdeps || return 1
1612 fi
1613 return 0
1614 fi
1615
1616 if [ -n "$CBUILDROOT" ]; then
1617 error "Recursive rebuilding is not supported when cross compiling."
1618 return 1
1619 fi
1620 [ -z "$recursive" ] && return 1
1621
1622 # find dependencies that are installed but missing in repo.
1623 for i in $builddeps; do
1624 local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
1625 if [ -z "$m" ]; then
1626 missing="$missing $i"
1627 fi
1628 done
1629
1630 for i in $(deptrace $missing); do
1631 # i = pkg:dir
1632 local dir=${i#*:}
1633 local pkg=${i%:*}
1634
1635 # ignore if dependency is in other repo
1636 [ -d "$dir" ] || continue
1637
1638 # check if dep is blacklisted
1639 if list_has $pkg $ABUILD_BLACKLIST; then
1640 error "$pkg is blacklisted"
1641 return 1
1642 fi
1643
1644 # break circular deps
1645 list_has $pkg $ABUILD_VISITED && continue
1646 export ABUILD_VISITED="$ABUILD_VISITED $pkg"
1647
1648 msg "Entering $dir"
1649 cd "$dir" && $0 $forceroot $keep $quiet $install_deps \
1650 $recursive $upgrade $color_opt abuildindex || return 1
1651 done
1652 $SUDO_APK add --upgrade --repository "$abuildrepo" \
1653 $apk_opt_wait \
1654 --virtual .makedepends-$pkgname $builddeps
1655}
1656
1657# replace the md5sums in the APKBUILD
1658checksum() {
1659 local s files
1660 [ -z "$source" ] && [ -n "${md5sums}${sha256sums}${sha512sums}" ] \
1661 && msg "Removing checksums from APKBUILD"
1662 sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
1663 sed -i -e '/^sha512sums="/,/"\$/d; /^sha512sums=''/,/''\$/d' "$APKBUILD"
1664 sed -i -e '/^sha256sums="/,/"\$/d; /^sha256sums=''/,/''\$/d' "$APKBUILD"
1665 [ -z "$source" ] && return 0
1666 fetch
1667 for s in $source; do
1668 files="$files $(filename_from_uri $s)"
1669 done
1670
1671 # for compatibility/backporting reasons we still add md5sum
1672 msg "Updating the md5sums in APKBUILD..."
1673 md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
1674 echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
1675
1676 msg "Updating the sha256sums in APKBUILD..."
1677 sha256sums="$(cd "$srcdir" && sha256sum $files)" \
1678 || die "sha256sum failed"
1679 echo "sha256sums=\"$sha256sums\"" >>"$APKBUILD"
1680
1681 msg "Updating the sha512sums in APKBUILD..."
1682 sha512sums="$(cd "$srcdir" && sha512sum $files)" \
1683 || die "sha512sum failed"
1684 echo "sha512sums=\"$sha512sums\"" >>"$APKBUILD"
1685}
1686
1687stripbin() {
1688 local bin
1689 if options_has "!strip" || [ "$arch" = "noarch" ]; then
1690 return 0
1691 fi
1692 cd "${subpkgdir:-$pkgdir}" || return 1
1693 msg "Stripping binaries"
1694 scanelf --recursive --nobanner --etype "ET_DYN,ET_EXEC" . \
1695 | sed -e 's:^ET_DYN ::' -e 's:^ET_EXEC ::' \
1696 | while read filename; do
1697 XATTR=$(getfattr --match="*" --dump "${filename}")
1698 ${CROSS_COMPILE}strip "${filename}"
1699 [ -n "$XATTR" ] && (echo "$XATTR" | setfattr --restore=-)
1700 done
1701}
1702
1703# simply list target apks
1704listpkg() {
1705 local name
1706 getpkgver || return 1
1707 for name in $(listpkgnames) ; do
1708 echo "$name-$pkgver-r$pkgrel.apk"
1709 done
1710}
1711
1712source_has() {
1713 local i
1714 for i in $source; do
1715 [ "$1" = "${i##*/}" ] && return 0
1716 [ "$1" = "${i%%::*}" ] && return 0
1717 done
1718 return 1
1719}
1720
1721subpackages_has() {
1722 local i
1723 for i in $subpackages; do
1724 [ "$1" = "${i%:*}" ] && return 0
1725 done
1726 return 1
1727}
1728
1729subpackage_types_has() {
1730 local i
1731 for i in $subpackages; do
1732 [ "$1" = "${i##*-}" ] && return 0
1733 done
1734 return 1
1735}
1736
1737list_has() {
1738 local needle="$1"
1739 local i
1740 shift
1741 for i in $@; do
1742 [ "$needle" = "$i" ] && return 0
1743 [ "$needle" = "!$i" ] && return 1
1744 done
1745 return 1
1746}
1747
1748# same as list_has but we filter version info
1749deplist_has() {
1750 local needle="$1"
1751 local i
1752 shift
1753 for i in $@; do
1754 i=${i%%[<>=]*}
1755 [ "$needle" = "$i" ] && return 0
1756 [ "$needle" = "!$i" ] && return 1
1757 done
1758 return 1
1759}
1760
1761options_has() {
1762 list_has "$1" $options
1763}
1764
1765depends_has() {
1766 deplist_has "$1" $depends
1767}
1768
1769makedepends_has() {
1770 deplist_has "$1" $makedepends
1771}
1772
1773md5sums_has() {
1774 list_has "$1" $md5sums
1775}
1776
1777install_has() {
1778 list_has "$1" $install
1779}
1780
1781# install package after build
1782post_add() {
1783 getpkgver || return 1
1784 local pkgf="$PKGDEST/$1-$pkgver-r$pkgrel.apk"
1785 local deps i
1786 if ! subpackages_has $1 && [ "$1" != "$pkgname" ]; then
1787 die "$1 is not built by this APKBUILD"
1788 fi
1789 # recursively install dependencies that are provided by this APKBUILD
1790 deps=$($APK index "$pkgf" 2>/dev/null | awk -F: '$1=="D" { print $2 }')
1791 for i in $deps; do
1792 if subpackages_has $i || [ "$i" = "$pkgname" ]; then
1793 post_add $i || return 1
1794 fi
1795 done
1796 $SUDO_APK add $apk_opt_wait --upgrade "$pkgf" \
1797 || die "Failed to install $1"
1798}
1799
1800deps() {
1801 local builddeps i
1802 builddeps="$makedepends"
1803 for i in $depends; do
1804 [ "$pkgname" = "${i%%[<>=]*}" ] && continue
1805 subpackages_has ${i%%[<>=]*} || builddeps="$builddeps $i"
1806 done
1807 $SUDO_APK add $apk_opt_wait --repository "$abuildrepo" \
1808 --virtual .makedepends-$pkgname \
1809 $builddeps
1810}
1811
1812undeps (){
1813 $SUDO_APK del $apk_opt_wait .makedepends-$pkgname
1814 if cross_compiling; then
1815 $SUDO_APK del --root "$CBUILDROOT" $apk_opt_wait \
1816 .makedepends-$pkgname
1817 fi
1818}
1819
1820# compat
1821installdeps() { deps; }
1822uninstalldeps() { undeps; }
1823index() { update_abuildrepo_index; }
1824
1825all() {
1826 if ! [ -n "$force" ]; then
1827 check_arch
1828 if [ $? -ne 0 ]; then
1829 echo "Package not available for the target architecture ($CARCH). Aborting."
1830 return 0
1831 fi
1832 check_libc || return 0
1833 fi
1834 if up2date && [ -z "$force" ]; then
1835 msg "Package is up to date"
1836 else
1837 build_abuildrepo
1838 fi
1839}
1840
1841# This abuild hook will checkout an svn or git repository by specifying
1842# $svnurl or $giturl in APKBUILD. You can checkout a specific branch in
1843# git by adding -b $branch in $giturl. $reporev will select the correct
1844# commit, revision or tag for you. If you specify $disturl your distfile
1845# will automatically be uploaded with rsync to the url provided.
1846# Base version defaults to 0 except if specified by $verbase.
1847
1848snapshot() {
1849 # check if we setup vars correctly
1850 [ -z "$disturl" ] && warning "Missing disturl in APKBUILD, auto uploading disabled."
1851 [ -z "$svnurl" ] && [ -z "$giturl" ] && die "Missding repository url in APKBUILD!"
1852 [ -n "$svnurl" ] && [ -n "$giturl" ] && die "You can only use a single repository!"
1853 local _date=$(date +%Y%m%d)
1854 local _format="tar.gz"
1855 # remove any repositories left in srcdir
1856 abuild clean
1857 mkdir -p "$srcdir" && cd "$srcdir"
1858 # clone git repo and archive
1859 if [ -n "$giturl" ]; then
1860 local _version=${verbase:-0}_git${_date}
1861 command -v git >/dev/null || \
1862 die "Missing git! Install git to support git clone."
1863 [ -z "$reporev" ] && local _rev="HEAD" && local _depth="--depth=1"
1864 msg "Creating git snapshot: $pkgname-$_version"
1865 git clone $_depth --bare $giturl $pkgname-$_version || return 1
1866 git --git-dir $pkgname-$_version archive \
1867 --format=$_format \
1868 -o $pkgname-$_version.$_format \
1869 --prefix=$pkgname-$_version/ $_rev \
1870 || return 1
1871 fi
1872 # export svn repo and archive
1873 if [ -n "$svnurl" ]; then
1874 local _version=${verbase:-0}_svn${_date}
1875 command -v svn >/dev/null || \
1876 die "Missing svn! Install subverion to support svn export."
1877 [ -n "$reporev" ] && local _rev="-r $reporev"
1878 msg "Creating svn snapshot: $pkgname-$_version"
1879 svn co $_rev $svnurl $pkgname-$_version || return 1
1880 tar zcf $pkgname-$_version.$_format $pkgname-$_version || return 1
1881 fi
1882 # upload to defined distfiles url
1883 if [ -n "$disturl" ]; then
1884 command -v rsync >/dev/null || \
1885 die "Missing rsync! Install rsync to enable automatic uploads."
1886 msg "Uploading to $disturl"
1887 rsync --progress -La $pkgname-$_version.$_format \
1888 $disturl || return 1
1889 cd "$startdir"
1890 # set the pkgver to current date and update checksum
1891 sed -i -e "s/^pkgver=.*/pkgver=${_version}/" \
1892 APKBUILD || return 1
1893 abuild checksum
1894 fi
1895}
1896
1897usage() {
1898 echo "$program $program_version"
1899 cat << EOF
1900usage: $program [options] [-i PKG] [-P REPODEST] [-p PKGDEST]
1901 [-s SRCDEST] [cmd] ...
1902 $program [-c] -n PKGNAME[-PKGVER]
1903Options:
1904 -A Print CARCH and exit
1905 -c Enable colored output
1906 -d Disable dependency checking
1907 -f Force specified cmd, even if they are already done
1908 -F Force run as root
1909 -h Show this help
1910 -i Install PKG after successful build
1911 -k Keep built packages, even if APKBUILD or sources are newer
1912 -m Disable colors (monochrome)
1913 -p Set package destination directory
1914 -P Set PKGDEST to REPODEST/<repo>/\$CARCH, where repo is the parents dir name
1915 -q Quiet
1916 -r Install missing dependencies from system repository (using sudo)
1917 -R Recursively build and install missing dependencies (using sudo)
1918 -s Set source package destination directory
1919 -u Recursively build and upgrade all dependencies (using sudo)
1920
1921Commands:
1922 build Compile and install package into \$pkgdir
1923 checksum Generate checksum to be included in APKBUILD
1924 clean Remove temp build and install dirs
1925 cleancache Remove downloaded files from \$SRCDEST
1926 cleanoldpkg Remove binary packages except current version
1927 cleanpkg Remove already built binary and source package
1928 deps Install packages listed in makedepends and depends
1929 fetch Fetch sources to \$SRCDEST and verify checksums
1930 index Regenerate the APKINDEX for abuildrepo
1931 listpkg List target packages
1932 package Create package in \$PKGDEST
1933 prepare Apply patches
1934 rootpkg Run 'package', the split functions and create apks as fakeroot
1935 sanitycheck Basic sanity check of APKBUILD
1936 snapshot Create a \$giturl or \$svnurl snapshot and upload to \$disturl
1937 sourcecheck Check if remote source package exists upstream
1938 srcpkg Make a source package
1939 undeps Uninstall packages listed in makedepends and depends
1940 unpack Unpack sources to \$srcdir
1941 up2date Compare target and sources dates
1942 verify Verify checksums
1943
1944EOF
1945 exit 0
1946}
1947
1948APKBUILD="${APKBUILD:-./APKBUILD}"
1949unset force
1950unset recursive
1951while getopts "AcdfFhi:kimnp:P:qrRs:u" opt; do
1952 case $opt in
1953 'A') echo "$CARCH"; exit 0;;
1954 'c') enable_colors
1955 color_opt="-c";;
1956 'd') nodeps=1;;
1957 'f') force="-f";;
1958 'F') forceroot="-F";;
1959 'h') usage;;
1960 'i') install_after="$install_after $OPTARG";;
1961 'k') keep="-k";;
1962 'm') disable_colors
1963 color_opt="-m";;
1964 'n') die "Use newapkbuild to create new aports";;
1965 'p') PKGDEST=$OPTARG;;
1966 'P') REPODEST=$OPTARG;;
1967 'q') quiet="-q";;
1968 'r') install_deps="-r";;
1969 'R') recursive="-R";;
1970 's') SRCDEST=$OPTARG;;
1971 'u') upgrade="-u"
1972 recursive="-R";;
1973 esac
1974done
1975shift $(( $OPTIND - 1 ))
1976
1977# check so we are not root
1978if [ "$(whoami)" = "root" ] && [ -z "$FAKEROOTKEY" ]; then
1979 [ -z "$forceroot" ] && die "Do not run abuild as root"
1980 FAKEROOT=
1981fi
1982
1983# find startdir
1984[ -f "$APKBUILD" ] || die "Could not find $APKBUILD (PWD=$PWD)"
1985APKBUILD=$(readlink -f "$APKBUILD")
1986
1987startdir="${APKBUILD%/*}"
1988srcdir=${srcdir:-"$startdir/src"}
1989pkgbasedir=${pkgbasedir:-"$startdir/pkg"}
1990
1991repo=${startdir%/*}
1992repo=${repo##*/}
1993
1994SRCDEST=${SRCDEST:-$startdir}
1995PKGDEST=${PKGDEST:-$startdir}
1996
1997# set a default CC
1998: ${CC:=gcc}
1999export CC
2000
2001cd "$startdir" || die
2002. "$APKBUILD"
2003
2004# If REPODEST is set then it will override the PKGDEST
2005if [ -n "$REPODEST" ]; then
2006 PKGDEST="$REPODEST/$repo/$CARCH"
2007 # for recursive action
2008 export REPODEST
2009 abuildrepo="$REPODEST"/$repo
2010else
2011 abuildrepo="$abuildrepo_base"/$repo
2012fi
2013
2014# if we want build debug package
2015if [ -n "$DEBUG" ] || subpackage_types_has "dbg"; then
2016 CFLAGS="$CFLAGS -g"
2017 CXXFLAGS="$CXXFLAGS -g"
2018 options="$options !strip"
2019fi
2020
2021# If we are handling a sub package then reset subpackages and install
2022if [ -n "$subpkgname" ]; then
2023 origsubpackages="$subpackages"
2024 subpackages=
2025fi
2026pkgdir="$pkgbasedir/$pkgname"
2027controldir="$pkgbasedir"/.control.${subpkgname:-$pkgname}
2028
2029trap 'die "Aborted by user"' INT
2030set_xterm_title "abuild: $pkgname"
2031
2032if [ -z "$1" ]; then
2033 set all
2034fi
2035
2036while [ $# -gt 0 ]; do
2037 runpart $1
2038 shift
2039done
2040
2041for i in $install_after; do
2042 post_add $i
2043done
2044
2045cleanup
2046