abuild.in 26.7 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 77
set_xterm_title() {
	if [ "$TERM" = xterm ]; 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() {
Natanael Copa's avatar
Natanael Copa committed
97
	local i
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"

Natanael Copa's avatar
Natanael Copa committed
111 112 113 114 115 116 117
	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"

118 119
	if [ -n "$source" ]; then
		for i in $source; do
Natanael Copa's avatar
Natanael Copa committed
120 121 122 123
			if install_has "$i"; then
				warning "You should not have \$install in source"
				continue
			fi
124
			md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
125 126 127
			case "$i" in
				https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
			esac
128 129
		done
	fi
Natanael Copa's avatar
Natanael Copa committed
130

131 132
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
133
			source_has $i || die "$i exists in md5sums but is missing in source"
134 135
		done
	fi
136

137 138 139
	# 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
140 141

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
144 145 146 147
	return 0
}

md5check() {
Natanael Copa's avatar
Natanael Copa committed
148
	local dummy f
149 150 151
	if [ -z "$source" ]; then
		return 0
	fi
152
	if [ -z "$md5sums" ]; then
153
		die "Use 'abuild checksum' to generate/update the checksum(s)"
154
	fi
155 156 157
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
158
	fetch || return 1
159
	msg "Checking md5sums..."
160
	cd "$srcdir" && echo "$md5sums" | md5sum -c 
Natanael Copa's avatar
Natanael Copa committed
161 162 163 164
}

uri_fetch() {
	local uri="$1"
165
	local d="${uri##*/}"	# $(basename $uri)
166 167 168 169
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

170 171 172 173 174
	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
	
Natanael Copa's avatar
Natanael Copa committed
175
	mkdir -p "$SRCDEST"
176 177 178
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
179
	fi
Linux User's avatar
Linux User committed
180
	msg "Fetching $uri"
181 182
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
183 184
}

185 186
is_remote() {
	case "$1" in
187
		http://*|ftp://*|https://*)
188 189 190 191 192
			return 0;;
	esac
	return 1
}

193 194 195 196 197
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
198 199 200 201 202
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
203 204 205 206
	fi
	uri_fetch "$uri"
}

207
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
208 209
	local s
	mkdir -p "$srcdir"
210 211
	for s in $source; do
		if is_remote "$s"; then
212
			uri_fetch_mirror "$s" || return 1
213 214 215 216
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
217 218 219
	done
}

220 221 222 223
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
224
# unpack the sources
225
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
226
	local u
227
	md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
228 229 230 231
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
232
			*.tar.gz|*.tgz)
233
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
234 235
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
236
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
237
				tar -C "$srcdir" -jxf "$s" || return 1;;
238 239 240 241
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
Natanael Copa's avatar
Natanael Copa committed
242 243 244
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
245 246 247 248
		esac
	done
}

249 250 251 252
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
253 254
# cleanup source and package dir
clean() {
255
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
256
	rm -rf "$srcdir"
257
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
258 259 260 261 262 263
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
264 265 266 267
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
268 269 270 271 272
	done
}

cleanpkg() {
	local i
273
	getpkgver || return 1
274
	msg "Cleaning built packages..."
275
	for i in $pkgname $subpackages; do
276 277
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
278
			"$abuildrepo"/$p.apk
Natanael Copa's avatar
Natanael Copa committed
279
	done
280
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
281 282
}

283 284 285
# clean all packages except current
cleanoldpkg() {
	local i j
286
	getpkgver || return 1
287 288 289 290 291 292 293 294 295
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
	for i in $pkgname $subpackages; do
		for j in "$PKGDEST"/${i%:*}-[0-9]*.apk; do
			[ "$j" != "$PKGDEST/${i%:*}-$pkgver-r$pkgrel.apk" ] \
				&& rm -f "$j"
		done
	done
	return 0
}
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313

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
314 315
runpart() {
	local part=$1
316
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
317 318 319 320
	$part || die "$part failed"
}

