abuild.in 25.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

Natanael Copa's avatar
Natanael Copa committed
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
#colors
if [ -n "$USE_COLORS" ]; then
	NORMAL="\033[1;0m"
	STRONG="\033[1;1m"
	RED="\033[1;31m"
	GREEN="\033[1;32m"
	YELLOW="\033[1;33m"
	BLUE="\033[1;34m"
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
62 63

	
Linux User's avatar
Linux User committed
64 65
set_xterm_title() {
	if [ "$TERM" = xterm ]; then
66
		 printf "\033]0;$1\007" >&2
Linux User's avatar
Linux User committed
67 68 69 70 71
	fi
} 

cleanup() {
	set_xterm_title ""
72
	if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
73
		$SUDO $APK del $uninstall_after
74
	fi
Linux User's avatar
Linux User committed
75 76
}

Natanael Copa's avatar
Natanael Copa committed
77
die() {
Linux User's avatar
Linux User committed
78
	error "$@"
Linux User's avatar
Linux User committed
79
	cleanup
Natanael Copa's avatar
Natanael Copa committed
80 81 82
	exit 1
}

83 84
# check if apkbuild is basicly sane
sanitycheck() {
Natanael Copa's avatar
Natanael Copa committed
85
	local i
86
	msg "Checking sanity of $APKBUILD..."
87 88 89
	[ -z "$pkgname" ] && die "Missing pkgname in APKBUILD"
	[ -z "${pkgname##* *}" ] && die "pkgname contains spaces"
	[ -z "$pkgver" ] && die "Missing pkgver in APKBUILD"
90
	if [ "$pkgver" != "volatile" ] && [ -z "$nodeps" ]; then
91
		$APK version --check -q "$pkgver" ||\
Natanael Copa's avatar
Natanael Copa committed
92 93
			die "$pkgver is not a valid version"
	fi
94
	[ -z "$pkgrel" ] && die "Missing pkgrel in APKBUILD"
95 96 97 98
	[ -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
99 100 101 102 103 104 105
	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"

106 107
	if [ -n "$source" ]; then
		for i in $source; do
Natanael Copa's avatar
Natanael Copa committed
108 109 110 111
			if install_has "$i"; then
				warning "You should not have \$install in source"
				continue
			fi
112
			md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
Natanael Copa's avatar
Natanael Copa committed
113 114 115
			case "$i" in
				https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
			esac
116 117
		done
	fi
Natanael Copa's avatar
Natanael Copa committed
118

119 120
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
121
			source_has $i || die "$i exists in md5sums but is missing in source"
122 123
		done
	fi
124

125 126 127
	# 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
128 129

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
132 133 134 135
	return 0
}

md5check() {
Natanael Copa's avatar
Natanael Copa committed
136
	local dummy f
137 138 139
	if [ -z "$source" ]; then
		return 0
	fi
140
	if [ -z "$md5sums" ]; then
141
		die "Use 'abuild checksum' to generate/update the checksum(s)"
142
	fi
143 144 145
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
146
	fetch || return 1
147
	msg "Checking md5sums..."
148
	cd "$srcdir" && echo "$md5sums" | md5sum -c 
Natanael Copa's avatar
Natanael Copa committed
149 150 151 152
}

uri_fetch() {
	local uri="$1"
153
	local d="${uri##*/}"	# $(basename $uri)
154 155 156 157
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

Natanael Copa's avatar
Natanael Copa committed
158 159 160 161 162
	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
	
Natanael Copa's avatar
Natanael Copa committed
163
	mkdir -p "$SRCDEST"
164 165 166
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
167
	fi
Linux User's avatar
Linux User committed
168
	msg "Fetching $uri"
169 170
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
171 172
}

173 174
is_remote() {
	case "$1" in
Natanael Copa's avatar
Natanael Copa committed
175
		http://*|ftp://*|https://*)
176 177 178 179 180
			return 0;;
	esac
	return 1
}

181 182 183 184 185 186 187 188 189 190
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
		uri_fetch "$DISTFILES_MIRROR"/$d && return 0
	fi
	uri_fetch "$uri"
}

191
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
192 193
	local s
	mkdir -p "$srcdir"
