abuild.in 34.9 KB
Newer Older
Natanael Copa's avatar
Natanael Copa committed
1 2
#!/bin/sh

3
# script to build apk packages (light version of makepkg)
Natanael Copa's avatar
Natanael Copa committed
4 5 6 7 8 9 10
# Copyright (c) 2008 Natanael Copa <natanael.copa@gmail.com>
#
# Distributed under GPL-2
#
# Depends on: busybox utilities, fakeroot, 
#

Natanael Copa's avatar
Natanael Copa committed
11 12
abuild_ver=@VERSION@
sysconfdir=@sysconfdir@
13
abuildrepo_base=@abuildrepo@
Natanael Copa's avatar
Natanael Copa committed
14
datadir=@datadir@
Natanael Copa's avatar
Natanael Copa committed
15

16
program=${0##*/}
17
abuild_path=$(readlink -f $0)
Natanael Copa's avatar
Natanael Copa committed
18 19

# defaults
20
BUILD_BASE="build-base"
21
SUDO=${SUDO:-"sudo"}
22
FAKEROOT=${FAKEROOT:-"fakeroot"}
23
APK=${APK:-apk}
24
apk_opt_wait="--wait 30"
25

Natanael Copa's avatar
Natanael Copa committed
26
# read config
Natanael Copa's avatar
Natanael Copa committed
27
ABUILD_CONF=${ABUILD_CONF:-"$sysconfdir/abuild.conf"}
Natanael Copa's avatar
Natanael Copa committed
28 29
[ -f "$ABUILD_CONF" ] && . "$ABUILD_CONF"

30
default_colors() {
Natanael Copa's avatar
Natanael Copa committed
31 32 33 34 35 36
	NORMAL="\033[1;0m"
	STRONG="\033[1;1m"
	RED="\033[1;31m"
	GREEN="\033[1;32m"
	YELLOW="\033[1;33m"
	BLUE="\033[1;34m"
37 38 39 40 41 42 43 44 45 46 47 48 49 50
}

monochrome() {
	NORMAL=""
	STRONG=""
	RED=""
	GREEN=""
	YELLOW=""
	BLUE=""
}

#colors
if [ -n "$USE_COLORS" ]; then
    default_colors
Natanael Copa's avatar
Natanael Copa committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
fi	
	

# functions
msg() {
	local prompt="$GREEN>>>${NORMAL}"
	local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
	local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
	[ -z "$quiet" ] && printf "${prompt} ${name}${fake}: $@\n" >&2
}

warning() {
	local prompt="${YELLOW}>>> WARNING:${NORMAL}"
	local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
	local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
	printf "${prompt} ${name}${fake}: $@\n" >&2
}

error() {
	local prompt="${RED}>>> ERROR:${NORMAL}"
	local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
	local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
	printf "${prompt} ${name}${fake}: $@\n" >&2
}
Natanael Copa's avatar
Natanael Copa committed
75 76

	
Linux User's avatar
Linux User committed
77
set_xterm_title() {
78
	if [ "$TERM" = xterm ] && [ -n "$USE_COLORS" ]; then
79
		 printf "\033]0;$1\007" >&2
Linux User's avatar
Linux User committed
80 81 82 83 84
	fi
} 

cleanup() {
	set_xterm_title ""
85
	if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
86 87
		msg "Uninstalling dependencies..."
		$SUDO $APK del --quiet $apk_opt_wait $uninstall_after
88
	fi
Linux User's avatar
Linux User committed
89 90
}

Natanael Copa's avatar
Natanael Copa committed
91
die() {
Linux User's avatar
Linux User committed
92
	error "$@"
Linux User's avatar
Linux User committed
93
	cleanup
Natanael Copa's avatar
Natanael Copa committed
94 95 96
	exit 1
}

97 98
# check if apkbuild is basicly sane
sanitycheck() {
99
	local i= j= suggestion=
100
	msg "Checking sanity of $APKBUILD..."
101 102 103
	[ -z "$pkgname" ] && die "Missing pkgname in APKBUILD"
	[ -z "${pkgname##* *}" ] && die "pkgname contains spaces"
	[ -z "$pkgver" ] && die "Missing pkgver in APKBUILD"
104
	if [ "$pkgver" != "volatile" ] && [ -z "$nodeps" ]; then
105
		$APK version --check -q "$pkgver" ||\
Natanael Copa's avatar
Natanael Copa committed
106 107
			die "$pkgver is not a valid version"
	fi
108
	[ -z "$pkgrel" ] && die "Missing pkgrel in APKBUILD"
109 110 111 112
	[ -z "$pkgdesc" ] && die "Missing pkgdesc in APKBUILD"
	[ -z "$url" ] && die "Missing url in APKBUILD"
	[ -z "$license" ] && die "Missing license in APKBULID"

113 114 115 116 117 118 119
	# check so no package names starts with -
	for i in $pkgname $subpackages; do
		case $i in
		-*) die "${i%:*} is not a valid package name";;
		esac
	done

120 121 122 123 124 125 126 127 128 129
	# check if CARCH, CBUILD, CHOST and CTARGET is set
	if [ -z "$CARCH" ]; then
		case "$(uname -m)" in
		i[0-9]86) suggestion=" (Suggestion: CARCH=x86)";;
		x86_64) suggestion=" (Suggestion: CARCH=x86_64)";;
		esac
		die "Please set CARCH in /etc/abuild.conf$suggestion"
	fi
	[ -z "$CHOST" ] && die "Please set CHOST in /etc/abuild.conf"