# override those in your build script
321 322 323 324 325 326 327 328
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
329 330 331 332
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
333
build() {
334
	:
Natanael Copa's avatar
Natanael Copa committed
335 336
}

337 338 339 340 341 342
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

343 344 345 346 347 348 349 350 351
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
}

352
prepare_subpackages() {
353
	if [ -z "$subpackages" ]; then
354 355
		return 0
	fi
Natanael Copa's avatar
Natanael Copa committed
356 357
	local i
	cd "$startdir"
358
	for i in $subpackages; do
359 360
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
361
		msg "Running split function $func..."
362
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
363
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
364 365 366
	done
}

367
prepare_metafiles() {
368
	getpkgver || return 1
369 370
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
371
	local dir=${subpkgdir:-$pkgdir}
372
	local pkg="$name-$pkgver-r$pkgrel.apk"
373
	local pkginfo="$controldir"/.PKGINFO
374
	local sub
Natanael Copa's avatar
Natanael Copa committed
375 376 377
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
378
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
379 380 381
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')

382
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
383
	if [ -n "$FAKEROOTKEY" ]; then
384
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
385
	fi
386 387
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
388
pkgname = $name
389
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
390 391 392 393 394 395
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
EOF
396 397
	local i deps
	deps="$depends"
398
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
Natanael Copa's avatar
Natanael Copa committed
399 400
		for i in $install ${triggers%%:*}; do
			if head -n 1 "$startdir/$i" | grep '^#!/bin/sh' >/dev/null ; then
401 402 403 404 405
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
406 407
	fi
	
Natanael Copa's avatar
Natanael Copa committed
408
	for i in $license; do
409
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
410 411
	done
	for i in $replaces; do
412
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
413
	done
414
	for i in $deps; do
415
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
416 417
	done
	for i in $conflicts; do
418
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
419 420
	done
	for i in $provides; do
421
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
422 423
	done
	for i in $backup; do
424
		echo "backup = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
425
	done
Natanael Copa's avatar
Natanael Copa committed
426 427 428
	if [ -n "$triggers" ]; then
		echo "triggers = ${triggers#*:}" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
429 430

	local metafiles=".PKGINFO"
Natanael Copa's avatar
Natanael Copa committed
431
	for i in $install ${triggers%%:*}; do
432 433
		script=${i#$name}
		case "$script" in
Natanael Copa's avatar
Natanael Copa committed
434
			.pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
435 436
				msg "Adding $script"
				;;
Natanael Copa's avatar
Natanael Copa committed
437
			*) 	error "$script: Invalid install/trigger script"
438 439 440
				return 1
				;;
		esac
Natanael Copa's avatar
Natanael Copa committed
441
		cp "$startdir/$i" "$controldir/$script" || return 1
442
		chmod +x "$controldir/$script"
443 444
		metafiles="$metafiles $script"
	done
445
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
446
}
447

448 449 450
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
	options_has "!tracedeps" && return 0
451 452
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
453
	scanelf -Rn "$dir" | awk -F "\ " '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
454 455 456
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
457 458
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
459
	done
Natanael Copa's avatar
Natanael Copa committed
460 461
}

462
prepare_package() {
463
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
464
	stripbin
465
	prepare_metafiles && prepare_tracedeps
466 467 468 469 470 471 472 473
}

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

