abuild.in 31.4 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=@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

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

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

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

#colors
if [ -n "$USE_COLORS" ]; then
    default_colors
Natanael Copa's avatar
Natanael Copa committed
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
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
}
74 75

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

cleanup() {
	set_xterm_title ""
84
	if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
85
		$SUDO $APK del $uninstall_after
86
	fi
Linux User's avatar
Linux User committed
87 88
}

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

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

111 112 113 114 115 116 117 118 119 120
	# 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
121 122 123 124 125 126 127
	for i in $install; do
		[ -e "$startdir/$i" ] || die "install script $startdir/$i is missing"
	done
	
	[ -n "${triggers%%:*}" ] && [ ! -e "$startdir"/${triggers%%:*} ] \
		&& die "trigger script $startdir/${triggers%%:*} is missing"

128 129
	if [ -n "$source" ]; then
		for i in $source; do
Natanael Copa's avatar
Natanael Copa committed
130 131 132 133
			if install_has "$i"; then
				warning "You should not have \$install in source"
				continue
			fi
134
			md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
135 136 137
			case "$i" in
				https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
			esac
138 139
		done
	fi
Natanael Copa's avatar
Natanael Copa committed
140

141 142
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
143
			source_has $i || die "$i exists in md5sums but is missing in source"
144 145
		done
	fi
146

147 148 149
	# 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
150 151

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
154 155 156 157
	return 0
}

md5check() {
Natanael Copa's avatar
Natanael Copa committed
158
	local dummy f
159 160 161
	if [ -z "$source" ]; then
		return 0
	fi
162
	if [ -z "$md5sums" ]; then
163
		die "Use 'abuild checksum' to generate/update the checksum(s)"
164
	fi
165 166 167
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
168
	fetch || return 1
169
	msg "Checking md5sums..."
170
	cd "$srcdir" && echo "$md5sums" | md5sum -c 
Natanael Copa's avatar
Natanael Copa committed
171 172
}

173 174
# verify upstream sources
sourcecheck() {
175 176 177 178 179 180 181 182 183 184
	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
185
	done
186
	return 0
187
}
188

Natanael Copa's avatar
Natanael Copa committed
189 190
uri_fetch() {
	local uri="$1"
191
	local d="${uri##*/}"	# $(basename $uri)
192 193 194 195
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

196 197 198 199 200
	# 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)}";;
201
	esac
202 203 204 205 206

	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
207
	
Natanael Copa's avatar
Natanael Copa committed
208
	mkdir -p "$SRCDEST"
209 210 211
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
212
	fi
Linux User's avatar
Linux User committed
213
	msg "Fetching $uri"
214 215
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
216 217
}

218 219
is_remote() {
	case "$1" in
220
		http://*|ftp://*|https://*|saveas-*://*)
221 222 223 224 225
			return 0;;
	esac
	return 1
}

226 227 228 229 230
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
231 232 233 234 235
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
236 237 238 239
	fi
	uri_fetch "$uri"
}

240
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
241 242
	local s
	mkdir -p "$srcdir"
243 244
	for s in $source; do
		if is_remote "$s"; then
245
			uri_fetch_mirror "$s" || return 1
246 247 248 249
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
250 251 252
	done
}

253 254 255 256
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
# 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
272
# unpack the sources
273
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
274
	local u
275 276
	if [ -z "$force" ]; then
		md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
277
		initdcheck || return 1
278
	fi
Natanael Copa's avatar
Natanael Copa committed
279 280 281 282
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
283
			*.tar.gz|*.tgz)
284
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
285 286
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
287
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
288
				tar -C "$srcdir" -jxf "$s" || return 1;;
289 290 291 292
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
293 294 295
			*.tar.xz)
				msg "Unpacking $s..."
				unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
Natanael Copa's avatar
Natanael Copa committed
296 297 298
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
299 300 301 302
		esac
	done
}

303 304 305 306
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
307 308
# cleanup source and package dir
clean() {
309
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
310
	rm -rf "$srcdir"
311
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
312 313 314 315 316 317
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
318 319 320 321
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
322 323 324
	done
}