Natanael Copa's avatar
Natanael Copa committed
130
	for i in $install; do
131
		local n=${i%.*}
132
		local suff=${i##*.}
133 134 135 136 137
		case "$suff" in
		pre-install|post-install|pre-upgrade|post-upgrade|pre-deinstall|post-deinstall);;
		*) die "$i: unknown install script suffix"
		esac
		if ! subpackages_has "$n" && [ "$n" != "$pkgname" ]; then
138
			die "$i: install script does not match pkgname or any subpackage"
139
		fi
140
		[ -e "$startdir/$i" ] || die "install script $i is missing"
Natanael Copa's avatar
Natanael Copa committed
141 142
	done
	
143 144 145 146 147 148 149 150
	for i in $triggers; do
		local f=${i%=*}
		local p=${f%.trigger}
		[ "$f" = "$i" ] && die "$f: triggers must contain '='" 
		[ "$p" = "$f" ] && die "$f: triggers scripts must have .trigger suffix"
		if ! subpackages_has "$p" && [ "$p" != "$pkgname" ]; then
			die "$p: trigger script does not match pkgname or any subpackage"
		fi
Natanael Copa's avatar
Natanael Copa committed
151

152 153
		[ -e "$startdir"/$f ] || die "trigger script $f is missing"
	done
154 155
	if [ -n "$source" ]; then
		for i in $source; do
Natanael Copa's avatar
Natanael Copa committed
156 157 158 159
			if install_has "$i"; then
				warning "You should not have \$install in source"
				continue
			fi
160
			md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
Natanael Copa's avatar
Natanael Copa committed
161 162 163
			case "$i" in
				https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
			esac
164 165
		done
	fi
Natanael Copa's avatar
Natanael Copa committed
166

167 168
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
169
			source_has $i || die "$i exists in md5sums but is missing in source"
170 171
		done
	fi
172

173 174 175
	# common spelling errors
	[ -n "$depend" ] && die "APKBUILD contains 'depend'. It should be depends"
	[ -n "$makedepend" ] && die "APKBUILD contains 'makedepend'. It should be makedepends"
Natanael Copa's avatar
Natanael Copa committed
176 177

	grep '^# Maintainer:' $APKBUILD >/dev/null || warning "No maintainer"
178 179

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
180 181 182 183
	return 0
}

md5check() {
184
	local dummy f endreturnval originalparams origin file
185 186 187
	if [ -z "$source" ]; then
		return 0
	fi
188
	if [ -z "$md5sums" ]; then
189
		die "Use 'abuild checksum' to generate/update the checksum(s)"
190
	fi
191 192 193
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
194
	fetch || return 1
195
	msg "Checking md5sums..."
Natanael Copa's avatar
Natanael Copa committed
196
	cd "$srcdir" || return 1
197 198 199 200 201 202 203 204
	IFS=$'\n'
	endreturnval=0
	originalparams=$@
	set -- $source
	for src in $md5sums; do
		origin=$1; shift
		echo "$src" | md5sum -c
		if [ $? -ne 0 ]; then
205
			endreturnval=1
206 207 208 209 210 211 212 213 214 215 216
			is_remote $origin || continue
			echo "Because the remote file above failed the md5sum check it will be deleted."
			echo "Rebuilding will cause it to re-download which in some cases may fix the problem."
			file=`echo "$src" | sed 's/.*[ \t\n]\(.*\)/\1/'`
			echo "Deleting: $file"
			rm $file
		fi
	done
	unset IFS
	set -- $originalparams
	return $endreturnval
Natanael Copa's avatar
Natanael Copa committed
217 218
}