474 475 476
trace_apk_deps() {
	local name="$1"
	local dir="$2"
477 478
	local i j found autodeps=
	msg "Tracing dependencies for $name..."
479 480 481 482 483 484
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
		msg "Added pkgconfig as dependency"
		echo "depend = pkgconfig" >> "$dir"/.PKGINFO
	fi
485
	for i in $(cat "$dir"/.needs-so 2>/dev/null); do
486
		found=
487
		# first check if its provide by same apkbuild
488
		for j in "$dir"/../.control.*/.provides-so; do
489
			grep -w "$i" "$j" >/dev/null || continue
490
			found=${j%/.provides-so}
491
			found=${found##*/.control.}
492
			break
493 494 495
		done
		# check apk db if not provided by a subpackage
		if [ -z "$found" ]; then
496
			found=$($APK info -q -W /lib/$i /usr/lib/$i)
497 498 499 500 501
		fi
		if [ -z "$found" ]; then
			error "Could not find dependency for $i"
			return 1
		fi
502
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
503 504 505
			warning "You can remove '$found' from depends"
			continue
		fi
506 507
		list_has "$found" $autodeps || autodeps="$autodeps $found"
		msg "Added '$found' as dependency as it has $i"
508 509 510 511 512 513 514 515
	done
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

516 517
create_apks() {
	local file
518
	getpkgver || return 1
519
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
520 521 522
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
523
		local apk=$name-$ver.apk
524
		local datadir="$pkgbasedir"/$name
525 526

		trace_apk_deps "$name" "$dir" || return 1
527
		msg "Creating $apk..."
528
		(
529 530
		cd "$datadir"
		# data.tar.gz
531 532 533 534 535 536
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
537 538 539 540 541 542

		# 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
543
		cd "$dir"
544 545
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
546
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
547

548 549 550
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
551 552 553
	done
}

554
update_abuildrepo() {
555
	if ! apk_up2date || [ -n "$force" ]; then
556
		sanitycheck && builddeps && clean && fetch && unpack \
Natanael Copa's avatar
Natanael Copa committed
557
			&& prepare && mkusers && rootpkg || return 1
558
	fi
559

560
	local apk
561 562
	mkdir -p "$abuildrepo" || return 1
	cd "$abuildrepo"
563 564 565 566 567 568 569 570 571

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

	# create links for this package
572
	for apk in $(listpkg); do
573
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$apk
574 575
	done

576
	msg "Updating the cached abuild repository index..."
577 578 579 580 581
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
	if [ -f APKINDEX.tar.gz ]; then
		oldindex="--index APKINDEX.tar.gz"
	fi
582
	$APK index $oldindex --output APKINDEX.tar.gz.unsigned \
583 584
		--description "$repo $(cd $startdir && git describe)" \
		*.apk || exit 1
585 586 587
	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
588
	chmod 644 APKINDEX.tar.gz
589 590
}

591
# predefined splitfunc doc
592
default_doc() {
593
	depends="$depends_doc"
594
	install="$install_doc"
Natanael Copa's avatar
Natanael Copa committed
595
	triggers="$triggers_doc"
Natanael Copa's avatar
Natanael Copa committed
596
	local i
597
	for i in doc man info html sgml licenses gtk-doc; do
598 599 600
		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
601 602
		fi
	done
603 604 605
	
	rm -f "$subpkgdir/usr/share/info/dir"

606 607 608
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
609 610 611 612

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

Natanael Copa's avatar
Natanael Copa committed
613
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
614 615 616
	return 0
}

617 618 619 620
doc() {
	default_doc
}

621
# predefined splitfunc mod
622
default_mod() {
623
	depends="$kernel $depends_mod"
624
	install="$install_mod"
Natanael Copa's avatar
Natanael Copa committed
625
	for i in firmware modules; do
626 627 628
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
629
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
630 631 632 633
		fi
	done
}

634 635 636 637
mod() {
	default_mod
}

638
# predefined splitfunc dev
639
default_dev() {
640
	local i= j=
641
	depends="$pkgname $depends_dev"
642
	install="$install_dev"
Natanael Copa's avatar
Natanael Copa committed
643
	triggers="$triggers_dev"
644 645 646 647
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
648
	cd "$pkgdir" || return 0
649 650
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
651
			usr/share/vala/vapi usr/share/gir-[0-9]*\
652 653 654
			$(find -name include -type d) 		\
			$(find usr/ -name '*.[acho]' -o -name '*.la' \
			2>/dev/null); do
655 656
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
657
			mkdir -p "$d"
658
			mv "$pkgdir/$i" "$d"
659
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
660 661
		fi
	done
662 663 664 665 666 667 668
	# 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
669
	return 0
Natanael Copa's avatar
Natanael Copa committed
670 671
}

672 673 674 675
dev() {
	default_dev
}

676 677 678 679
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

Natanael Copa's avatar
Natanael Copa committed
680 681
# build and package in fakeroot
rootpkg() {
682 683 684 685
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
686
		do_build=package
687
	fi
Natanael Copa's avatar
Natanael Copa committed
688
	cd "$startdir"
689
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
690
	$FAKEROOT -- "$abuild_path" $color_opt $do_build \
691 692
		prepare_subpackages \
		prepare_package \
693
		create_apks
Natanael Copa's avatar
Natanael Copa committed
694 695 696
}

srcpkg() {
697
	getpkgver || return 1
698
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
699
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
700 701 702 703 704
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
705
	msg "Creating source package $p.src.tar.gz..."
706
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
707 708
}

709
# check if package is up to date
710
apk_up2date() {
711
	getpkgver || return 1
712
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
713
	local i s
Natanael Copa's avatar
Natanael Copa committed
714
	cd "$startdir"
715 716 717 718 719
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

720
	for i in $source APKBUILD; do
721 722 723 724 725 726
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
727 728 729 730 731 732 733
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

734
abuildindex_up2date() {
735
	local i apk
736
	getpkgver || return 1
737 738
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
739
		[ "$abuildrepo"/APKINDEX.tar.gz -nt "$abuildrepo"/$apk ] || return 1
740 741 742 743 744
	done
	return 0
}

up2date() {
745 746 747 748 749 750 751
	apk_up2date && abuildindex_up2date
}

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

754 755 756 757 758 759 760
# 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
	(
761 762
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
763 764 765 766
		pkgname=
		subpackages=
		depends=
		makedepends=
767
		. $i
768
		dir=${i%/APKBUILD}
769
		deps=
770
		# filter out conflicts from deps and version info
771 772 773 774
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
775
			deps="$deps ${j%%[<>=]*}"
776
		done
777
		for j in $pkgname $subpackages; do
778
			echo "o ${j%%:*} $dir"
779
			set -- $deps
780 781 782 783 784 785 786
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
787 788
		done
	done
789
	)
790 791
}

792
deptrace() {
793 794 795
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
796
		deps="$deps ${i%%[<>=]*}"
797
	done
798
	[ -z "$deps" ] && return 0
799 800 801
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
802
			$APK info -q | sort |  sed 's/^/i /'
803
		fi
804
	) | awk -v pkgs="$deps" '
805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829

	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() {
830
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
831
	local filtered_deps= conflicts=
832
	[ -n "$nodeps" ] && return 0
833
	msg "Analyzing dependencies..."
834

835 836 837
	# add depends unless it is a subpackage or package itself
	for i in $BUILD_BASE $depends $makedepends; do
		[ "$pkgname" = "${i%%[<>=]*}" ] && continue
838
		subpackages_has ${i%%[<>=]*} || deps="$deps $i"
839 840
	done

841
	installed_deps=$($APK info -e $deps)
842
	# find which deps are missing
843
	for i in $deps; do
844
		if [ "${i#\!}" != "$i" ]; then
845
			$APK info -q -e "${i#\!}" \
846
				&& conflicts="$conflicts ${i#\!}"
847
		elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
848 849 850
			missing="$missing $i"
		fi
	done
851
	
852
	if [ -n "$conflicts" ]; then
853 854
		error "Conflicting package(s) installed:$conflicts"
		return 1
855 856 857 858 859 860 861 862
	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
863

864
	uninstall_after=".makedepends-$pkgname $uninstall_after"
865
	if [ -n "$install_deps" ] && [ -z "$recursive" ]; then
866 867
		# make a --simluate run first to detect missing deps
		# apk-tools --virtual is no goot at reporting those.
868
		$SUDO $APK add --repository "$abuildrepo" \
869 870
			--wait 30 \
			--simulate --quiet $deps || return 1
871
		$SUDO $APK add --repository "$abuildrepo" \
872
			--wait 30 \
873 874
			--virtual .makedepends-$pkgname $deps \
			&& return 0
875
	fi
876 877
	
	[ -z "$recursive" ] && return 1
878 879 880

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
881
		local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
882
		if [ -z "$m" ]; then
883 884 885
			missing="$missing $i"
		fi
	done
886 887
	
	for i in $(deptrace $missing); do
888 889 890
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
891 892 893 894 895 896 897 898 899 900

		# 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 \
901
			$recursive $upgrade $color_opt abuildindex || return 1
902
	done
903
	$SUDO $APK add -u --repository "$abuildrepo" \
904
		--wait 30 \
905
		--virtual .makedepends-$pkgname $deps
906 907
}

908
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
909 910
checksum() {
	local s files
911
	[ -z "$source" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
912
	fetch
913
	msg "Updating the md5sums in APKBUILD..."
Natanael Copa's avatar
Natanael Copa committed
914 915 916 917
	for s in $source; do
		files="$files ${s##*/}"
	done
	md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
918 919
	sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
	echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
920 921
}

922
stripbin() {
923
	local bin
924
	options_has "!strip" && return 0
925 926
	cd "${subpkgdir:-$pkgdir}" || return 1
	msg "Stripping binaries"
927 928 929
	scanelf --recursive --nobanner --etype "ET_DYN,ET_EXEC" . \
		| sed -e 's:^ET_DYN ::' -e 's:^ET_EXEC ::' \
		| xargs -r strip
930 931
}

932 933 934
# simply list target apks
listpkg() {
	local i 
935
	getpkgver || return 1
936 937 938 939 940
	for i in $pkgname $subpackages; do
		echo "${i%:*}-$pkgver-r$pkgrel.apk"
	done
}

Natanael Copa's avatar
Natanael Copa committed
941 942 943 944 945 946 947 948
source_has() {
	local i
	for i in $source; do
		[ "$1" = "${i##*/}" ] && return 0
	done
	return 1
}

Natanael Copa's avatar
Natanael Copa committed
949 950 951 952 953 954 955 956
subpackages_has() {
	local i
	for i in $subpackages; do
		[ "$1" = "${i%:*}" ] && return 0
	done
	return 1
}

957 958 959 960 961 962 963 964 965 966 967
list_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

968 969 970 971 972 973
# same as list_has but we filter version info
deplist_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
974
		i=${i%%[<>=]*}
975 976 977 978 979 980
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

981 982 983 984
options_has() {
	list_has "$1" $options
}

985
depends_has() {
986
	deplist_has "$1" $depends
987 988
}

989
makedepends_has() {
990
	deplist_has "$1" $makedepends
991 992
}

Natanael Copa's avatar
Natanael Copa committed
993 994 995 996
md5sums_has() {
	list_has "$1" $md5sums
}

Natanael Copa's avatar
Natanael Copa committed
997 998 999 1000
install_has() {
	list_has "$1" $install
}

Natanael Copa's avatar
Natanael Copa committed
1001 1002
# install package after build
post_add() {
1003
	getpkgver || return 1
Natanael Copa's avatar
Natanael Copa committed
1004 1005 1006 1007 1008 1009
	local pkgf="$PKGDEST/$1-$pkgver-r$pkgrel.apk"
	local deps i
	if ! subpackages_has $1 && [ "$1" != "$pkgname" ]; then
		die "$1 is not built by this APKBUILD"
	fi
	# recursively install dependencies that are provided by this APKBUILD
1010
	deps=$($APK index "$pkgf" 2>/dev/null | awk -F: '$1=="D" { print $2 }')
Natanael Copa's avatar
Natanael Copa committed
1011 1012 1013 1014 1015
	for i in $deps; do
		if subpackages_has $i || [ "$i" = "$pkgname" ]; then
			post_add $i || return 1
		fi
	done
1016
	$SUDO $APK add --wait 30 -u "$pkgf" || die "Failed to install $1"
Natanael Copa's avatar
Natanael Copa committed
1017 1018
}

1019
installdeps() {
1020
	local deps i
1021
	sudo $APK add --wait 30 --repository "$abuildrepo" \
1022
		--virtual .makedepends-$pkgname \
1023
		$makedepends
1024 1025 1026
}

uninstalldeps (){
1027
	sudo $APK del .makedepends-$pkgname
1028
}
1029 1030 1031 1032 1033

all() {
	if up2date && [ -z "$force" ]; then
		msg "Package is up to date"
	else
1034
		update_abuildrepo
1035 1036 1037
	fi
}

Natanael Copa's avatar
Natanael Copa committed
1038
usage() {
1039 1040
	echo "$program $abuild_ver"
	echo "usage: $program [options] [-i PKG] [-P REPODEST] [-p PKGDEST]"
1041
	echo "              [-s SRCDEST] [cmd] ..."
1042
	echo "       $program [-c] -n PKGNAME[-PKGVER]"
Natanael Copa's avatar
Natanael Copa committed
1043
	echo "Options:"
1044
	echo " -c  Enable colored output"
1045
	echo " -d  Disable dependency checking"
Natanael Copa's avatar
Natanael Copa committed
1046
	echo " -f  Force specified cmd, even if they are already done"
1047
	echo " -F  Force run as root"
1048 1049
	echo " -h  Show this help"
	echo " -i  Install PKG after successul build"
1050
	echo " -k  Keep built packages, even if APKBUILD or sources are newer"
1051
	echo " -m  Disable colors (monochrome)"
1052
	echo " -p  Set package destination directory"
1053
	echo " -P  Set PKGDEST to REPODEST/<repo>, where repo is the parents dir name" 
Natanael Copa's avatar
Natanael Copa committed
1054
	echo " -q  Quiet"
1055 1056
	echo " -r  Install missing dependencies from system repository (using sudo)"
	echo " -R  Recursively build and install missing dependencies (using sudo)"
1057
	echo " -s  Set source package destination directory"
1058
	echo " -u  Recursively build and upgrade all dependencies (using sudo)"
Natanael Copa's avatar
Natanael Copa committed
1059 1060
	echo ""
	echo "Commands:"
1061
	echo "  checksum    Generate checksum to be included in APKBUILD"
1062 1063 1064 1065 1066
	echo "  fetch       Fetch sources to \$SRCDEST and verify checksums"
	echo "  sanitycheck Basic sanity check of APKBUILD"
	echo "  md5check    Check md5sums"
	echo "  unpack      Unpack sources to \$srcdir"
	echo "  build       Compile and install package into \$pkgdir"
1067
	echo "  listpkg     List target packages"
1068 1069 1070
	echo "  package     Create package in \$PKGDEST"
	echo "  rootpkg     Run '$0 build package' as fakeroot"
	echo "  clean       Remove temp build and install dirs"
1071
	echo "  cleanoldpkg Remove binary packages except current version"
1072 1073 1074 1075
	echo "  cleanpkg    Remove already built binary and source package"
	echo "  cleancache  Remove downloaded files from \$SRCDEST"
	echo "  srcpkg      Make a source package"
	echo "  up2date     Compare target and sources dates"
1076 1077
	echo "  installdeps Install packages listed in makedepends and depends"
	echo "  uninstalldeps Uninstall packages listed in makedepends and depends"
Natanael Copa's avatar
Natanael Copa committed
1078 1079 1080 1081 1082
	echo ""
	exit 0
}

APKBUILD="${APKBUILD:-./APKBUILD}"
1083 1084
unset force
unset recursive