325 326 327 328 329 330 331 332 333 334
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
335 336
cleanpkg() {
	local i
337
	getpkgver || return 1
338
	msg "Cleaning built packages..."
339
	for i in $(listpkgnames); do
340 341
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
342
			"$abuildrepo"/$p.apk
Natanael Copa's avatar
Natanael Copa committed
343
	done
344
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
345 346
}

347 348 349
# clean all packages except current
cleanoldpkg() {
	local i j
350
	getpkgver || return 1
351
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
352
	for i in $(listpkgnames); do
353 354 355 356 357 358 359
		for j in "$PKGDEST"/${i%:*}-[0-9]*.apk; do
			[ "$j" != "$PKGDEST/${i%:*}-$pkgver-r$pkgrel.apk" ] \
				&& rm -f "$j"
		done
	done
	return 0
}
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377

mkusers() {
	local i
	for i in $pkgusers; do
		if ! getent passwd $i >/dev/null; then
			msg "Creating user $i"
			$SUDO adduser -D -H $i || return 1
		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
378 379
runpart() {
	local part=$1
380
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
381 382 383 384
	$part || die "$part failed"
}

# override those in your build script
385 386 387 388 389 390 391 392
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
393 394 395 396
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
397
build() {
398
	:
Natanael Copa's avatar
Natanael Copa committed
399 400
}

401 402 403 404 405 406
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

407 408 409 410 411 412 413 414 415
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
}

416
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
417 418
	local i
	cd "$startdir"
419
	for i in $subpackages; do
420 421
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
422
		msg "Running split function $func..."
423
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
424
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
425 426 427
	done
}

428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
lang_subpkg() {
	if [ -z "$lang" ]; then
		error "lang is not set"
		return 1
	fi
	arch="noarch"
	install_if="$pkgname=$pkgver-r$pkgrel lang-$lang"
	mkdir -p "$subpkgdir"/usr/share/locale
	mv "$pkgdir"/usr/share/locale/$lang* \
		"$subpkgdir"/usr/share/locale/ \
		|| return 1
}

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
}

450 451 452 453 454 455 456 457 458 459 460 461
# 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}'
}

462
prepare_metafiles() {
463
	getpkgver || return 1
464 465
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
466
	local dir=${subpkgdir:-$pkgdir}
467
	local pkg="$name-$pkgver-r$pkgrel.apk"
468
	local pkginfo="$controldir"/.PKGINFO
469
	local sub
Natanael Copa's avatar
Natanael Copa committed
470 471 472
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
473
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
474 475
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
476 477 478 479
	local parch="$CARCH"
	if [ "$arch" = "noarch" ]; then
		parch="noarch"
	fi
Natanael Copa's avatar
Natanael Copa committed
480

481
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
482
	if [ -n "$FAKEROOTKEY" ]; then
483
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
484
	fi
485 486
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
487
pkgname = $name
488
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
489 490 491 492 493
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
Natanael Copa's avatar
Natanael Copa committed
494
arch = $parch
495
commit = $(git_last_commit)$(git_dirty)
Natanael Copa's avatar
Natanael Copa committed
496
EOF
497 498
	local i deps
	deps="$depends"
499
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
Natanael Copa's avatar
Natanael Copa committed
500 501
		for i in $install ${triggers%%:*}; do
			if head -n 1 "$startdir/$i" | grep '^#!/bin/sh' >/dev/null ; then
502 503 504 505 506
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
507 508
	fi
	
Natanael Copa's avatar
Natanael Copa committed
509
	for i in $license; do
510
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
511 512
	done
	for i in $replaces; do
513
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
514
	done
515
	for i in $deps; do
516
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
517 518
	done
	for i in $conflicts; do
519
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
520 521
	done
	for i in $provides; do
522
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
523
	done
Natanael Copa's avatar
Natanael Copa committed
524 525 526
	if [ -n "$triggers" ]; then
		echo "triggers = ${triggers#*:}" >> "$pkginfo"
	fi
527 528 529
	if [ -n "$install_if" ]; then
		echo "install_if = $(echo $install_if)" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
530 531

	local metafiles=".PKGINFO"
Natanael Copa's avatar
Natanael Copa committed
532
	for i in $install ${triggers%%:*}; do
533 534
		script=${i#$name}
		case "$script" in
Natanael Copa's avatar
Natanael Copa committed
535
			.pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
536 537
				msg "Adding $script"
				;;
Natanael Copa's avatar
Natanael Copa committed
538
			*) 	error "$script: Invalid install/trigger script"
539 540 541
				return 1
				;;
		esac
Natanael Copa's avatar
Natanael Copa committed
542
		cp "$startdir/$i" "$controldir/$script" || return 1
543
		chmod +x "$controldir/$script"
544 545
		metafiles="$metafiles $script"
	done
546
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
547
}
548