Natanael Copa's avatar
Natanael Copa committed
219 220
# verify upstream sources
sourcecheck() {
221 222 223 224 225 226 227 228 229 230
	local uri
	for uri in $source; do
		is_remote $uri || continue
		case "$uri" in
		saveas-*://*)
			uri=${uri#saveas-}
			uri=${uri%/*}
			;;
		esac
		wget -q -s "$uri" || return 1
Natanael Copa's avatar
Natanael Copa committed
231
	done
232
	return 0
Natanael Copa's avatar
Natanael Copa committed
233
}
234

Natanael Copa's avatar
Natanael Copa committed
235 236
uri_fetch() {
	local uri="$1"
237
	local d="${uri##*/}"	# $(basename $uri)
238 239 240 241
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

242 243 244 245 246
	# fix saveas-*://* URIs
	case "$uri" in
		# remove 'saveas-' from beginning and
		# '/filename' from end of URI
		saveas-*://*) uri="${uri:7:$(expr ${#uri} - 7 - ${#d} - 1)}";;
Natanael Copa's avatar
Natanael Copa committed
247
	esac
248 249 250 251 252

	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
Natanael Copa's avatar
Natanael Copa committed
253
	
Natanael Copa's avatar
Natanael Copa committed
254
	mkdir -p "$SRCDEST"
255 256 257
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
258
	fi
Linux User's avatar
Linux User committed
259
	msg "Fetching $uri"
260 261
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
262 263
}

264 265
is_remote() {
	case "$1" in
266
		http://*|ftp://*|https://*|saveas-*://*)
267 268 269 270 271
			return 0;;
	esac
	return 1
}

272 273 274 275 276
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
277 278 279 280 281
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
282 283 284 285
	fi
	uri_fetch "$uri"
}

286
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
287 288
	local s
	mkdir -p "$srcdir"
289 290
	for s in $source; do
		if is_remote "$s"; then
291
			uri_fetch_mirror "$s" || return 1
292 293 294 295
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
296 297 298
	done
}

299 300 301 302
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
# verify that all init.d scripts are openrc runscripts
initdcheck() {
	local i
	for i in $source; do
		case $i in
		*.initd)
			head -n 1 "$srcdir"/$i | grep -q '/sbin/runscript' \
				&& continue
			error "$i is not an openrc #!/sbin/runscript"
			return 1
			;;
		esac
	done			
}

Natanael Copa's avatar
Natanael Copa committed
318
# unpack the sources
319
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
320
	local u
321 322
	if [ -z "$force" ]; then
		md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
323
		initdcheck || return 1
324
	fi
Natanael Copa's avatar
Natanael Copa committed
325 326 327 328
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
329
			*.tar.gz|*.tgz)
330
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
331 332
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
333
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
334
				tar -C "$srcdir" -jxf "$s" || return 1;;
335 336 337 338
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
339 340 341
			*.tar.xz)
				msg "Unpacking $s..."
				unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
Natanael Copa's avatar
Natanael Copa committed
342 343 344
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
345 346 347 348
		esac
	done
}

349 350 351 352
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
353 354
# cleanup source and package dir
clean() {
355
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
356
	rm -rf "$srcdir"
357
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
358 359 360 361 362 363
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
364 365 366 367
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
368 369 370
	done
}

371 372 373 374 375 376 377 378 379 380
listpkgnames() {
	local i
	for i in $pkgname $subpackages; do
		echo ${i%:*}
	done
	for i in $linguas; do
		echo $pkgname-lang-$i
	done
}

