abuild.in 26.6 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
}
Natanael Copa's avatar
Natanael Copa committed
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" ||\
Natanael Copa's avatar
Natanael Copa committed
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"
Natanael Copa's avatar
Natanael Copa committed
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

Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
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 *
}

Natanael Copa's avatar
Natanael Copa committed
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() {
Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
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 651 652 653
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
			$(find -name include -type d) 		\
			$(find usr/ -name '*.[acho]' -o -name '*.la' \
			2>/dev/null); do
654 655
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
656
			mkdir -p "$d"
657
			mv "$pkgdir/$i" "$d"
658
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
659 660
		fi
	done
661 662 663 664 665 666 667
	# 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
668
	return 0
Natanael Copa's avatar
Natanael Copa committed
669 670
}

671 672 673 674
dev() {
	default_dev
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Natanael Copa's avatar
Natanael Copa committed
988
makedepends_has() {
989
	deplist_has "$1" $makedepends
Natanael Copa's avatar
Natanael Copa committed
990 991
}

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

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

Natanael Copa's avatar
abuild:  
Natanael Copa committed
1000 1001
# install package after build
post_add() {
1002
	getpkgver || return 1
Natanael Copa's avatar
abuild:  
Natanael Copa committed
1003 1004 1005 1006 1007 1008
	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
1009
	deps=$($APK index "$pkgf" 2>/dev/null | awk -F: '$1=="D" { print $2 }')
Natanael Copa's avatar
abuild:  
Natanael Copa committed
1010 1011 1012 1013 1014
	for i in $deps; do
		if subpackages_has $i || [ "$i" = "$pkgname" ]; then
			post_add $i || return 1
		fi
	done
1015
	$SUDO $APK add --wait 30 -u "$pkgf" || die "Failed to install $1"
Natanael Copa's avatar
abuild:  
Natanael Copa committed
1016 1017
}

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

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

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

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

APKBUILD="${APKBUILD:-./APKBUILD}"
1082 1083
unset force
unset recursive
1084
while getopts "cdfFhi:kimnp:P:qrRs:u" opt; do
Natanael Copa's avatar
Natanael Copa committed