194 195
	for s in $source; do
		if is_remote "$s"; then
196
			uri_fetch_mirror "$s" || return 1
197 198 199 200
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
201 202 203
	done
}

204 205 206 207
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
208
# unpack the sources
209
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
210
	local u
211
	md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
212 213 214 215
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
216
			*.tar.gz|*.tgz)
217
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
218 219
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
220
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
221
				tar -C "$srcdir" -jxf "$s" || return 1;;
222 223 224 225
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
Natanael Copa's avatar
Natanael Copa committed
226 227 228
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
229 230 231 232
		esac
	done
}

233 234 235 236
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
237 238
# cleanup source and package dir
clean() {
239
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
240
	rm -rf "$srcdir"
241
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
242 243 244 245 246 247
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
248 249 250 251
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
252 253 254 255 256
	done
}

cleanpkg() {
	local i
257
	getpkgver || return 1
258
	msg "Cleaning built packages..."
259
	for i in $pkgname $subpackages; do
260 261
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
262
			"$abuildrepo"/$p.apk
Natanael Copa's avatar
Natanael Copa committed
263
	done
264
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
265 266
}

267 268 269
# clean all packages except current
cleanoldpkg() {
	local i j
270
	getpkgver || return 1
271 272 273 274 275 276 277 278 279
	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
}
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297

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
298 299
runpart() {
	local part=$1
300
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
301 302 303 304
	$part || die "$part failed"
}

# override those in your build script
305 306 307 308 309 310 311 312
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
313 314 315 316
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
317
build() {
318
	:
Natanael Copa's avatar
Natanael Copa committed
319 320
}

321 322 323 324 325 326
# 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
327 328 329 330 331 332 333 334 335
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
}

336
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
337
	if [ -z "$subpackages" ]; then
338 339
		return 0
	fi
Natanael Copa's avatar
Natanael Copa committed
340 341
	local i
	cd "$startdir"
342
	for i in $subpackages; do
Natanael Copa's avatar
Natanael Copa committed
343 344
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
345
		msg "Running split function $func..."
346
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
347
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
348 349 350
	done
}

351
prepare_metafiles() {
352
	getpkgver || return 1
353 354
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
355
	local dir=${subpkgdir:-$pkgdir}
356
	local pkg="$name-$pkgver-r$pkgrel.apk"
357
	local pkginfo="$controldir"/.PKGINFO
358
	local sub
Natanael Copa's avatar
Natanael Copa committed
359 360 361
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
362
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
363 364 365
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')

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

	local metafiles=".PKGINFO"
Natanael Copa's avatar
Natanael Copa committed
415
	for i in $install ${triggers%%:*}; do
416 417
		script=${i#$name}
		case "$script" in
Natanael Copa's avatar
Natanael Copa committed
418
			.pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
419 420
				msg "Adding $script"
				;;
Natanael Copa's avatar
Natanael Copa committed
421
			*) 	error "$script: Invalid install/trigger script"
422 423 424
				return 1
				;;
		esac
Natanael Copa's avatar
Natanael Copa committed
425
		cp "$startdir/$i" "$controldir/$script" || return 1
426
		chmod +x "$controldir/$script"
427 428
		metafiles="$metafiles $script"
	done
429
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
430
}
431

432 433 434
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
	options_has "!tracedeps" && return 0
435 436
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
437
	scanelf -Rn "$dir" | awk -F "\ " '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
438 439 440
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
441 442
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
443
	done
Natanael Copa's avatar
Natanael Copa committed
444 445
}

446
prepare_package() {
447
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
448
	stripbin
449
	prepare_metafiles && prepare_tracedeps
450 451 452 453 454 455 456 457
}

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

