abuild.in 26 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
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
772
		deps="$deps ${i%%[<>=]*}"
773
	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
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
807
	local filtered_deps= conflicts=
808
	[ -n "$nodeps" ] && return 0
809
	msg "Analyzing dependencies..."
810

811 812 813
	# add depends unless it is a subpackage or package itself
	for i in $BUILD_BASE $depends $makedepends; do
		[ "$pkgname" = "${i%%[<>=]*}" ] && continue
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
				&& conflicts="$conflicts ${i#\!}"
823
		elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
824 825 826 827
			missing="$missing $i"
		fi
	done

828 829 830 831 832 833 834 835 836 837
	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
838

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

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
856
		local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
857
		if [ -z "$m" ]; then
858 859 860
			missing="$missing $i"
		fi
	done
861 862
	
	for i in $(deptrace $missing); do
863 864 865
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
866 867 868 869 870 871 872 873 874 875 876

		# 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 \
			$recursive $upgrade abuildindex || return 1
877
	done
878
	$SUDO $APK add -u --repository "$abuildrepo" \
879
		--wait 30 \
880
		--virtual .makedepends-$pkgname $deps
881 882
}

883
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
884 885
checksum() {
	local s files
886
	[ -z "$source" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
887
	fetch
888
	msg "Updating the md5sums in APKBUILD..."
Natanael Copa's avatar
Natanael Copa committed
889 890 891 892
	for s in $source; do
		files="$files ${s##*/}"
	done
	md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
893 894
	sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
	echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
895 896
}

897
stripbin() {
898
	local bin
899
	options_has "!strip" && return 0
900 901
	cd "${subpkgdir:-$pkgdir}" || return 1
	msg "Stripping binaries"
902 903 904
	scanelf --recursive --nobanner --etype "ET_DYN,ET_EXEC" . \
		| sed -e 's:^ET_DYN ::' -e 's:^ET_EXEC ::' \
		| xargs -r strip
905 906
}

907 908 909
# simply list target apks
listpkg() {
	local i 
910
	getpkgver || return 1
911 912 913 914 915
	for i in $pkgname $subpackages; do
		echo "${i%:*}-$pkgver-r$pkgrel.apk"
	done
}

Natanael Copa's avatar
Natanael Copa committed
916 917 918 919 920 921 922 923
source_has() {
	local i
	for i in $source; do
		[ "$1" = "${i##*/}" ] && return 0
	done
	return 1
}

Natanael Copa's avatar
abuild:  
Natanael Copa committed
924 925 926 927 928 929 930 931
subpackages_has() {
	local i
	for i in $subpackages; do
		[ "$1" = "${i%:*}" ] && return 0
	done
	return 1
}

932 933 934 935 936 937 938 939 940 941 942
list_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

943 944 945 946 947 948
# same as list_has but we filter version info
deplist_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
949
		i=${i%%[<>=]*}
950 951 952 953 954 955
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

956 957 958 959
options_has() {
	list_has "$1" $options
}

960
depends_has() {
961
	deplist_has "$1" $depends
962 963
}

Natanael Copa's avatar
Natanael Copa committed
964
makedepends_has() {
965
	deplist_has "$1" $makedepends
Natanael Copa's avatar
Natanael Copa committed
966 967
}

Natanael Copa's avatar
Natanael Copa committed
968 969 970 971
md5sums_has() {
	list_has "$1" $md5sums
}

Natanael Copa's avatar
Natanael Copa committed
972 973 974 975
install_has() {
	list_has "$1" $install
}

Natanael Copa's avatar
abuild:  
Natanael Copa committed
976 977
# install package after build
post_add() {
978
	getpkgver || return 1
Natanael Copa's avatar
abuild:  
Natanael Copa committed
979 980 981 982 983 984
	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
985
	deps=$($APK index "$pkgf" 2>/dev/null | awk -F: '$1=="D" { print $2 }')
Natanael Copa's avatar
abuild:  
Natanael Copa committed
986 987 988 989 990
	for i in $deps; do
		if subpackages_has $i || [ "$i" = "$pkgname" ]; then
			post_add $i || return 1
		fi
	done
991
	$SUDO $APK add --wait 30 -u "$pkgf" || die "Failed to install $1"
Natanael Copa's avatar
abuild:  
Natanael Copa committed
992 993
}

994
installdeps() {
995
	local deps i
996
	sudo $APK add --wait 30 --repository "$abuildrepo" \
997
		--virtual .makedepends-$pkgname \
998
		$makedepends
999 1000 1001
}

uninstalldeps (){
1002
	sudo $APK del .makedepends-$pkgname
1003
}
1004 1005 1006 1007 1008

all() {
	if up2date && [ -z "$force" ]; then
		msg "Package is up to date"
	else
1009
		update_abuildrepo
1010 1011 1012
	fi
}

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

APKBUILD="${APKBUILD:-./APKBUILD}"
1056 1057
unset force
unset recursive
1058
while getopts "dfFhi:kinp:P:qrRs:u" opt; do
Natanael Copa's avatar
Natanael Copa committed
1059
	case $opt in
1060
		'd') nodeps=1;;
1061 1062
		'f') force="-f";;
		'F') forceroot="-F";;
1063 1064
		'h') usage;;
		'i') install_after="$install_after $OPTARG";;
1065
		'k') keep="-k";;
1066
		'n') die "Use newapkbuild to create new aports";;
1067
		'p') PKGDEST=$OPTARG;;
1068
		'P') REPODEST=$OPTARG;;
1069 1070 1071
		'q') quiet="-q";;
		'r') install_deps="-r";;
		'R') recursive="-R";;
1072
		's') SRCDEST=$OPTARG;;
1073 1074
		'u') upgrade="-u"
		     recursive="-R";;
Natanael Copa's avatar
Natanael Copa committed
1075 1076 1077 1078
	esac
done
shift $(( $OPTIND - 1 ))

1079 1080 1081 1082 1083 1084 1085
# check so we are not root
if [ "$(whoami)" = "root" ] && [ -z "$FAKEROOTKEY" ]; then
	[ -z "$forceroot" ] && die "Do not run abuild as root"
	SUDO=
	FAKEROOT=
fi