projects/aports·blob v2.9.2 · refs

1452 lines · 33.4 KiB

1#!/bin/sh
2
3# script to build apk packages (light version of makepkg)
4# Copyright (c) 2008 Natanael Copa <[email protected]>
5#
6# Distributed under GPL-2
7#
8# Depends on: busybox utilities, fakeroot,
9#
10
11abuild_ver=@VERSION@
12sysconfdir=@sysconfdir@
13abuildrepo_base=@abuildrepo@
14datadir=@datadir@
15
16program=${0##*/}
17abuild_path=$(readlink -f $0)
18
19# defaults
20BUILD_BASE="build-base"
21SUDO=${SUDO:-"sudo"}
22FAKEROOT=${FAKEROOT:-"fakeroot"}
23APK=${APK:-apk}
24apk_opt_wait="--wait 30"
25
26# read config
27ABUILD_CONF=${ABUILD_CONF:-"$sysconfdir/abuild.conf"}
28[ -f "$ABUILD_CONF" ] && . "$ABUILD_CONF"
29
30default_colors() {
31 NORMAL="\033[1;0m"
32 STRONG="\033[1;1m"
33 RED="\033[1;31m"
34 GREEN="\033[1;32m"
35 YELLOW="\033[1;33m"
36 BLUE="\033[1;34m"
37}
38
39monochrome() {
40 NORMAL=""
41 STRONG=""
42 RED=""
43 GREEN=""
44 YELLOW=""
45 BLUE=""
46}
47
48#colors
49if [ -n "$USE_COLORS" ]; then
50 default_colors
51fi
52
53
54# functions
55msg() {
56 local prompt="$GREEN>>>${NORMAL}"
57 local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
58 local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
59 [ -z "$quiet" ] && printf "${prompt} ${name}${fake}: $@\n" >&2
60}
61
62warning() {
63 local prompt="${YELLOW}>>> WARNING:${NORMAL}"
64 local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
65 local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
66 printf "${prompt} ${name}${fake}: $@\n" >&2
67}
68
69error() {
70 local prompt="${RED}>>> ERROR:${NORMAL}"
71 local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
72 local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
73 printf "${prompt} ${name}${fake}: $@\n" >&2
74}
75
76
77set_xterm_title() {
78 if [ "$TERM" = xterm ] && [ -n "$USE_COLORS" ]; then
79 printf "\033]0;$1\007" >&2
80 fi
81}
82
83cleanup() {
84 set_xterm_title ""
85 if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
86 $SUDO $APK del $apk_opt_wait $uninstall_after
87 fi
88}
89
90die() {
91 error "$@"
92 cleanup
93 exit 1
94}
95
96# check if apkbuild is basicly sane
97sanitycheck() {
98 local i suggestion
99 msg "Checking sanity of $APKBUILD..."
100 [ -z "$pkgname" ] && die "Missing pkgname in APKBUILD"
101 [ -z "${pkgname##* *}" ] && die "pkgname contains spaces"
102 [ -z "$pkgver" ] && die "Missing pkgver in APKBUILD"
103 if [ "$pkgver" != "volatile" ] && [ -z "$nodeps" ]; then
104 $APK version --check -q "$pkgver" ||\
105 die "$pkgver is not a valid version"
106 fi
107 [ -z "$pkgrel" ] && die "Missing pkgrel in APKBUILD"
108 [ -z "$pkgdesc" ] && die "Missing pkgdesc in APKBUILD"
109 [ -z "$url" ] && die "Missing url in APKBUILD"
110 [ -z "$license" ] && die "Missing license in APKBULID"
111
112 # check if CARCH, CBUILD, CHOST and CTARGET is set
113 if [ -z "$CARCH" ]; then
114 case "$(uname -m)" in
115 i[0-9]86) suggestion=" (Suggestion: CARCH=x86)";;
116 x86_64) suggestion=" (Suggestion: CARCH=x86_64)";;
117 esac
118 die "Please set CARCH in /etc/abuild.conf$suggestion"
119 fi
120 [ -z "$CHOST" ] && die "Please set CHOST in /etc/abuild.conf"
121
122 for i in $install; do
123 [ -e "$startdir/$i" ] || die "install script $startdir/$i is missing"
124 done
125
126 [ -n "${triggers%%:*}" ] && [ ! -e "$startdir"/${triggers%%:*} ] \
127 && die "trigger script $startdir/${triggers%%:*} is missing"
128
129 if [ -n "$source" ]; then
130 for i in $source; do
131 if install_has "$i"; then
132 warning "You should not have \$install in source"
133 continue
134 fi
135 md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
136 case "$i" in
137 https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
138 esac
139 done
140 fi
141
142 if [ -n "$md5sums" ]; then
143 for i in $(echo "$md5sums" | awk '{ print $2 }'); do
144 source_has $i || die "$i exists in md5sums but is missing in source"
145 done
146 fi
147
148 # common spelling errors
149 [ -n "$depend" ] && die "APKBUILD contains 'depend'. It should be depends"
150 [ -n "$makedepend" ] && die "APKBUILD contains 'makedepend'. It should be makedepends"
151
152 grep '^# Maintainer:' $APKBUILD >/dev/null || warning "No maintainer"
153
154 makedepends_has 'g++' && warning "g++ should not be in makedepends"
155 return 0
156}
157
158md5check() {
159 local dummy f endreturnval originalparams origin file
160 if [ -z "$source" ]; then
161 return 0
162 fi
163 if [ -z "$md5sums" ]; then
164 die "Use 'abuild checksum' to generate/update the checksum(s)"
165 fi
166 if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
167 die "Number of md5sums does not correspond to number of sources"
168 fi
169 fetch || return 1
170 msg "Checking md5sums..."
171 cd "$srcdir" || return 1
172 IFS=$'\n'
173 endreturnval=0
174 originalparams=$@
175 set -- $source
176 for src in $md5sums; do
177 origin=$1; shift
178 echo "$src" | md5sum -c
179 if [ $? -ne 0 ]; then
180 endreturnval=1
181 is_remote $origin || continue
182 echo "Because the remote file above failed the md5sum check it will be deleted."
183 echo "Rebuilding will cause it to re-download which in some cases may fix the problem."
184 file=`echo "$src" | sed 's/.*[ \t\n]\(.*\)/\1/'`
185 echo "Deleting: $file"
186 rm $file
187 fi
188 done
189 unset IFS
190 set -- $originalparams
191 return $endreturnval
192}
193
194# verify upstream sources
195sourcecheck() {
196 local uri
197 for uri in $source; do
198 is_remote $uri || continue
199 case "$uri" in
200 saveas-*://*)
201 uri=${uri#saveas-}
202 uri=${uri%/*}
203 ;;
204 esac
205 wget -q -s "$uri" || return 1
206 done
207 return 0
208}
209
210uri_fetch() {
211 local uri="$1"
212 local d="${uri##*/}" # $(basename $uri)
213 local opts
214 [ -n "$quiet" ] && opts="-q"
215 [ -f "$SRCDEST/$d" ] && return 0
216
217 # fix saveas-*://* URIs
218 case "$uri" in
219 # remove 'saveas-' from beginning and
220 # '/filename' from end of URI
221 saveas-*://*) uri="${uri:7:$(expr ${#uri} - 7 - ${#d} - 1)}";;
222 esac
223
224 # we need GNU wget for this
225 case "$uri" in
226 https://*) opts="--no-check-certificate";;
227 esac
228
229 mkdir -p "$SRCDEST"
230 if [ -f "$SRCDEST/$d.part" ]; then
231 msg "Partial download found. Trying to resume"
232 opts="$opts -c"
233 fi
234 msg "Fetching $uri"
235 wget $opts -O "$SRCDEST/$d.part" "$uri" \
236 && mv "$SRCDEST/$d.part" "$SRCDEST/$d"
237}
238
239is_remote() {
240 case "$1" in
241 http://*|ftp://*|https://*|saveas-*://*)
242 return 0;;
243 esac
244 return 1
245}
246
247# try download from file from mirror first
248uri_fetch_mirror() {
249 local uri="$1"
250 local d="${uri##*/}" # $(basename $uri)
251 if [ -n "$DISTFILES_MIRROR" ]; then
252 if is_remote "$DISTFILES_MIRROR"; then
253 uri_fetch "$DISTFILES_MIRROR"/$d && return 0
254 else
255 cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
256 fi
257 fi
258 uri_fetch "$uri"
259}
260
261default_fetch() {
262 local s
263 mkdir -p "$srcdir"
264 for s in $source; do
265 if is_remote "$s"; then
266 uri_fetch_mirror "$s" || return 1
267 ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
268 else
269 ln -sf "$startdir/$s" "$srcdir/"
270 fi
271 done
272}
273
274fetch() {
275 default_fetch
276}
277
278# verify that all init.d scripts are openrc runscripts
279initdcheck() {
280 local i
281 for i in $source; do
282 case $i in
283 *.initd)
284 head -n 1 "$srcdir"/$i | grep -q '/sbin/runscript' \
285 && continue
286 error "$i is not an openrc #!/sbin/runscript"
287 return 1
288 ;;
289 esac
290 done
291}
292
293# unpack the sources
294default_unpack() {
295 local u
296 if [ -z "$force" ]; then
297 md5check || return 1
298 initdcheck || return 1
299 fi
300 mkdir -p "$srcdir"
301 for u in $source; do
302 local s="$SRCDEST/${u##*/}" # $(basename $s)
303 case "$s" in
304 *.tar.gz|*.tgz)
305 msg "Unpacking $s..."
306 tar -C "$srcdir" -zxf "$s" || return 1;;
307 *.tar.bz2)
308 msg "Unpacking $s..."
309 tar -C "$srcdir" -jxf "$s" || return 1;;
310 *.tar.lzma)
311 msg "Unpacking $s..."
312 unlzma -c "$s" | tar -C "$srcdir" -x \
313 || return 1;;
314 *.tar.xz)
315 msg "Unpacking $s..."
316 unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
317 *.zip)
318 msg "Unpacking $s..."
319 unzip "$s" -d "$srcdir" || return 1;;
320 esac
321 done
322}
323
324unpack() {
325 default_unpack
326}
327
328# cleanup source and package dir
329clean() {
330 msg "Cleaning temporary build dirs..."
331 rm -rf "$srcdir"
332 rm -rf "$pkgbasedir"
333}
334
335# cleanup fetched sources
336cleancache() {
337 local s
338 for s in $source; do
339 if is_remote "$s"; then
340 msg "Cleaning downloaded ${s##*/}..."
341 rm -f "$SRCDEST/${s##*/}"
342 fi
343 done
344}
345
346listpkgnames() {
347 local i
348 for i in $pkgname $subpackages; do
349 echo ${i%:*}
350 done
351 for i in $linguas; do
352 echo $pkgname-lang-$i
353 done
354}
355
356cleanpkg() {
357 local i
358 getpkgver || return 1
359 msg "Cleaning built packages..."
360 for i in $(listpkgnames); do
361 local p="${i%:*}-$pkgver-r$pkgrel"
362 rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
363 "$abuildrepo"/$p.apk "$abuildrepo"/*/$p.apk
364 done
365 # remove given packages from index
366}
367
368# clean all packages except current
369cleanoldpkg() {
370 local i j
371 getpkgver || return 1
372 msg "Cleaning all packages except $pkgver-r$pkgrel..."
373 for i in $(listpkgnames); do
374 local pn=${i%:*}
375 for j in "$PKGDEST"/$pn-[0-9]*.apk ; do
376 [ "$j" = "$PKGDEST/$pn-$pkgver-r$pkgrel.apk" ] \
377 && continue
378 rm -f "$j" "$abuildrepo"/*/${j##*/}
379 done
380 done
381 return 0
382}
383
384mkusers() {
385 local i
386 for i in $pkgusers; do
387 if ! getent passwd $i >/dev/null; then
388 local gopt=
389 msg "Creating user $i"
390 if getent group $i >/dev/null; then
391 gopt="-G $i"
392 fi
393 $SUDO adduser -D -H $gopt $i || return 1
394 fi
395 done
396 for i in $pkggroups; do
397 if ! getent group $i >/dev/null; then
398 msg "Creating group $i"
399 $SUDO addgroup $i || return 1
400 fi
401 done
402}
403
404
405runpart() {
406 local part=$1
407 [ -n "$DEBUG" ] && msg "$part"
408 $part || die "$part failed"
409}
410
411# override those in your build script
412getpkgver() {
413 # this func is supposed to be overridden by volatile packages
414 if [ "$pkgver" = "volatile" ]; then
415 error "Please provide a getpkgver() function in your APKBUILD"
416 return 1
417 fi
418}
419
420prepare() {
421 :
422}
423
424build() {
425 :
426}
427
428# generate a simple tar.gz package of pkgdir
429targz() {
430 cd "$pkgdir" || return 1
431 mkdir -p "$PKGDEST"
432 tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
433}
434
435get_split_func() {
436 # get the 'func' from "sub-pkg:func"
437 local func=${1##*:}
438
439 # get 'func' from "sub-pkg-func" if there was no :func
440 [ "$func" = "$1" ] && func=${func##*-}
441 echo $func
442}
443
444prepare_subpackages() {
445 local i
446 cd "$startdir"
447 for i in $subpackages; do
448 local func=$(get_split_func $i)
449 # call abuild recursively, setting subpkg{dir,name}
450 msg "Running split function $func..."
451 subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
452 $0 $func prepare_package || return 1
453 done
454}
455
456lang_subpkg() {
457 if [ -z "$lang" ]; then
458 error "lang is not set"
459 return 1
460 fi
461 arch="noarch"
462 install_if="$pkgname=$pkgver-r$pkgrel lang-$lang"
463 mkdir -p "$subpkgdir"/usr/share/locale
464 mv "$pkgdir"/usr/share/locale/$lang* \
465 "$subpkgdir"/usr/share/locale/ \
466 || return 1
467}
468
469prepare_language_packs() {
470 for lang in $linguas; do
471 lang="$lang" \
472 subpkgname="$pkgname-lang-$lang" \
473 subpkgdir="$pkgbasedir"/$subpkgname \
474 $0 lang_subpkg prepare_package || return 1
475 done
476}
477
478# echo '-dirty' if git is not clean
479git_dirty() {
480 if [ $(git status -s "$startdir" | wc -l) -ne 0 ]; then
481 echo "-dirty"
482 fi
483}
484
485# echo last commit hash id
486git_last_commit() {
487 git log --format=oneline -n 1 "$startdir" | awk '{print $1}'
488}
489
490get_maintainer() {
491 if [ -z "$maintainer" ]; then
492 maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
493 fi
494}
495
496prepare_metafiles() {
497 getpkgver || return 1
498 local name=${subpkgname:-$pkgname}
499 [ -z "${name##* *}" ] && die "package name contains spaces"
500 local dir=${subpkgdir:-$pkgdir}
501 local pkg="$name-$pkgver-r$pkgrel.apk"
502 local pkginfo="$controldir"/.PKGINFO
503 local sub
504
505 [ ! -d "$dir" ] && die "Missing $dir"
506 cd "$dir"
507 mkdir -p "$controldir"
508 local builddate=$(date -u "+%s")
509 local size=$(du -sk | awk '{print $1 * 1024}')
510 local parch="$CARCH"
511
512 # we need to wait with setting noarch til our build infra can handle it
513# if [ "$arch" = "noarch" ]; then
514# parch="noarch"
515# fi
516
517 echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
518 if [ -n "$FAKEROOTKEY" ]; then
519 echo "# using $($FAKEROOT -v)" >> "$pkginfo"
520 fi
521 echo "# $(date -u)" >> "$pkginfo"
522 cat >> "$pkginfo" <<EOF
523pkgname = $name
524pkgver = $pkgver-r$pkgrel
525pkgdesc = $pkgdesc
526url = $url
527builddate = $builddate
528packager = ${PACKAGER:-"Unknown"}
529size = $size
530arch = $parch
531origin = $pkgname
532EOF
533 local i deps
534 deps="$depends"
535 if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
536 for i in $install ${triggers%%:*}; do
537 if head -n 1 "$startdir/$i" | grep '^#!/bin/sh' >/dev/null ; then
538 msg "Script found. busybox added as a dependency for $pkg"
539 deps="$deps busybox"
540 break
541 fi
542 done
543 fi
544
545 local last_commit="$(git_last_commit)$(git_dirty)"
546 if [ -n "$last_commit" ]; then
547 echo "commit = $last_commit" >> "$pkginfo"
548 fi
549
550 get_maintainer
551 if [ -n "$maintainer" ]; then
552 echo "maintainer = $maintainer" >> "$pkginfo"
553 fi
554
555 for i in $license; do
556 echo "license = $i" >> "$pkginfo"
557 done
558 for i in $replaces; do
559 echo "replaces = $i" >> "$pkginfo"
560 done
561 for i in $deps; do
562 echo "depend = $i" >> "$pkginfo"
563 done
564 for i in $conflicts; do
565 echo "conflict = $i" >> "$pkginfo"
566 done
567 for i in $provides; do
568 echo "provides = $i" >> "$pkginfo"
569 done
570 if [ -n "$triggers" ]; then
571 echo "triggers = ${triggers#*:}" >> "$pkginfo"
572 fi
573 if [ -n "$install_if" ]; then
574 echo "install_if = $(echo $install_if)" >> "$pkginfo"
575 fi
576
577 local metafiles=".PKGINFO"
578 for i in $install ${triggers%%:*}; do
579 script=${i#$name}
580 case "$script" in
581 .pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
582 msg "Adding $script"
583 ;;
584 *) error "$script: Invalid install/trigger script"
585 return 1
586 ;;
587 esac
588 cp "$startdir/$i" "$controldir/$script" || return 1
589 chmod +x "$controldir/$script"
590 metafiles="$metafiles $script"
591 done
592 echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
593}
594
595prepare_tracedeps() {
596 local dir=${subpkgdir:-$pkgdir}
597 [ "$arch" = "noarch" ] && return 0
598 options_has "!tracedeps" && return 0
599 # lets tell all the .so files this package provides in .provides-so
600 find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
601 >"$controldir"/.provides-so
602 # lets tell all the places we should look for .so files - all rpaths
603 scanelf -q -Rr "$dir" | sed -e 's/[[:space:]].*//' -e 's/:/\n/' \
604 | sort | uniq \
605 >"$controldir"/.rpaths
606 # now find the so dependencies
607 scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}' \
608 | sed 's:,:\n:g' | sort | uniq \
609 | while read i; do
610 # only add files that are not self provided
611 grep "^$i$" "$controldir"/.provides-so >/dev/null \
612 || echo $i >> "$controldir"/.needs-so
613 done
614}
615
616# check if dir has arch specific binaries
617dir_has_arch_binaries() {
618 local dir="$1"
619 # if scanelf returns something, then we have binaries
620 [ -n "$(scanelf -R "$dir" | head -n 1)" ] && return 0
621
622 # look for static *.a
623 [ -n "$(find "$dir" -type f -name '*.a' | head -n 1)" ] && return 0
624
625 return 1
626}
627
628# returns true if this is the -dev package
629is_dev_pkg() {
630 test "${subpkgname%-dev}" != "$subpkgname"
631}
632
633# check that noarch is set if needed
634archcheck() {
635 options_has "!archcheck" && return 0
636 if dir_has_arch_binaries "${subpkgdir:-$pkgdir}"; then
637 [ "$arch" != "noarch" ] && return 0
638 error "Arch specific binaries found so arch must not be set to \"noarch\""
639 return 1
640 elif [ "$arch" != "noarch" ] && ! is_dev_pkg; then
641 # we dont want -dev package go to noarch
642 warning "No arch specific binaries found so arch should probably be set to \"noarch\""
643 fi
644 return 0
645}
646
647prepare_package() {
648 msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
649 stripbin
650 prepare_metafiles && prepare_tracedeps || return 1
651 archcheck
652}
653
654pkginfo_val() {
655 local key="$1"
656 local file="$2"
657 awk -F ' = ' "\$1 == \"$key\" {print \$2}" "$file"
658}
659
660# find real path to so files
661real_so_path() {
662 local so="$1"
663 shift
664 while [ $# -gt 0 ]; do
665 [ -e "$1"/$so ] && realpath "$1/$so" && return 0
666 shift
667 done
668 error "$so: path not found"
669 return 1
670}
671
672# search rpaths and /usr/lib /lib for given so files
673find_so_files() {
674 local rpaths=$(cat "$1")
675 shift
676 while [ $# -gt 0 ]; do
677 real_so_path "$1" /usr/lib /lib $rpaths || return 1
678 shift
679 done
680 return 0
681}
682
683trace_apk_deps() {
684 local name="$1"
685 local dir="$2"
686 local i= j= found= autodeps= deppkgs= missing= so_paths= self_provided=
687 msg "Tracing dependencies for $name..."
688 # add pkgconfig if usr/lib/pkgconfig is found
689 if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
690 && ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
691 msg " added pkgconfig (found /usr/lib/pkgconfig)"
692 autodeps="$autodeps pkgconfig"
693 fi
694
695 # special case for libpthread: we need depend on libgcc
696 if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
697 && ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
698 autodeps="$autodeps libgcc"
699 msg " added libgcc (due to libpthread)"
700 fi
701 [ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
702 found=
703 # first check if its provide by same apkbuild
704 for j in "$dir"/../.control.*/.provides-so; do
705 grep -w "$i" "$j" >/dev/null || continue
706 found=${j%/.provides-so}
707 found=${found##*/.control.}
708 break
709 done
710 if [ -n "$found" ]; then
711 if ! list_has "$found" $self_provided; then
712 self_provided="$self_provided $found"
713 fi
714 else
715 missing="$missing $i"
716 fi
717 done
718
719 # find all packages that holds the so files
720 if [ -f "$dir"/.rpaths ]; then
721 so_files=$(find_so_files "$dir"/.rpaths $missing) || return 1
722 deppkgs=$($APK info -q -W $so_files) || return 1
723 fi
724
725 for found in $self_provided $deppkgs; do
726 if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
727 warning "You can remove '$found' from depends"
728 continue
729 fi
730 if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
731 autodeps="$autodeps $found"
732 msg " added $found"
733 fi
734 done
735
736 [ -z "$autodeps" ] && return 0
737 echo "# automatically detected:" >> "$dir"/.PKGINFO
738 for i in $autodeps; do
739 echo "depend = $i" >> "$dir"/.PKGINFO
740 done
741}
742
743create_apks() {
744 local file
745 getpkgver || return 1
746 mkdir -p "$PKGDEST"
747 for file in "$pkgbasedir"/.control.*/.PKGINFO; do
748 local dir="${file%/.PKGINFO}"
749 local name=$(pkginfo_val pkgname $file)
750 local ver=$(pkginfo_val pkgver $file)
751 local apk=$name-$ver.apk
752 local datadir="$pkgbasedir"/$name
753
754 trace_apk_deps "$name" "$dir" || return 1
755 msg "Creating $apk..."
756 (
757 cd "$datadir"
758 # data.tar.gz
759 set -- *
760 if [ "$1" = '*' ]; then
761 touch .dummy
762 set -- .dummy
763 fi
764 tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
765
766 # append the hash for data.tar.gz
767 local sha256=$(sha256sum "$dir"/data.tar.gz | cut -f1 -d' ')
768 echo "datahash = $sha256" >> "$dir"/.PKGINFO
769
770 # control.tar.gz
771 cd "$dir"
772 tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
773 | gzip -9 > control.tar.gz
774 abuild-sign -q control.tar.gz || exit 1
775
776 # create the final apk
777 cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
778 )
779 done
780}
781
782clean_abuildrepo() {
783 local apk
784 cd "$abuildrepo" || return 1
785
786 # remove compat symlink
787 for d in "$abuildrepo/$CARCH"; do
788 [ -L "$d" ] && rm "$d"
789 done
790
791 # remove broken links from abuildrepo
792 for apk in *.apk */*.apk; do
793 if [ -L "$apk" ] && [ ! -f "$apk" ]; then
794 rm -f "$apk"
795 fi
796 done
797}
798
799mklinks_abuildrepo() {
800 local apk
801 mkdir -p "$abuildrepo"/$CARCH
802 cd "$abuildrepo" || return 1
803 # create links for this package
804 for apk in $(listpkg); do
805 [ -f "$PKGDEST"/$apk ] || continue
806 ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk
807 done
808}
809
810update_abuildrepo() {
811 local d apk
812 if ! apk_up2date || [ -n "$force" ]; then
813 sanitycheck && builddeps && clean && fetch && unpack \
814 && prepare && mkusers && rootpkg || return 1
815 fi
816
817 clean_abuildrepo
818 mklinks_abuildrepo
819
820 cd "$abuildrepo"
821 local index=$CARCH/APKINDEX.tar.gz
822
823 msg "Updating the cached abuild repository index..."
824 local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
825 local oldindex=
826 if [ -f "$index" ]; then
827 oldindex="--index $index"
828 fi
829 $APK index --quiet $oldindex --output "$index".unsigned \
830 --description "$repo $(cd $startdir && git describe)" \
831 --rewrite-arch $CARCH \
832 $CARCH/*.apk || exit 1
833 msg "Signing the index..."
834 abuild-sign -q "$index".unsigned || exit 1
835 mv "$index".unsigned "$index"
836 chmod 644 "$index"
837}
838
839# predefined splitfunc doc
840default_doc() {
841 depends="$depends_doc"
842 install="$install_doc"
843 triggers="$triggers_doc"
844 pkgdesc="$pkgdesc (documentation)"
845 arch=${arch_doc:-"noarch"}
846
847 local i
848 for i in doc man info html sgml licenses gtk-doc; do
849 if [ -d "$pkgdir/usr/share/$i" ]; then
850 mkdir -p "$subpkgdir/usr/share"
851 mv "$pkgdir/usr/share/$i" "$subpkgdir/usr/share/"
852 fi
853 done
854
855 rm -f "$subpkgdir/usr/share/info/dir"
856
857# # compress info and man pages
858# find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
859# -o -name '*.[1-9]' \) -exec gzip {} \;
860
861 # remove if empty, ignore error (not empty)
862 rmdir "$pkgdir/usr/share" "$pkgdir/usr" 2>/dev/null
863
864# [ -d "$subpkgdir/usr/share/man" ] && depends="man"
865 return 0
866}
867
868doc() {
869 default_doc
870}
871
872# predefined splitfunc mod
873default_mod() {
874 depends="$kernel $depends_mod"
875 install="$install_mod"
876 for i in firmware modules; do
877 if [ -d "$pkgdir/lib/$i" ]; then
878 rm -rf "$subpkgdir/lib"
879 mkdir -p "$subpkgdir/lib"
880 mv "$pkgdir/lib/$i" "$subpkgdir/lib"
881 fi
882 done
883}
884
885mod() {
886 default_mod
887}
888
889# predefined splitfunc dev
890default_dev() {
891 local i= j=
892 depends="$pkgname $depends_dev"
893 install="$install_dev"
894 triggers="$triggers_dev"
895 pkgdesc="$pkgdesc (development files)"
896
897 for i in $origsubpackages; do
898 [ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
899 done
900
901 cd "$pkgdir" || return 0
902 for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
903 usr/share/gettext usr/bin/*-config \
904 usr/share/vala/vapi usr/share/gir-[0-9]*\
905 $(find -name include -type d) \
906 $(find usr/ -name '*.[acho]' -o -name '*.la' \
907 2>/dev/null); do
908 if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
909 d="$subpkgdir/${i%/*}" # dirname $i
910 mkdir -p "$d"
911 mv "$pkgdir/$i" "$d"
912 rmdir "$pkgdir/${i%/*}" 2>/dev/null
913 fi
914 done
915 # move *.so links needed when linking the apps to -dev packages
916 for i in lib/*.so usr/lib/*.so; do
917 if [ -L "$i" ]; then
918 mkdir -p "$subpkgdir"/"${i%/*}"
919 mv "$i" "$subpkgdir/$i" || return 1
920 fi
921 done
922 return 0
923}
924
925dev() {
926 default_dev
927}
928
929is_function() {
930 type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
931}
932
933do_fakeroot() {
934 if [ -n "$FAKEROOT" ]; then
935 $FAKEROOT -- "$@"
936 else
937 "$@"
938 fi
939}
940
941# build and package in fakeroot
942rootpkg() {
943 local do_build=build
944 cd "$startdir"
945 if is_function package; then
946 build || return 1
947 do_build=package
948 fi
949 cd "$startdir"
950 [ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
951 do_fakeroot "$abuild_path" $color_opt $do_build \
952 prepare_subpackages \
953 prepare_language_packs \
954 prepare_package \
955 create_apks
956}
957
958srcpkg() {
959 getpkgver || return 1
960 local p="$pkgname-$pkgver-$pkgrel"
961 local prefix="${startdir##*/}"
962 local i files="$prefix/APKBUILD"
963 for i in $source; do
964 files="$files $prefix/${i##*/}"
965 done
966 mkdir -p "$PKGDEST"
967 msg "Creating source package $p.src.tar.gz..."
968 (cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files)
969}
970
971# return true if arch is supported or noarch
972check_arch() {
973 list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
974}
975
976# check if package is up to date
977apk_up2date() {
978 getpkgver || return 1
979 local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
980 local i s
981 cd "$startdir"
982 for i in $pkgname $subpackages; do
983 [ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
984 done
985 [ -n "$keep" ] && return 0
986
987 for i in $source APKBUILD; do
988 local s
989 if is_remote "$i"; then
990 s="$SRCDEST/${i##*/}" # $(basename $i)
991 else
992 s="$startdir/${i##*/}"
993 fi
994 if [ "$s" -nt "$pkg" ]; then
995 return 1
996 fi
997 done
998 return 0
999}
1000
1001abuildindex_up2date() {
1002 local i
1003 getpkgver || return 1
1004 local dir="$abuildrepo"/$CARCH
1005 local apk="${pkgname%:*}-$pkgver-r$pkgrel.apk"
1006 local idx="$dir"/APKINDEX.tar.gz
1007 local file="$dir"/$apk
1008
1009 # check if index is missing
1010 [ -f "$idx" ] || return 1
1011
1012 # if link or file is missing, then we need update abuildrepo index
1013 [ -f "$file" ] || return 1
1014
1015 # if file exists and is newer than index, then we need update index
1016 [ "$file" -nt "$idx" ] && return 1
1017
1018 return 0
1019}
1020
1021up2date() {
1022 check_arch || return 0
1023 apk_up2date && abuildindex_up2date
1024}
1025
1026# rebuild package and abuildrepo index if needed
1027abuildindex() {
1028 up2date && return 0
1029 update_abuildrepo
1030}
1031
1032# source all APKBUILDs and output:
1033# 1) origin of package
1034# 2) all dependencies
1035# the output is i in a format easy parseable for awk
1036depparse_aports() {
1037 # lets run this in a subshell since we source all APKBUILD here
1038 (
1039 aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
1040 for i in $aportsdir/*/APKBUILD; do
1041 pkgname=
1042 subpackages=
1043 depends=
1044 makedepends=
1045 . $i
1046 dir=${i%/APKBUILD}
1047 deps=
1048 # filter out conflicts from deps and version info
1049 for j in $depends $makedepends; do
1050 case "$j" in
1051 !*) continue;;
1052 esac
1053 deps="$deps ${j%%[<>=]*}"
1054 done
1055 for j in $pkgname $subpackages; do
1056 echo "o ${j%%:*} $dir"
1057 set -- $deps
1058 echo -n "d ${j%%:*} $1"
1059 shift
1060 while [ $# -gt 0 ]; do
1061 echo -n ",$1"
1062 shift
1063 done
1064 echo
1065 done
1066 done
1067 )
1068}
1069
1070deptrace() {
1071 local deps= i=
1072 # strip versions from deps
1073 for i in "$@"; do
1074 deps="$deps ${i%%[<>=]*}"
1075 done
1076 [ -z "$deps" ] && return 0
1077 ( depparse_aports
1078 if [ -z "$upgrade" ]; then
1079 # list installed pkgs and prefix with 'i '
1080 $APK info -q | sort | sed 's/^/i /'
1081 fi
1082 ) | awk -v pkgs="$deps" '
1083
1084 function depgraph(pkg, a, i) {
1085 if (visited[pkg])
1086 return 0;
1087 visited[pkg] = 1;
1088 split(deps[pkg], a, ",");
1089 for (i in a)
1090 depgraph(a[i]);
1091 print pkg ":" origin[pkg];
1092
1093 }
1094
1095 $1 == "i" { visited[$2] = 1 }
1096 $1 == "o" { origin[$2] = $3 }
1097 $1 == "d" { deps[$2] = $3 }
1098 END {
1099 split(pkgs, pkgarray);
1100 for (i in pkgarray)
1101 depgraph(pkgarray[i]);
1102 }
1103 '
1104}
1105
1106# build and install dependencies
1107builddeps() {
1108 local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
1109 local filtered_deps= conflicts=
1110 [ -n "$nodeps" ] && return 0
1111 msg "Analyzing dependencies..."
1112
1113 # add depends unless it is a subpackage or package itself
1114 for i in $BUILD_BASE $depends $makedepends; do
1115 [ "$pkgname" = "${i%%[<>=]*}" ] && continue
1116 subpackages_has ${i%%[<>=]*} || deps="$deps $i"
1117 done
1118
1119 installed_deps=$($APK info -e $deps)
1120 # find which deps are missing
1121 for i in $deps; do
1122 if [ "${i#\!}" != "$i" ]; then
1123 $APK info -q -e "${i#\!}" \
1124 && conflicts="$conflicts ${i#\!}"
1125 elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
1126 missing="$missing $i"
1127 fi
1128 done
1129
1130 if [ -n "$conflicts" ]; then
1131 error "Conflicting package(s) installed:$conflicts"
1132 return 1
1133 fi
1134
1135 if [ -z "$install_deps" ] && [ -z "$recursive" ]; then
1136 # if we dont have any missing deps we are done now
1137 [ -z "$missing" ] && return 0
1138 error "Missing dependencies: $missing Use -r to autoinstall or -R to build"
1139 return 1
1140 fi
1141
1142 uninstall_after=".makedepends-$pkgname $uninstall_after"
1143 if [ -n "$install_deps" ] && [ -z "$recursive" ] && [ -n "$deps" ]; then
1144 # make a --simluate run first to detect missing deps
1145 # apk-tools --virtual is no goot at reporting those.
1146 $SUDO $APK add --repository "$abuildrepo" \
1147 $apk_opt_wait \
1148 --simulate --quiet $deps || return 1
1149 $SUDO $APK add --repository "$abuildrepo" \
1150 $apk_opt_wait \
1151 --virtual .makedepends-$pkgname $deps \
1152 && return 0
1153 fi
1154
1155 [ -z "$recursive" ] && return 1
1156
1157 # find dependencies that are installed but missing in repo.
1158 for i in $deps; do
1159 local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
1160 if [ -z "$m" ]; then
1161 missing="$missing $i"
1162 fi
1163 done
1164
1165 for i in $(deptrace $missing); do
1166 # i = pkg:dir
1167 local dir=${i#*:}
1168 local pkg=${i%:*}
1169
1170 # ignore if dependency is in other repo
1171 [ -d "$dir" ] || continue
1172
1173 # break cricular deps
1174 list_has $pkg $ABUILD_VISITED && continue
1175 export ABUILD_VISITED="$ABUILD_VISITED $pkg"
1176
1177 msg "Entering $dir"
1178 cd "$dir" && $0 $forceroot $keep $quiet $install_deps \
1179 $recursive $upgrade $color_opt abuildindex || return 1
1180 done
1181 $SUDO $APK add -u --repository "$abuildrepo" \
1182 $apk_opt_wait \
1183 --virtual .makedepends-$pkgname $deps
1184}
1185
1186# replace the md5sums in the APKBUILD
1187checksum() {
1188 local s files
1189 [ -z "$source" ] && return 0
1190 fetch
1191 msg "Updating the md5sums in APKBUILD..."
1192 for s in $source; do
1193 files="$files ${s##*/}"
1194 done
1195 md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
1196 sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
1197 echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
1198}
1199
1200stripbin() {
1201 local bin
1202 if options_has "!strip" || [ "$arch" = "noarch" ]; then
1203 return 0
1204 fi
1205 cd "${subpkgdir:-$pkgdir}" || return 1
1206 msg "Stripping binaries"
1207 scanelf --recursive --nobanner --etype "ET_DYN,ET_EXEC" . \
1208 | sed -e 's:^ET_DYN ::' -e 's:^ET_EXEC ::' \
1209 | xargs -r strip
1210}
1211
1212# simply list target apks
1213listpkg() {
1214 local name
1215 getpkgver || return 1
1216 for name in $(listpkgnames) ; do
1217 echo "$name-$pkgver-r$pkgrel.apk"
1218 done
1219}
1220
1221source_has() {
1222 local i
1223 for i in $source; do
1224 [ "$1" = "${i##*/}" ] && return 0
1225 done
1226 return 1
1227}
1228
1229subpackages_has() {
1230 local i
1231 for i in $subpackages; do
1232 [ "$1" = "${i%:*}" ] && return 0
1233 done
1234 return 1
1235}
1236
1237list_has() {
1238 local needle="$1"
1239 local i
1240 shift
1241 for i in $@; do
1242 [ "$needle" = "$i" ] && return 0
1243 [ "$needle" = "!$i" ] && return 1
1244 done
1245 return 1
1246}
1247
1248# same as list_has but we filter version info
1249deplist_has() {
1250 local needle="$1"
1251 local i
1252 shift
1253 for i in $@; do
1254 i=${i%%[<>=]*}
1255 [ "$needle" = "$i" ] && return 0
1256 [ "$needle" = "!$i" ] && return 1
1257 done
1258 return 1
1259}
1260
1261options_has() {
1262 list_has "$1" $options
1263}
1264
1265depends_has() {
1266 deplist_has "$1" $depends
1267}
1268
1269makedepends_has() {
1270 deplist_has "$1" $makedepends
1271}
1272
1273md5sums_has() {
1274 list_has "$1" $md5sums
1275}
1276
1277install_has() {
1278 list_has "$1" $install
1279}
1280
1281# install package after build
1282post_add() {
1283 getpkgver || return 1
1284 local pkgf="$PKGDEST/$1-$pkgver-r$pkgrel.apk"
1285 local deps i
1286 if ! subpackages_has $1 && [ "$1" != "$pkgname" ]; then
1287 die "$1 is not built by this APKBUILD"
1288 fi
1289 # recursively install dependencies that are provided by this APKBUILD
1290 deps=$($APK index "$pkgf" 2>/dev/null | awk -F: '$1=="D" { print $2 }')
1291 for i in $deps; do
1292 if subpackages_has $i || [ "$i" = "$pkgname" ]; then
1293 post_add $i || return 1
1294 fi
1295 done
1296 $SUDO $APK add $apk_opt_wait -u "$pkgf" || die "Failed to install $1"
1297}
1298
1299installdeps() {
1300 local deps i
1301 $SUDO $APK add $apk_opt_wait --repository "$abuildrepo" \
1302 --virtual .makedepends-$pkgname \
1303 $makedepends
1304}
1305
1306uninstalldeps (){
1307 $SUDO $APK del $apk_opt_wait .makedepends-$pkgname
1308}
1309
1310all() {
1311 if ! [ -n "$force" ]; then
1312 check_arch || return 0
1313 fi
1314 if up2date && [ -z "$force" ]; then
1315 msg "Package is up to date"
1316 else
1317 update_abuildrepo
1318 fi
1319}
1320
1321usage() {
1322 echo "$program $abuild_ver"
1323 echo "usage: $program [options] [-i PKG] [-P REPODEST] [-p PKGDEST]"
1324 echo " [-s SRCDEST] [cmd] ..."
1325 echo " $program [-c] -n PKGNAME[-PKGVER]"
1326 echo "Options:"
1327 echo " -c Enable colored output"
1328 echo " -d Disable dependency checking"
1329 echo " -f Force specified cmd, even if they are already done"
1330 echo " -F Force run as root"
1331 echo " -h Show this help"
1332 echo " -i Install PKG after successul build"
1333 echo " -k Keep built packages, even if APKBUILD or sources are newer"
1334 echo " -m Disable colors (monochrome)"
1335 echo " -p Set package destination directory"
1336 echo " -P Set PKGDEST to REPODEST/<repo>/\$CARCH, where repo is the parents dir name"
1337 echo " -q Quiet"
1338 echo " -r Install missing dependencies from system repository (using sudo)"
1339 echo " -R Recursively build and install missing dependencies (using sudo)"
1340 echo " -s Set source package destination directory"
1341 echo " -u Recursively build and upgrade all dependencies (using sudo)"
1342 echo ""
1343 echo "Commands:"
1344 echo " checksum Generate checksum to be included in APKBUILD"
1345 echo " fetch Fetch sources to \$SRCDEST and verify checksums"
1346 echo " sanitycheck Basic sanity check of APKBUILD"
1347 echo " md5check Check md5sums"
1348 echo " unpack Unpack sources to \$srcdir"
1349 echo " build Compile and install package into \$pkgdir"
1350 echo " listpkg List target packages"
1351 echo " package Create package in \$PKGDEST"
1352 echo " rootpkg Run '$0 build package' as fakeroot"
1353 echo " clean Remove temp build and install dirs"
1354 echo " cleanoldpkg Remove binary packages except current version"
1355 echo " cleanpkg Remove already built binary and source package"
1356 echo " cleancache Remove downloaded files from \$SRCDEST"
1357 echo " srcpkg Make a source package"
1358 echo " sourcecheck Check if remote source package exists upstream"
1359 echo " up2date Compare target and sources dates"
1360 echo " installdeps Install packages listed in makedepends and depends"
1361 echo " uninstalldeps Uninstall packages listed in makedepends and depends"
1362 echo ""
1363 exit 0
1364}
1365
1366APKBUILD="${APKBUILD:-./APKBUILD}"
1367unset force
1368unset recursive
1369while getopts "cdfFhi:kimnp:P:qrRs:u" opt; do
1370 case $opt in
1371 'c') default_colors
1372 color_opt="-c";;
1373 'd') nodeps=1;;
1374 'f') force="-f";;
1375 'F') forceroot="-F";;
1376 'h') usage;;
1377 'i') install_after="$install_after $OPTARG";;
1378 'k') keep="-k";;
1379 'm') monochrome
1380 color_opt="-m";;
1381 'n') die "Use newapkbuild to create new aports";;
1382 'p') PKGDEST=$OPTARG;;
1383 'P') REPODEST=$OPTARG;;
1384 'q') quiet="-q";;
1385 'r') install_deps="-r";;
1386 'R') recursive="-R";;
1387 's') SRCDEST=$OPTARG;;
1388 'u') upgrade="-u"
1389 recursive="-R";;
1390 esac
1391done
1392shift $(( $OPTIND - 1 ))
1393
1394# check so we are not root
1395if [ "$(whoami)" = "root" ] && [ -z "$FAKEROOTKEY" ]; then
1396 [ -z "$forceroot" ] && die "Do not run abuild as root"
1397 SUDO=
1398 FAKEROOT=
1399fi
1400
1401# find startdir
1402[ -f "$APKBUILD" ] || die "Could not find $APKBUILD (PWD=$PWD)"
1403APKBUILD=$(readlink -f "$APKBUILD")
1404
1405startdir="${APKBUILD%/*}"
1406srcdir=${srcdir:-"$startdir/src"}
1407pkgbasedir=${pkgbasedir:-"$startdir/pkg"}
1408pkgrel=0
1409repo=${startdir%/*}
1410repo=${repo##*/}
1411abuildrepo="$abuildrepo_base"/$repo
1412
1413SRCDEST=${SRCDEST:-$startdir}
1414PKGDEST=${PKGDEST:-$startdir}
1415
1416cd "$startdir" || die
1417. "$APKBUILD"
1418
1419# If REPODEST is set then it will override the PKGDEST
1420if [ -n "$REPODEST" ]; then
1421 PKGDEST="$REPODEST/$repo/$CARCH"
1422 # for recursive action
1423 export REPODEST
1424fi
1425
1426# If we are handling a sub package then reset subpackages and install
1427if [ -n "$subpkgname" ]; then
1428 origsubpackages="$subpackages"
1429 subpackages=
1430 install=
1431fi
1432pkgdir="$pkgbasedir/$pkgname"
1433controldir="$pkgbasedir"/.control.${subpkgname:-$pkgname}
1434
1435trap 'die "Aborted by user"' INT
1436set_xterm_title "abuild: $pkgname"
1437
1438if [ -z "$1" ]; then
1439 set all
1440fi
1441
1442while [ $# -gt 0 ]; do
1443 runpart $1
1444 shift
1445done
1446
1447for i in $install_after; do
1448 post_add $i
1449done
1450
1451cleanup
1452