458 459 460
trace_apk_deps() {
	local name="$1"
	local dir="$2"
461 462
	local i j found autodeps=
	msg "Tracing dependencies for $name..."
463 464 465 466 467 468
	# 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
469
	for i in $(cat "$dir"/.needs-so 2>/dev/null); do
470
		found=
471
		# first check if its provide by same apkbuild
472
		for j in "$dir"/../.control.*/.provides-so; do
473
			grep -w "$i" "$j" >/dev/null || continue
474
			found=${j%/.provides-so}
475
			found=${found##*/.control.}
476
			break
477 478 479
		done
		# check apk db if not provided by a subpackage
		if [ -z "$found" ]; then
480
			found=$($APK info -q -W /lib/$i /usr/lib/$i)
481 482 483 484 485
		fi
		if [ -z "$found" ]; then
			error "Could not find dependency for $i"
			return 1
		fi
486
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
487 488 489
			warning "You can remove '$found' from depends"
			continue
		fi
490 491
		list_has "$found" $autodeps || autodeps="$autodeps $found"
		msg "Added '$found' as dependency as it has $i"
492 493 494 495 496 497 498 499
	done
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

500 501
create_apks() {
	local file
502
	getpkgver || return 1
503
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
504 505 506
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
507
		local apk=$name-$ver.apk
508
		local datadir="$pkgbasedir"/$name
509 510

		trace_apk_deps "$name" "$dir" || return 1
511
		msg "Creating $apk..."
512
		(
513 514
		cd "$datadir"
		# data.tar.gz
515 516 517 518 519 520
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
521 522 523 524 525 526

		# 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
527
		cd "$dir"
528 529
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
530
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
531

532 533 534
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
535 536 537
	done
}

538
update_abuildrepo() {
539
	if ! apk_up2date || [ -n "$force" ]; then
540
		sanitycheck && builddeps && clean && fetch && unpack \
Natanael Copa's avatar
Natanael Copa committed
541
			&& prepare && mkusers && rootpkg || return 1
542
	fi
543

544
	local apk
545 546
	mkdir -p "$abuildrepo" || return 1
	cd "$abuildrepo"
547 548 549 550 551 552 553 554 555

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

	# create links for this package
556
	for apk in $(listpkg); do
557
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$apk
558 559
	done

560
	msg "Updating the cached abuild repository index..."
561 562 563 564 565
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
	if [ -f APKINDEX.tar.gz ]; then
		oldindex="--index APKINDEX.tar.gz"
	fi
566
	$APK index $oldindex --output APKINDEX.tar.gz.unsigned \
567 568
		--description "$repo $(cd $startdir && git describe)" \
		*.apk || exit 1
569 570 571
	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
572
	chmod 644 APKINDEX.tar.gz
573 574
}

575
# predefined splitfunc doc
576
default_doc() {
577
	depends="$depends_doc"
578
	install="$install_doc"
Natanael Copa's avatar
Natanael Copa committed
579
	triggers="$triggers_doc"
Natanael Copa's avatar
Natanael Copa committed
580
	local i
581
	for i in doc man info html sgml licenses gtk-doc; do
582 583 584
		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
585 586
		fi
	done
587 588 589
	
	rm -f "$subpkgdir/usr/share/info/dir"

590 591 592
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
593 594 595 596

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

Natanael Copa's avatar
Natanael Copa committed
597
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
598 599 600
	return 0
}

601 602 603 604
doc() {
	default_doc
}

605
# predefined splitfunc mod
606
default_mod() {
607
	depends="$kernel $depends_mod"
608
	install="$install_mod"
Natanael Copa's avatar
Natanael Copa committed
609
	for i in firmware modules; do
610 611 612
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
613
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
614 615 616 617
		fi
	done
}

618 619 620 621
mod() {
	default_mod
}

622
# predefined splitfunc dev
623
default_dev() {
624
	local i
625
	depends="$pkgname $depends_dev"
626
	install="$install_dev"
Natanael Copa's avatar
Natanael Copa committed
627
	triggers="$triggers_dev"
628 629 630 631
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
632
	cd "$pkgdir" || return 0
633 634 635 636 637
	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
638 639
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
640
			mkdir -p "$d"
641
			mv "$pkgdir/$i" "$d"
642
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
643 644
		fi
	done
Natanael Copa's avatar
Natanael Copa committed
645
	return 0
Natanael Copa's avatar
Natanael Copa committed
646 647
}

648 649 650 651
dev() {
	default_dev
}

652 653 654 655
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

Natanael Copa's avatar
Natanael Copa committed
656 657
# build and package in fakeroot
rootpkg() {
658 659 660 661
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
662
		do_build=package
663
	fi
Natanael Copa's avatar
Natanael Copa committed
664
	cd "$startdir"
665 666 667 668
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
	$FAKEROOT "$abuild_path" $do_build \
		prepare_subpackages \
		prepare_package \
669
		create_apks
Natanael Copa's avatar
Natanael Copa committed
670 671 672
}

srcpkg() {
673
	getpkgver || return 1
674
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
675
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
676 677 678 679 680
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
681
	msg "Creating source package $p.src.tar.gz..."
682
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
683 684
}

685
# check if package is up to date
686
apk_up2date() {
687
	getpkgver || return 1
688
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
689
	local i s
Natanael Copa's avatar
Natanael Copa committed
690
	cd "$startdir"
691 692 693 694 695
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

696
	for i in $source APKBUILD; do
697 698 699 700 701 702
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
703 704 705 706 707 708 709
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

710
abuildindex_up2date() {
711
	local i apk
712
	getpkgver || return 1
713 714
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
715
		[ "$abuildrepo"/APKINDEX.tar.gz -nt "$abuildrepo"/$apk ] || return 1
716 717 718 719 720
	done
	return 0
}

up2date() {
721 722 723 724 725 726 727
	apk_up2date && abuildindex_up2date
}

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

730 731 732 733 734 735 736
# 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
	(
737 738
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
739 740 741 742
		pkgname=
		subpackages=
		depends=
		makedepends=
743
		. $i
744
		dir=${i%/APKBUILD}
745
		deps=
746
		# filter out conflicts from deps and version info
747 748 749 750
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
751
			deps="$deps ${j%[<>=]*}"
752
		done
753
		for j in $pkgname $subpackages; do
754
			echo "o ${j%%:*} $dir"
755
			set -- $deps
756 757 758 759 760 761 762
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
763 764
		done
	done
765
	)
766 767
}

768
deptrace() {
769 770 771 772 773
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
		deps="$deps ${i%[<>=]*}"
	done
774
	[ -z "$deps" ] && return 0
775 776 777
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
778
			$APK info -q | sort |  sed 's/^/i /'
779
		fi
780
	) | awk -v pkgs="$deps" '
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805

	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() {
806 807
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
	local filtered_deps=
808
	[ -n "$nodeps" ] && return 0
809
	msg "Analyzing dependencies..."
810 811 812 813
	deps="$BUILD_BASE $makedepends"

	# add depends unless it is a subpackage
	for i in $depends; do
814
		subpackages_has ${i%[<>=]*} || deps="$deps $i"
815 816
	done

817
	installed_deps=$($APK info -e $deps)
818
	# find which deps are missing
819
	for i in $deps; do
820
		if [ "${i#\!}" != "$i" ]; then
821
			list_has ${i#\!} $installed_deps \
822
				&& die "Conflicting package ${i#\!} is installed."
823
		elif ! deplist_has $i $installed_deps; then
824 825 826 827 828 829 830
			if [ -z "$install_deps" ] && [ -z "$recursive" ]; then
				die "Missing dependency $i. Use -r to autoinstall or -R to build"
			fi
			missing="$missing $i"
		fi
	done

831 832
	[ -z "$missing" ] && [ -z "$install_deps" ] && [ -z "$recursive" ] \
		&& return 0
833

834
	uninstall_after=".makedepends-$pkgname $uninstall_after"
835
	if [ -n "$install_deps" ] && [ -z "$recursive" ]; then
836 837
		# make a --simluate run first to detect missing deps
		# apk-tools --virtual is no goot at reporting those.
838
		$SUDO $APK add --repository "$abuildrepo" \
839 840
			--wait 30 \
			--simulate --quiet $deps || return 1
841
		$SUDO $APK add --repository "$abuildrepo" \
842
			--wait 30 \
843 844
			--virtual .makedepends-$pkgname $deps \
			&& return 0
845
	fi
846 847
	
	[ -z "$recursive" ] && return 1
848 849 850

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
851
		local m=$($APK search --repository "$abuildrepo" ${i%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
852
		if [ -z "$m" ]; then
853 854 855
			missing="$missing $i"
		fi
	done
856 857
	
	for i in $(deptrace $missing); do
858 859 860
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
Natanael Copa's avatar
Natanael Copa committed
861 862
		if [ -d "$dir" ]; then
			msg "Entering $dir"
863
			cd "$dir" && $0 -k -r abuildindex || return 1
Natanael Copa's avatar
Natanael Copa committed
864
		fi
865
	done
866
	$SUDO $APK add -u --repository "$abuildrepo" \
867
		--wait 30 \
868
		--virtual .makedepends-$pkgname $deps
869 870
}

871
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
872 873
checksum() {
	local s files
874
	[ -z "$source" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
875
	fetch
876
	msg "Updating the md5sums in APKBUILD..."
Natanael Copa's avatar
Natanael Copa committed
877 878 879 880
	for s in $source; do
		files="$files ${s##*/}"
	done
	md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
881 882
	sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
	echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
883 884
}

885
stripbin() {
886
	local bin
887
	options_has "!strip" && return 0
888 889
	cd "${subpkgdir:-$pkgdir}" || return 1
	msg "Stripping binaries"
890 891 892
	scanelf --recursive --nobanner --etype "ET_DYN,ET_EXEC" . \
		| sed -e 's:^ET_DYN ::' -e 's:^ET_EXEC ::' \
		| xargs -r strip
893 894
}

895 896 897
# simply list target apks
listpkg() {
	local i 
898
	getpkgver || return 1
899 900 901 902 903
	for i in $pkgname $subpackages; do
		echo "${i%:*}-$pkgver-r$pkgrel.apk"
	done
}

Natanael Copa's avatar
Natanael Copa committed
904 905 906 907 908 909 910 911
source_has() {
	local i
	for i in $source; do
		[ "$1" = "${i##*/}" ] && return 0
	done
	return 1
}

Natanael Copa's avatar
abuild:  
Natanael Copa committed
912 913 914 915 916 917 918 919
subpackages_has() {
	local i
	for i in $subpackages; do
		[ "$1" = "${i%:*}" ] && return 0
	done
	return 1
}

920 921 922 923 924 925 926 927 928 929 930
list_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

931 932 933 934 935 936 937 938 939 940 941 942 943
# same as list_has but we filter version info
deplist_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		i=${i%[<>=]*}
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

944 945 946 947
options_has() {
	list_has "$1" $options
}

948
depends_has() {
949
	deplist_has "$1" $depends
950 951
}

Natanael Copa's avatar
Natanael Copa committed
952
makedepends_has() {
953
	deplist_has "$1" $makedepends
Natanael Copa's avatar
Natanael Copa committed
954 955
}

Natanael Copa's avatar
Natanael Copa committed
956 957 958 959
md5sums_has() {
	list_has "$1" $md5sums
}

Natanael Copa's avatar
Natanael Copa committed
960 961 962 963
install_has() {
	list_has "$1" $install
}

Natanael Copa's avatar
abuild:  
Natanael Copa committed
964 965
# install package after build
post_add() {
966
	getpkgver || return 1
Natanael Copa's avatar
abuild:  
Natanael Copa committed
967 968 969 970 971 972
	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
973
	deps=$($APK index "$pkgf" 2>/dev/null | awk -F: '$1=="D" { print $2 }')
Natanael Copa's avatar
abuild:  
Natanael Copa committed
974 975 976 977 978
	for i in $deps; do
		if subpackages_has $i || [ "$i" = "$pkgname" ]; then
			post_add $i || return 1
		fi
	done
979
	$SUDO $APK add --wait 30 -u "$pkgf" || die "Failed to install $1"
Natanael Copa's avatar
abuild:  
Natanael Copa committed
980 981
}

982
installdeps() {
983
	local deps i
984
	sudo $APK add --wait 30 --repository "$abuildrepo" \
985
		--virtual .makedepends-$pkgname \
986
		$makedepends
987 988 989
}

uninstalldeps (){
990
	sudo $APK del .makedepends-$pkgname
991
}
992 993 994 995 996

all() {
	if up2date && [ -z "$force" ]; then
		msg "Package is up to date"
	else
997
		update_abuildrepo
998 999 1000
	fi
}

Natanael Copa's avatar
Natanael Copa committed
1001
usage() {
1002 1003
	echo "$program $abuild_ver"
	echo "usage: $program [options] [-i PKG] [-P REPODEST] [-p PKGDEST]"
1004
	echo "              [-s SRCDEST] [cmd] ..."
1005
	echo "       $program [-c] -n PKGNAME[-PKGVER]"
Natanael Copa's avatar
Natanael Copa committed
1006
	echo "Options:"
1007
	echo " -d  Disable dependency checking"
Natanael Copa's avatar
Natanael Copa committed
1008
	echo " -f  Force specified cmd, even if they are already done"
1009
	echo " -F  Force run as root"
1010 1011
	echo " -h  Show this help"
	echo " -i  Install PKG after successul build"
1012
	echo " -k  Keep built packages, even if APKBUILD or sources are newer"
1013
	echo " -p  Set package destination directory"
1014
	echo " -P  Set PKGDEST to REPODEST/<repo>, where repo is the parents dir name" 
Natanael Copa's avatar
Natanael Copa committed
1015
	echo " -q  Quiet"
1016 1017
	echo " -r  Install missing dependencies from system repository (using sudo)"
	echo " -R  Recursively build and install missing dependencies (using sudo)"
1018
	echo " -s  Set source package destination directory"
1019
	echo " -u  Recursively build and upgrade all dependencies (using sudo)"
Natanael Copa's avatar
Natanael Copa committed
1020 1021
	echo ""
	echo "Commands:"
1022
	echo "  checksum    Generate checksum to be included in APKBUILD"
1023 1024 1025 1026 1027
	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"
1028
	echo "  listpkg     List target packages"
1029 1030 1031
	echo "  package     Create package in \$PKGDEST"
	echo "  rootpkg     Run '$0 build package' as fakeroot"
	echo "  clean       Remove temp build and install dirs"
1032
	echo "  cleanoldpkg Remove binary packages except current version"
1033 1034 1035 1036
	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"
1037 1038
	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
1039 1040 1041 1042 1043
	echo ""
	exit 0
}

APKBUILD="${APKBUILD:-./APKBUILD}"
1044 1045
unset force
unset recursive
1046
while getopts "dfFhi:kinp:P:qrRs:u" opt; do
Natanael Copa's avatar
Natanael Copa committed
1047
	case $opt in
1048
		'd') nodeps=1;;
Natanael Copa's avatar
Natanael Copa committed
1049
		'f') force=1;;
1050
		'F') forceroot=1;;
1051 1052
		'h') usage;;
		'i') install_after="$install_after $OPTARG";;
1053
		'k') keep=1;;
1054
		'n') die "Use newapkbuild to create new aports";;