Natanael Copa's avatar
Natanael Copa committed
381 382
cleanpkg() {
	local i
383
	getpkgver || return 1
384
	msg "Cleaning built packages..."
385
	for i in $(listpkgnames); do
386 387
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
388
			"$abuildrepo"/$p.apk "$abuildrepo"/*/$p.apk
Natanael Copa's avatar
Natanael Copa committed
389
	done
390
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
391 392
}

393 394 395
# clean all packages except current
cleanoldpkg() {
	local i j
396
	getpkgver || return 1
397
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
398
	for i in $(listpkgnames); do
399 400 401 402 403
		local pn=${i%:*}
		for j in "$PKGDEST"/$pn-[0-9]*.apk ; do
			[ "$j" = "$PKGDEST/$pn-$pkgver-r$pkgrel.apk" ] \
				&& continue
			rm -f "$j" "$abuildrepo"/*/${j##*/}
404 405 406 407
		done
	done
	return 0
}
408 409 410 411 412

mkusers() {
	local i
	for i in $pkgusers; do
		if ! getent passwd $i >/dev/null; then
413
			local gopt=
414
			msg "Creating user $i"
415 416 417 418
			if getent group $i >/dev/null; then
				gopt="-G $i"
			fi
			$SUDO adduser -D -H $gopt $i || return 1
419 420 421 422 423 424 425 426 427 428 429
		fi
	done
	for i in $pkggroups; do
		if ! getent group $i >/dev/null; then
			msg "Creating group $i"
			$SUDO addgroup $i || return 1
		fi
	done
}


Natanael Copa's avatar
Natanael Copa committed
430 431
runpart() {
	local part=$1
432
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
433 434 435 436
	$part || die "$part failed"
}

# override those in your build script
437 438 439 440 441 442 443 444
getpkgver() {
	# this func is supposed to be overridden by volatile packages
	if [ "$pkgver" = "volatile" ]; then
		error "Please provide a getpkgver() function in your APKBUILD"
		return 1
	fi
}

Natanael Copa's avatar
Natanael Copa committed
445 446 447 448
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
449
build() {
450
	:
Natanael Copa's avatar
Natanael Copa committed
451 452
}

453 454 455
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
Natanael Copa's avatar
Natanael Copa committed
456
	mkdir -p "$PKGDEST"
457 458 459
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

Natanael Copa's avatar
Natanael Copa committed
460 461 462 463 464 465 466 467 468
get_split_func() {
	# get the 'func' from "sub-pkg:func"
	local func=${1##*:}

	# get 'func' from "sub-pkg-func" if there was no :func
	[ "$func" = "$1" ] && func=${func##*-}
	echo $func
}

469
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
470 471
	local i
	cd "$startdir"
472
	for i in $subpackages; do
Natanael Copa's avatar
Natanael Copa committed
473 474
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
475
		msg "Running split function $func..."
476
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
477
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
478
	done
479 480 481 482 483 484 485
	# post check
	i=$(find "$pkgdir" -name '*.la')
	if [ -n "$i" ] && ! options_has "libtool"; then
		error "Libtool archives (*.la) files found and \$options has no 'libtool' flag:"
		echo "$i" | sed 's/^/  /'
		return 1
	fi
486 487 488 489
	# post check for /usr/share/locale
	if [ -d "$pkgdir"/usr/share/locale ]; then
		warning "Found /usr/share/local. Maybe add \$pkgname-lang to subpackages?"
	fi
Natanael Copa's avatar
Natanael Copa committed
490 491
}

492
default_lang() {
493
	pkgdesc="Languages for package $pkgname"
494 495 496 497 498 499 500 501 502 503 504 505 506
	install_if="$pkgname=$pkgver-r$pkgrel lang"
	arch="noarch"
	local dir
	for dir in ${langdir:-/usr/share/locale}; do
		mkdir -p "$subpkgdir"/${dir%/*}
		mv "$pkgdir"/"$dir" "$subpkgdir"/"$dir" || return 1
	done
}

lang() {
	default_lang
}

507
default_lang_subpkg() {
508 509 510 511
	if [ -z "$lang" ]; then
		error "lang is not set"
		return 1
	fi
512
	pkgdesc="$pkgname language pack for $lang"
513 514
	arch="noarch"
	install_if="$pkgname=$pkgver-r$pkgrel lang-$lang"
515 516 517 518 519
	local dir
	for dir in ${langdir:-/usr/share/locale}; do
		mkdir -p "$subpkgdir"/$dir
		mv "$pkgdir"/$dir/$lang* \
		"$subpkgdir"/$dir/ \
520
		|| return 1
521 522 523 524 525
	done
}

lang_subpkg() {
	default_lang_subpkg
526 527 528 529 530 531 532 533 534 535 536
}

prepare_language_packs() {
	for lang in $linguas; do
		lang="$lang" \
		subpkgname="$pkgname-lang-$lang" \
		subpkgdir="$pkgbasedir"/$subpkgname \
			$0 lang_subpkg prepare_package || return 1
	done
}

537 538 539 540 541 542 543 544 545 546 547 548
# echo '-dirty' if git is not clean
git_dirty() {
	if [ $(git status -s "$startdir" | wc -l) -ne 0 ]; then
		echo "-dirty"
	fi
}

# echo last commit hash id
git_last_commit() {
	git log --format=oneline -n 1 "$startdir" | awk '{print $1}'
}

549 550 551 552 553 554
get_maintainer() {
	if [ -z "$maintainer" ]; then
		maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
	fi
}

555
prepare_metafiles() {
556
	getpkgver || return 1
557 558
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
559
	local dir=${subpkgdir:-$pkgdir}
560
	local pkg="$name-$pkgver-r$pkgrel.apk"
561
	local pkginfo="$controldir"/.PKGINFO
562
	local sub
Natanael Copa's avatar
Natanael Copa committed
563 564 565
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
566
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
567 568
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
569
	local parch="$CARCH"
570 571 572 573 574

	# we need to wait with setting noarch til our build infra can handle it
#	if [ "$arch" = "noarch" ]; then
#		parch="noarch"
#	fi
Natanael Copa's avatar
Natanael Copa committed
575

576
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
577
	if [ -n "$FAKEROOTKEY" ]; then
578
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
579
	fi
580 581
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
582
pkgname = $name
583
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
584 585 586 587 588
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
Natanael Copa's avatar
Natanael Copa committed
589
arch = $parch
Natanael Copa's avatar
Natanael Copa committed
590
origin = $pkgname
Natanael Copa's avatar
Natanael Copa committed
591
EOF
592 593
	local i deps
	deps="$depends"
594
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
595 596
		for i in $install $triggers; do
			local s=${i%=*}
597
			[ "$name" != "${s%.*}" ] && continue
598
			if head -n 1 "$startdir/$s" | grep '^#!/bin/sh' >/dev/null ; then
599 600 601 602 603
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
604 605
	fi
	
606 607 608 609 610 611 612 613 614 615
	local last_commit="$(git_last_commit)$(git_dirty)"
	if [ -n "$last_commit" ]; then
		echo "commit = $last_commit" >> "$pkginfo"
	fi

	get_maintainer
	if [ -n "$maintainer" ]; then
		echo "maintainer = $maintainer" >> "$pkginfo"
	fi

Natanael Copa's avatar
Natanael Copa committed
616
	for i in $license; do
617
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
618 619
	done
	for i in $replaces; do
620
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
621
	done
622
	for i in $deps; do
623
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
624 625
	done
	for i in $conflicts; do
626
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
627 628
	done
	for i in $provides; do
629
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
630
	done
631 632 633 634 635 636
	for i in $triggers; do
		local f=${i%=*}
		local dirs=${i#*=}
		[ "${f%.trigger}" != "$name" ] && continue
		echo "triggers = ${dirs//:/ }" >> "$pkginfo"
	done
637 638 639
	if [ -n "$install_if" ]; then
		echo "install_if = $(echo $install_if)" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
640 641

	local metafiles=".PKGINFO"
642 643 644
	for i in $install $triggers; do
		local f=${i%=*}
		local n=${f%.*}
645
		if [ "$n" != "$name" ]; then
646 647
			continue
		fi
648
		script=${f#$name}
649
		msg "Adding $script"
650
		cp "$startdir/$f" "$controldir/$script" || return 1
651
		chmod +x "$controldir/$script"
652 653
		metafiles="$metafiles $script"
	done
654
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
655
}
656

657 658
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
659
	[ "$arch" = "noarch" ] && return 0
660
	options_has "!tracedeps" && return 0
661
	# lets tell all the .so files this package provides in .provides-so
662 663
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
664 665 666 667 668
	# lets tell all the places we should look for .so files - all rpaths
	scanelf -q -Rr "$dir" | sed -e 's/[[:space:]].*//' -e 's/:/\n/' \
		| sort | uniq \
		>"$controldir"/.rpaths
	# now find the so dependencies
669
	scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
670 671 672
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
673 674
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
675
	done
Natanael Copa's avatar
Natanael Copa committed
676 677
}

678 679 680 681 682 683 684 685 686 687 688 689
# check if dir has arch specific binaries
dir_has_arch_binaries() {
	local dir="$1"
	# if scanelf returns something, then we have binaries
	[ -n "$(scanelf -R "$dir" | head -n 1)" ] && return 0

	# look for static *.a
	[ -n "$(find "$dir" -type f -name '*.a' | head -n 1)" ] && return 0

	return 1
}

690 691 692 693 694
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

695 696 697 698 699 700 701
# check that noarch is set if needed
archcheck() {
	options_has "!archcheck" && return 0
	if dir_has_arch_binaries "${subpkgdir:-$pkgdir}"; then
		[ "$arch" != "noarch" ] && return 0
		error "Arch specific binaries found so arch must not be set to \"noarch\""
		return 1
702 703 704
	elif [ "$arch" != "noarch" ] && ! is_dev_pkg; then
		# we dont want -dev package go to noarch
		warning "No arch specific binaries found so arch should probably be set to \"noarch\""
705 706 707 708
	fi
	return 0
}

709
prepare_package() {
710
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
711
	stripbin
712 713
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
714 715 716 717 718 719 720 721
}

pkginfo_val() {
	local key="$1"
	local file="$2"
	awk -F ' = ' "\$1 == \"$key\" {print \$2}" "$file"
}

722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
# find real path to so files
real_so_path() {
	local so="$1"
	shift
	while [ $# -gt 0 ]; do
		[ -e "$1"/$so ] && realpath "$1/$so" && return 0
		shift
	done
	error "$so: path not found"
	return 1
}

# search rpaths and /usr/lib /lib for given so files
find_so_files() {
	local rpaths=$(cat "$1")
	shift
	while [ $# -gt 0 ]; do
		real_so_path "$1" /usr/lib /lib $rpaths || return 1
		shift
	done
	return 0
}

745 746 747
trace_apk_deps() {
	local name="$1"
	local dir="$2"
Natanael Copa's avatar
Natanael Copa committed
748
	local i= j= found= autodeps= deppkgs= missing= so_paths= self_provided=
749
	msg "Tracing dependencies for $name..."
750 751 752
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
753
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
754 755 756 757
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
758
	if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
759 760
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
761
		msg "  added libgcc (due to libpthread)"
762
	fi
763
	[ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
764
		found=
765
		# first check if its provide by same apkbuild
766
		for j in "$dir"/../.control.*/.provides-so; do
767
			grep -w "$i" "$j" >/dev/null || continue
768
			found=${j%/.provides-so}
769
			found=${found##*/.control.}
770
			break
771
		done
772 773 774 775 776 777
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
778
		fi
779 780 781
	done

	# find all packages that holds the so files
782 783 784 785 786
	if [ -f "$dir"/.rpaths ]; then
		so_files=$(find_so_files "$dir"/.rpaths $missing) || return 1
		deppkgs=$($APK info -q -W $so_files) || return 1
	fi

787
	for found in $self_provided $deppkgs; do
788
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
789 790 791
			warning "You can remove '$found' from depends"
			continue
		fi
792 793
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
794
			msg "  added $found"
795
		fi
796
	done
797

798 799 800 801 802 803 804
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

805 806
create_apks() {
	local file
807
	getpkgver || return 1
Natanael Copa's avatar
Natanael Copa committed
808
	mkdir -p "$PKGDEST"
809
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
810 811 812
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
813
		local apk=$name-$ver.apk
814
		local datadir="$pkgbasedir"/$name
815 816

		trace_apk_deps "$name" "$dir" || return 1
817
		msg "Creating $apk..."
818
		(
819 820
		cd "$datadir"
		# data.tar.gz
821 822 823 824 825 826
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
827 828 829 830 831 832

		# append the hash for data.tar.gz
		local sha256=$(sha256sum "$dir"/data.tar.gz | cut -f1 -d' ')
		echo "datahash = $sha256" >> "$dir"/.PKGINFO

		# control.tar.gz
833
		cd "$dir"
834 835
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
836
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
837

838 839 840
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
841 842 843
	done
}

844
clean_abuildrepo() {
845
	local apk
846 847 848
	cd "$abuildrepo" || return 1
	
	# remove compat symlink
849
	for d in "$abuildrepo/$CARCH"; do
850 851
		[ -L "$d" ] && rm "$d"
	done
852

853 854
	# remove broken links from abuildrepo
	for apk in *.apk */*.apk; do
855 856 857 858
		if [ -L "$apk" ] && [ ! -f "$apk" ]; then
			rm -f "$apk"
		fi
	done
859
}
860

861
mklinks_abuildrepo() {
862 863
	local apk
	mkdir -p "$abuildrepo"/$CARCH
864
	cd "$abuildrepo" || return 1
865
	# create links for this package
866
	for apk in $(listpkg); do
867
		[ -f "$PKGDEST"/$apk ] || continue
868
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk
869
	done
870 871 872 873 874 875 876 877 878 879 880 881 882 883
}

update_abuildrepo() {
	local d apk
	if ! apk_up2date || [ -n "$force" ]; then
		sanitycheck && builddeps && clean && fetch && unpack \
			&& prepare && mkusers && rootpkg || return 1
	fi

	clean_abuildrepo
	mklinks_abuildrepo

	cd "$abuildrepo"
	local index=$CARCH/APKINDEX.tar.gz
884

885
	msg "Updating the cached abuild repository index..."
886 887
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
888 889
	if [ -f "$index" ]; then
		oldindex="--index $index"
890
	fi
891
	$APK index --quiet $oldindex --output "$index".unsigned \
892
		--description "$repo $(cd $startdir && git describe)" \
893 894
		--rewrite-arch $CARCH \
		$CARCH/*.apk || exit 1
895
	msg "Signing the index..."
896 897 898
	abuild-sign -q "$index".unsigned || exit 1
	mv "$index".unsigned "$index"
	chmod 644 "$index"
899 900
}

901
# predefined splitfunc doc
902
default_doc() {
903
	depends="$depends_doc"
904
	pkgdesc="$pkgdesc (documentation)"
905
	arch=${arch_doc:-"noarch"}
906

Natanael Copa's avatar
Natanael Copa committed
907
	local i
908
	for i in doc man info html sgml licenses gtk-doc; do
909 910 911
		if [ -d "$pkgdir/usr/share/$i" ]; then
			mkdir -p "$subpkgdir/usr/share"
			mv "$pkgdir/usr/share/$i" "$subpkgdir/usr/share/"
Natanael Copa's avatar
Natanael Copa committed
912 913
		fi
	done
914 915 916
	
	rm -f "$subpkgdir/usr/share/info/dir"

917 918 919
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
920 921 922 923

	# remove if empty, ignore error (not empty)
	rmdir "$pkgdir/usr/share" "$pkgdir/usr" 2>/dev/null

Natanael Copa's avatar
Natanael Copa committed
924
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
925 926 927
	return 0
}

928 929 930 931
doc() {
	default_doc
}

932
# predefined splitfunc mod
933
default_mod() {
934
	depends="$kernel $depends_mod"
Natanael Copa's avatar
Natanael Copa committed
935
	for i in firmware modules; do
936 937 938
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
939
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
940 941 942 943
		fi
	done
}

944 945 946 947
mod() {
	default_mod
}

948
# predefined splitfunc dev
949
default_dev() {
950
	local i= j=
951
	depends="$pkgname $depends_dev"
952 953
	pkgdesc="$pkgdesc (development files)"

954 955 956 957
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
958
	cd "$pkgdir" || return 0
959 960
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
961
			usr/share/vala/vapi usr/share/gir-[0-9]*\
962
			$(find -name include -type d) 		\
963
			$(find usr/ -name '*.[acho]' 2>/dev/null); do
964 965
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
966
			mkdir -p "$d"
967
			mv "$pkgdir/$i" "$d"
968
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
969 970
		fi
	done
971 972 973 974 975 976 977
	# move *.so links needed when linking the apps to -dev packages
	for i in lib/*.so usr/lib/*.so; do
		if [ -L "$i" ]; then
			mkdir -p "$subpkgdir"/"${i%/*}"
			mv "$i" "$subpkgdir/$i" || return 1
		fi
	done
Natanael Copa's avatar
Natanael Copa committed
978
	return 0
Natanael Copa's avatar
Natanael Copa committed
979 980
}

981 982 983 984
dev() {
	default_dev
}

985 986 987 988
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

989 990 991 992 993 994 995 996
do_fakeroot() {
	if [ -n "$FAKEROOT" ]; then
		$FAKEROOT -- "$@"
	else
		"$@"
	fi
}

Natanael Copa's avatar
Natanael Copa committed
997 998
# build and package in fakeroot
rootpkg() {
999 1000 1001 1002
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
1003
		do_build=package
1004
	fi
Natanael Copa's avatar
Natanael Copa committed
1005
	cd "$startdir"
1006
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
1007
	do_fakeroot "$abuild_path" $color_opt $do_build \
1008
		prepare_subpackages \
1009
		prepare_language_packs \
1010
		prepare_package \
1011
		create_apks
Natanael Copa's avatar
Natanael Copa committed
1012 1013 1014
}

srcpkg() {
1015
	getpkgver || return 1
1016
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
1017
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
1018 1019 1020 1021 1022
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
1023
	msg "Creating source package $p.src.tar.gz..."
1024
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
1025 1026
}

Natanael Copa's avatar
Natanael Copa committed
1027 1028
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
1029
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
1030 1031
}

1032
# check if package is up to date
1033
apk_up2date() {
1034
	getpkgver || return 1
1035
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
1036
	local i s
Natanael Copa's avatar
Natanael Copa committed
1037
	cd "$startdir"
1038 1039 1040 1041 1042
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

1043
	for i in $source APKBUILD; do
1044 1045 1046 1047 1048 1049
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
1050 1051 1052 1053 1054 1055 1056
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

1057
abuildindex_up2date() {
1058
	local i
1059
	getpkgver || return 1
1060 1061 1062 1063 1064 1065 1066
	local dir="$abuildrepo"/$CARCH
	local apk="${pkgname%:*}-$pkgver-r$pkgrel.apk"
	local idx="$dir"/APKINDEX.tar.gz
	local file="$dir"/$apk

	# check if index is missing
	[ -f "$idx" ] || return 1
1067
			
1068 1069 1070 1071 1072
	# if link or file is missing, then we need update abuildrepo index
	[ -f "$file" ] || return 1

	# if file exists and is newer than index, then we need update index
	[ "$file" -nt "$idx" ] && return 1
1073
		
1074 1075 1076 1077
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
1078
	check_arch || return 0
1079 1080 1081 1082 1083 1084 1085
	apk_up2date && abuildindex_up2date
}

# rebuild package and abuildrepo index if needed
abuildindex() {
	up2date && return 0
	update_abuildrepo
1086 1087
}

1088 1089 1090 1091 1092 1093 1094
# source all APKBUILDs and output:
#  1) origin of package
#  2) all dependencies
# the output is i in a format easy parseable for awk
depparse_aports() {
	# lets run this in a subshell since we source all APKBUILD here
	(
1095 1096
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
1097 1098 1099 1100
		pkgname=
		subpackages=
		depends=
		makedepends=
1101
		. $i
1102
		dir=${i%/APKBUILD}
1103
		deps=
1104
		# filter out conflicts from deps and version info
1105 1106 1107 1108
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
1109
			deps="$deps ${j%%[<>=]*}"
1110
		done
1111
		for j in $pkgname $subpackages; do
1112
			echo "o ${j%%:*} $dir"
1113
			set -- $deps
1114 1115 1116 1117 1118 1119 1120
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
1121 1122
		done
	done
1123
	)
1124 1125
}

1126
deptrace() {
1127 1128 1129
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
1130
		deps="$deps ${i%%[<>=]*}"
1131
	done
1132
	[ -z "$deps" ] && return 0
1133 1134 1135
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
1136
			$APK info -q | sort |  sed 's/^/i /'
1137
		fi