abuild.in 26.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
}
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
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
Natanael Copa's avatar
Natanael Copa committed
661
	return 0
Natanael Copa's avatar
Natanael Copa committed
662 663
}

664 665 666 667
dev() {
	default_dev
}

668 669 670 671
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

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

srcpkg() {
689
	getpkgver || return 1
690
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
691
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
692 693 694 695 696
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
697
	msg "Creating source package $p.src.tar.gz..."
698
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
699 700
}

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

712
	for i in $source APKBUILD; do
713 714 715 716 717 718
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
719 720 721 722 723 724 725
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

726
abuildindex_up2date() {
727
	local i apk
728
	getpkgver || return 1
729 730
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
731
		[ "$abuildrepo"/APKINDEX.tar.gz -nt "$abuildrepo"/$apk ] || return 1
732 733 734 735 736
	done
	return 0
}

up2date() {
737 738 739 740 741 742 743
	apk_up2date && abuildindex_up2date
}

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

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

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

	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() {
822
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
823
	local filtered_deps= conflicts=
824
	[ -n "$nodeps" ] && return 0
825
	msg "Analyzing dependencies..."
826

827 828 829
	# add depends unless it is a subpackage or package itself
	for i in $BUILD_BASE $depends $makedepends; do
		[ "$pkgname" = "${i%%[<>=]*}" ] && continue
830
		subpackages_has ${i%%[<>=]*} || deps="$deps $i"
831 832
	done

833
	installed_deps=$($APK info -e $deps)
834
	# find which deps are missing
835
	for i in $deps; do
836
		if [ "${i#\!}" != "$i" ]; then
837
			list_has ${i#\!} $installed_deps \
838
				&& conflicts="$conflicts ${i#\!}"
839
		elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
840 841 842 843
			missing="$missing $i"
		fi
	done

844 845 846 847 848 849 850 851 852 853
	if [ -n "$conflicts" ]; then
		die "Conflicting package(s) installed:$conflics"
	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
854

855
	uninstall_after=".makedepends-$pkgname $uninstall_after"
856
	if [ -n "$install_deps" ] && [ -z "$recursive" ]; then
857 858
		# make a --simluate run first to detect missing deps
		# apk-tools --virtual is no goot at reporting those.
859
		$SUDO $APK add --repository "$abuildrepo" \
860 861
			--wait 30 \
			--simulate --quiet $deps || return 1
862
		$SUDO $APK add --repository "$abuildrepo" \
863
			--wait 30 \
864 865
			--virtual .makedepends-$pkgname $deps \
			&& return 0
866
	fi
867 868
	
	[ -z "$recursive" ] && return 1
869 870 871

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
872
		local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
873
		if [ -z "$m" ]; then
874 875 876
			missing="$missing $i"
		fi
	done
877 878
	
	for i in $(deptrace $missing); do
879 880 881
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
882 883 884 885 886 887 888 889 890 891

		# 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 \
892
			$recursive $upgrade $color_opt abuildindex || return 1
893
	done
894
	$SUDO $APK add -u --repository "$abuildrepo" \
895
		--wait 30 \
896
		--virtual .makedepends-$pkgname $deps
897 898
}

899
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
900 901
checksum() {
	local s files
902
	[ -z "$source" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
903
	fetch
904
	msg "Updating the md5sums in APKBUILD..."
Natanael Copa's avatar
Natanael Copa committed
905 906 907 908
	for s in $source; do
		files="$files ${s##*/}"
	done
	md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
909 910
	sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
	echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
911 912
}

913
stripbin() {
914
	local bin
915
	options_has "!strip" && return 0
916 917
	cd "${subpkgdir:-$pkgdir}" || return 1
	msg "Stripping binaries"
918 919 920
	scanelf --recursive --nobanner --etype "ET_DYN,ET_EXEC" . \
		| sed -e 's:^ET_DYN ::' -e 's:^ET_EXEC ::' \
		| xargs -r strip
921 922
}

923 924 925
# simply list target apks
listpkg() {
	local i 
926
	getpkgver || return 1
927 928 929 930 931
	for i in $pkgname $subpackages; do
		echo "${i%:*}-$pkgver-r$pkgrel.apk"
	done
}

Natanael Copa's avatar
Natanael Copa committed
932 933 934 935 936 937 938 939
source_has() {
	local i
	for i in $source; do
		[ "$1" = "${i##*/}" ] && return 0
	done
	return 1
}

Natanael Copa's avatar
abuild:  
Natanael Copa committed
940 941 942 943 944 945 946 947
subpackages_has() {
	local i
	for i in $subpackages; do
		[ "$1" = "${i%:*}" ] && return 0
	done
	return 1
}

948 949 950 951 952 953 954 955 956 957 958
list_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

959 960 961 962 963 964
# same as list_has but we filter version info
deplist_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
965
		i=${i%%[<>=]*}
966 967 968 969 970 971
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

972 973 974 975
options_has() {
	list_has "$1" $options
}

976
depends_has() {
977
	deplist_has "$1" $depends
978 979
}

Natanael Copa's avatar
Natanael Copa committed
980
makedepends_has() {
981
	deplist_has "$1" $makedepends
Natanael Copa's avatar
Natanael Copa committed
982 983
}

Natanael Copa's avatar
Natanael Copa committed
984 985 986 987
md5sums_has() {
	list_has "$1" $md5sums
}

Natanael Copa's avatar
Natanael Copa committed
988 989 990 991
install_has() {
	list_has "$1" $install
}

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

1010
installdeps() {
1011
	local deps i
1012
	sudo $APK add --wait 30 --repository "$abuildrepo" \
1013
		--virtual .makedepends-$pkgname \
1014
		$makedepends
1015 1016 1017
}

uninstalldeps (){
1018
	sudo $APK del .makedepends-$pkgname
1019
}
1020 1021 1022 1023 1024

all() {
	if up2date && [ -z "$force" ]; then
		msg "Package is up to date"
	else
1025
		update_abuildrepo
1026 1027 1028
	fi
}

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

APKBUILD="${APKBUILD:-./APKBUILD}"
1074 1075
unset force
unset recursive
1076
while getopts "cdfFhi:kimnp:P:qrRs:u" opt; do
Natanael Copa's avatar
Natanael Copa committed
1077
	case $opt in
1078 1079
		'c') default_colors
		     color_opt="-c";;
1080
		'd') nodeps=1;;
1081 1082
		'f') force="-f";;
		'F') forceroot="-F";;
1083 1084
		'h') usage;;
		'i') install_after="$install_after $OPTARG";;