1055
		'p') PKGDEST=$OPTARG;;
1056
		'P') REPODEST=$OPTARG;;
Natanael Copa's avatar
Natanael Copa committed
1057
		'q') quiet=1;;
1058 1059
		'r') install_deps=1;;
		'R') recursive=1;;
1060
		's') SRCDEST=$OPTARG;;
1061 1062
		'u') upgrade=1
		     recursive=1;;
Natanael Copa's avatar
Natanael Copa committed
1063 1064 1065 1066
	esac
done
shift $(( $OPTIND - 1 ))

1067 1068 1069 1070 1071 1072 1073
# check so we are not root
if [ "$(whoami)" = "root" ] && [ -z "$FAKEROOTKEY" ]; then
	[ -z "$forceroot" ] && die "Do not run abuild as root"
	SUDO=
	FAKEROOT=
fi

1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
# find startdir
[ -f "$APKBUILD" ] || die "Could not find $APKBUILD (PWD=$PWD)"
APKBUILD=$(readlink -f "$APKBUILD")

startdir="${APKBUILD%/*}"
srcdir=${srcdir:-"$startdir/src"}
pkgbasedir=${pkgbasedir:-"$startdir/pkg"}
pkgrel=0
repo=${startdir%/*}
repo=${repo##*/}

SRCDEST=${SRCDEST:-$startdir}
PKGDEST=${PKGDEST:-$startdir}

cd "$startdir" || die
. "$APKBUILD"

1091 1092 1093 1094