549 550
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
551
	[ "$arch" = "noarch" ] && return 0
552
	options_has "!tracedeps" && return 0
553
	# lets tell all the .so files this package provides in .provides-so
554 555
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
556 557 558 559 560
	# 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
561
	scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
562 563 564
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
565 566
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
567
	done
Natanael Copa's avatar
Natanael Copa committed
568 569
}

570 571 572 573 574 575 576 577 578 579 580 581
# 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
}

582 583 584 585 586
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

587 588 589 590 591 592 593
# 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
594 595 596
	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\""
597 598 599 600
	fi
	return 0
}

601
prepare_package() {
602
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
603
	stripbin
604 605
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
606 607 608 609 610 611 612 613
}

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

614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
# 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
}

637 638 639
trace_apk_deps() {
	local name="$1"
	local dir="$2"
640
	local i= j= found= autodeps= deppkgs= missing= so_paths= self_provided=
641
	msg "Tracing dependencies for $name..."
642 643 644
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
645
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
646 647 648 649
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
650
	if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
651 652
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
653
		msg "  added libgcc (due to libpthread)"
654
	fi
655
	[ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
656
		found=
657
		# first check if its provide by same apkbuild
658
		for j in "$dir"/../.control.*/.provides-so; do
659
			grep -w "$i" "$j" >/dev/null || continue
660
			found=${j%/.provides-so}
661
			found=${found##*/.control.}
662
			break
663
		done
664 665 666 667 668 669
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
670
		fi
671 672 673
	done

	# find all packages that holds the so files
674 675 676 677 678
	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

679
	for found in $self_provided $deppkgs; do
680
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
681 682 683
			warning "You can remove '$found' from depends"
			continue
		fi
684 685
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
686
			msg "  added $found"
687
		fi
688
	done
689

690 691 692 693 694 695 696
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

697 698
create_apks() {
	local file
699
	getpkgver || return 1
700
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
701 702 703
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
704
		local apk=$name-$ver.apk
705
		local datadir="$pkgbasedir"/$name
706 707

		trace_apk_deps "$name" "$dir" || return 1
708
		msg "Creating $apk..."
709
		(
710 711
		cd "$datadir"
		# data.tar.gz
712 713 714 715 716 717
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
718 719 720 721 722 723

		# 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
724
		cd "$dir"
725 726
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
727
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
728

729 730 731
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
732 733 734
	done
}

735
update_abuildrepo() {
736
	if ! apk_up2date || [ -n "$force" ]; then
737
		sanitycheck && builddeps && clean && fetch && unpack \
Natanael Copa's avatar
Natanael Copa committed
738
			&& prepare && mkusers && rootpkg || return 1
739
	fi
740

741
	local apk
742 743
	mkdir -p "$abuildrepo" || return 1
	cd "$abuildrepo"
744 745 746 747 748 749 750 751 752

	# remove broken links
	for apk in *.apk; do
		if [ -L "$apk" ] && [ ! -f "$apk" ]; then
			rm -f "$apk"
		fi
	done

	# create links for this package
753
	for apk in $(listpkg); do
754
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$apk
755 756
	done

757
	msg "Updating the cached abuild repository index..."
758 759 760 761 762
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
	if [ -f APKINDEX.tar.gz ]; then
		oldindex="--index APKINDEX.tar.gz"
	fi
763
	$APK index $oldindex --output APKINDEX.tar.gz.unsigned \
764 765
		--description "$repo $(cd $startdir && git describe)" \
		*.apk || exit 1
766 767 768
	msg "Signing the index..."
	abuild-sign -q APKINDEX.tar.gz.unsigned || exit 1
	mv APKINDEX.tar.gz.unsigned APKINDEX.tar.gz
Natanael Copa's avatar
Natanael Copa committed
769
	chmod 644 APKINDEX.tar.gz
770 771
}

772
# predefined splitfunc doc
773
default_doc() {
774
	depends="$depends_doc"
775
	install="$install_doc"
Natanael Copa's avatar
Natanael Copa committed
776
	triggers="$triggers_doc"
777
	pkgdesc="$pkgdesc (documentation)"
778
	arch=${arch_doc:-"noarch"}
779

Natanael Copa's avatar
Natanael Copa committed
780
	local i
781
	for i in doc man info html sgml licenses gtk-doc; do
782 783 784
		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
785 786
		fi
	done
787 788 789
	
	rm -f "$subpkgdir/usr/share/info/dir"

790 791 792
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
793 794 795 796

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

Natanael Copa's avatar
Natanael Copa committed
797
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
798 799 800
	return 0
}

801 802 803 804
doc() {
	default_doc
}

805
# predefined splitfunc mod
806
default_mod() {
807
	depends="$kernel $depends_mod"
808
	install="$install_mod"
Natanael Copa's avatar
Natanael Copa committed
809
	for i in firmware modules; do
810 811 812
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
813
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
814 815 816 817
		fi
	done
}

818 819 820 821
mod() {
	default_mod
}

822
# predefined splitfunc dev
823
default_dev() {
824
	local i= j=
825
	depends="$pkgname $depends_dev"
826
	install="$install_dev"
Natanael Copa's avatar
Natanael Copa committed
827
	triggers="$triggers_dev"
828 829
	pkgdesc="$pkgdesc (development files)"

830 831 832 833
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
834
	cd "$pkgdir" || return 0
835 836
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
837
			usr/share/vala/vapi usr/share/gir-[0-9]*\
838 839 840
			$(find -name include -type d) 		\
			$(find usr/ -name '*.[acho]' -o -name '*.la' \
			2>/dev/null); do
841 842
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
843
			mkdir -p "$d"
844
			mv "$pkgdir/$i" "$d"
845
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
846 847
		fi
	done
848 849 850 851 852 853 854
	# 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
855
	return 0
Natanael Copa's avatar
Natanael Copa committed
856 857
}

858 859 860 861
dev() {
	default_dev
}

862 863 864 865
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

Natanael Copa's avatar
Natanael Copa committed
866 867
# build and package in fakeroot
rootpkg() {
868 869 870 871
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
872
		do_build=package
873
	fi
Natanael Copa's avatar
Natanael Copa committed
874
	cd "$startdir"
875
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
876
	$FAKEROOT -- "$abuild_path" $color_opt $do_build \
877
		prepare_subpackages \
878
		prepare_language_packs \
879
		prepare_package \
880
		create_apks
Natanael Copa's avatar
Natanael Copa committed
881 882 883
}

srcpkg() {
884
	getpkgver || return 1
885
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
886
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
887 888 889 890 891
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
892
	msg "Creating source package $p.src.tar.gz..."
893
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
894 895
}

Natanael Copa's avatar
Natanael Copa committed
896 897
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
898
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
899 900
}

901
# check if package is up to date
902
apk_up2date() {
903
	getpkgver || return 1
904
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
905
	local i s
Natanael Copa's avatar
Natanael Copa committed
906
	cd "$startdir"
907 908 909 910 911
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

912
	for i in $source APKBUILD; do
913 914 915 916 917 918
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
919 920 921 922 923 924 925
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

926
abuildindex_up2date() {
927
	local i apk
928
	getpkgver || return 1
929 930
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
931
		[ "$abuildrepo"/APKINDEX.tar.gz -nt "$abuildrepo"/$apk ] || return 1
932 933 934 935 936
	done
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
937
	check_arch || return 0
938 939 940 941 942 943 944
	apk_up2date && abuildindex_up2date
}

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

947 948 949 950 951 952 953
# 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
	(
954 955
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
956 957 958 959
		pkgname=
		subpackages=
		depends=
		makedepends=
960
		. $i
961
		dir=${i%/APKBUILD}
962
		deps=
963
		# filter out conflicts from deps and version info
964 965 966 967
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
968
			deps="$deps ${j%%[<>=]*}"
969
		done
970
		for j in $pkgname $subpackages; do
971
			echo "o ${j%%:*} $dir"
972
			set -- $deps
973 974 975 976 977 978 979
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
980 981
		done
	done
982
	)
983 984
}

985
deptrace() {
986 987 988
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
989
		deps="$deps ${i%%[<>=]*}"
990
	done
991
	[ -z "$deps" ] && return 0
992 993 994
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
995
			$APK info -q | sort |  sed 's/^/i /'
996
		fi
997
	) | awk -v pkgs="$deps" '
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022

	function depgraph(pkg,	a, i) {
		if (visited[pkg])
			return 0;
		visited[pkg] = 1;
		split(deps[pkg], a, ",");
		for (i in a)
			depgraph(a[i]);
		print pkg ":" origin[pkg];

	}

	$1 == "i" { visited[$2] = 1 }
	$1 == "o" { origin[$2] = $3 }
	$1 == "d" { deps[$2] = $3 }
	END {
		split(pkgs, pkgarray);
		for (i in pkgarray)
			depgraph(pkgarray[i]);
	}
	'
}

# build and install dependencies
builddeps() {
1023
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
1024
	local filtered_deps= conflicts=
1025
	[ -n "$nodeps" ] && return 0
1026
	msg "Analyzing dependencies..."
1027

1028 1029 1030
	# add depends unless it is a subpackage or package itself
	for i in $BUILD_BASE $depends $makedepends; do
		[ "$pkgname" = "${i%%[<>=]*}" ] && continue
1031
		subpackages_has ${i%%[<>=]*} || deps="$deps $i"
1032 1033
	done

1034
	installed_deps=$($APK info -e $deps)
1035
	# find which deps are missing
1036
	for i in $deps; do
1037
		if [ "${i#\!}" != "$i" ]; then
1038
			$APK info -q -e "${i#\!}" \
1039
				&& conflicts="$conflicts ${i#\!}"
1040
		elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
1041 1042 1043
			missing="$missing $i"
		fi
	done
1044
	
1045
	if [ -n "$conflicts" ]; then
1046 1047
		error "Conflicting package(s) installed:$conflicts"
		return 1
1048 1049 1050 1051 1052 1053 1054 1055
	fi
	
	if [ -z "$install_deps" ] && [ -z "$recursive" ]; then
		# if we dont have any missing deps we are done now
		[ -z "$missing" ] && return 0
		error "Missing dependencies: $missing Use -r to autoinstall or -R to build"
		return 1
	fi
1056

1057
	uninstall_after=".makedepends-$pkgname $uninstall_after"
1058
	if [ -n "$install_deps" ] && [ -z "$recursive" ] && [ -n "$deps" ]; then
1059 1060
		# make a --simluate run first to detect missing deps
		# apk-tools --virtual is no goot at reporting those.
1061
		$SUDO $APK add --repository "$abuildrepo" \
1062 1063
			--wait 30 \
			--simulate --quiet $deps || return 1
1064
		$SUDO $APK add --repository "$abuildrepo" \
1065
			--wait 30 \
1066 1067
			--virtual .makedepends-$pkgname $deps \
			&& return 0
1068
	fi
1069 1070
	
	[ -z "$recursive" ] && return 1
1071 1072 1073

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
1074
		local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
1075
		if [ -z "$m" ]; then
1076 1077 1078
			missing="$missing $i"
		fi
	done
1079 1080
	
	for i in $(deptrace $missing); do
1081 1082 1083
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093

		# ignore if dependency is in other repo
		[ -d "$dir" ] || continue
		
		# break cricular deps
		list_has $pkg $ABUILD_VISITED && continue
		export ABUILD_VISITED="$ABUILD_VISITED $pkg"

		msg "Entering $dir"
		cd "$dir" && $0 $forceroot $keep $quiet $install_deps \
1094
			$recursive $upgrade $color_opt abuildindex || return 1
1095
	done
1096
	$SUDO $APK add -u --repository "$abuildrepo" \
1097
		--wait 30 \
1098
		--virtual .makedepends-$pkgname $deps
1099 1100
}

1101
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
1102 1103
checksum() {
	local s files
1104
	[ -z "$source" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
1105
	fetch
1106
	msg "Updating the md5sums in APKBUILD..."
Natanael Copa's avatar
Natanael Copa committed
1107 1108 1109 1110
	for s in $source; do
		files="$files ${s##*/}"
	done
	md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
1111 1112
	sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
	echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
1113 1114
}

1115
stripbin() {
1116
	local bin
Natanael Copa's avatar