abuild.in 38.9 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_base=@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
FAKEROOT=${FAKEROOT:-"fakeroot"}
22

23 24
: ${SUDO_APK:=abuild-apk}
: ${APK:=apk}
25 26 27
: ${ADDUSER:=abuild-adduser}
: ${ADDGROUP:=abuild-addgroup}

28
apk_opt_wait="--wait 30"
29

Natanael Copa's avatar
Natanael Copa committed
30
# read config
Natanael Copa's avatar
Natanael Copa committed
31
ABUILD_CONF=${ABUILD_CONF:-"$sysconfdir/abuild.conf"}
Natanael Copa's avatar
Natanael Copa committed
32 33
[ -f "$ABUILD_CONF" ] && . "$ABUILD_CONF"

34
default_colors() {
Natanael Copa's avatar
Natanael Copa committed
35 36 37 38 39 40
	NORMAL="\033[1;0m"
	STRONG="\033[1;1m"
	RED="\033[1;31m"
	GREEN="\033[1;32m"
	YELLOW="\033[1;33m"
	BLUE="\033[1;34m"
41 42 43 44 45 46 47 48 49 50 51 52 53 54
}

monochrome() {
	NORMAL=""
	STRONG=""
	RED=""
	GREEN=""
	YELLOW=""
	BLUE=""
}

#colors
if [ -n "$USE_COLORS" ]; then
    default_colors
Natanael Copa's avatar
Natanael Copa committed
55 56 57 58 59
fi	
	

# functions
msg() {
60
	[ -n "$quiet" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
61 62 63
	local prompt="$GREEN>>>${NORMAL}"
	local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
	local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
64 65 66 67 68 69 70
	printf "${prompt} ${name}${fake}: %s\n" "$1" >&2
}

msg2() {
	[ -n "$quiet" ] && return 0
	#      ">>> %s"
	printf "    %s\n" "$1" >&2
Natanael Copa's avatar
Natanael Copa committed
71 72 73 74 75 76
}

warning() {
	local prompt="${YELLOW}>>> WARNING:${NORMAL}"
	local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
	local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
77 78 79 80 81 82
	printf "${prompt} ${name}${fake}: %s\n" "$1" >&2
}

warning2() {
	#      ">>> WARNING: 
	printf "             %s\n" "$1" >&2
Natanael Copa's avatar
Natanael Copa committed
83 84 85 86 87 88
}

error() {
	local prompt="${RED}>>> ERROR:${NORMAL}"
	local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
	local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
89 90 91 92 93 94
	printf "${prompt} ${name}${fake}: %s\n" "$1" >&2
}

error2() {
	#      ">>> ERROR: 
	printf "           %s\n" "$1" >&2
Natanael Copa's avatar
Natanael Copa committed
95
}
96

Linux User's avatar
Linux User committed
97
set_xterm_title() {
98
	if [ "$TERM" = xterm ] && [ -n "$USE_COLORS" ]; then
99
		 printf "\033]0;$1\007" >&2
Linux User's avatar
Linux User committed
100 101 102 103 104
	fi
} 

cleanup() {
	set_xterm_title ""
105
	if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
106
		msg "Uninstalling dependencies..."
107
		$SUDO_APK del --quiet $apk_opt_wait $uninstall_after
108
	fi
109 110 111
	if [ -n "$CLEANUP_FILES" ]; then
		rm -f $CLEANUP_FILES
	fi
Linux User's avatar
Linux User committed
112 113
}

Natanael Copa's avatar
Natanael Copa committed
114
die() {
Linux User's avatar
Linux User committed
115
	error "$@"
Linux User's avatar
Linux User committed
116
	cleanup
Natanael Copa's avatar
Natanael Copa committed
117 118 119
	exit 1
}

120
# check if apkbuild is basicly sane
121
default_sanitycheck() {
122
	local i= j= suggestion=
123
	msg "Checking sanity of $APKBUILD..."
124 125 126
	[ -z "$pkgname" ] && die "Missing pkgname in APKBUILD"
	[ -z "${pkgname##* *}" ] && die "pkgname contains spaces"
	[ -z "$pkgver" ] && die "Missing pkgver in APKBUILD"
127
	if [ "$pkgver" != "volatile" ] && [ -z "$nodeps" ]; then
128
		$APK version --check -q "$pkgver" ||\
129 130
			die "$pkgver is not a valid version"
	fi
131
	[ -z "$pkgrel" ] && die "Missing pkgrel in APKBUILD"
132 133 134 135
	[ -z "$pkgdesc" ] && die "Missing pkgdesc in APKBUILD"
	[ -z "$url" ] && die "Missing url in APKBUILD"
	[ -z "$license" ] && die "Missing license in APKBULID"

136 137
	if [ -n "$replaces_priority" ] \
		&& ! echo $replaces_priority | egrep -q '^[0-9]+$'; then
138 139
		die "replaces_priority must be a number"
	fi
140 141 142 143 144 145 146
	# check so no package names starts with -
	for i in $pkgname $subpackages; do
		case $i in
		-*) die "${i%:*} is not a valid package name";;
		esac
	done

147 148 149 150 151 152 153 154 155 156
	# check if CARCH, CBUILD, CHOST and CTARGET is set
	if [ -z "$CARCH" ]; then
		case "$(uname -m)" in
		i[0-9]86) suggestion=" (Suggestion: CARCH=x86)";;
		x86_64) suggestion=" (Suggestion: CARCH=x86_64)";;
		esac
		die "Please set CARCH in /etc/abuild.conf$suggestion"
	fi
	[ -z "$CHOST" ] && die "Please set CHOST in /etc/abuild.conf"

Natanael Copa's avatar
Natanael Copa committed
157
	for i in $install; do
158
		local n=${i%.*}
159
		local suff=${i##*.}
160 161 162 163 164
		case "$suff" in
		pre-install|post-install|pre-upgrade|post-upgrade|pre-deinstall|post-deinstall);;
		*) die "$i: unknown install script suffix"
		esac
		if ! subpackages_has "$n" && [ "$n" != "$pkgname" ]; then
165
			die "$i: install script does not match pkgname or any subpackage"
166
		fi
167
		[ -e "$startdir/$i" ] || die "install script $i is missing"
168 169 170 171 172 173
		for j in chown chmod chgrp; do
			if grep -q $j "$startdir"/$i; then
				warning "$i: found $j"
				warning2 "Permissions should be fixed in APKBUILD package()"
			fi
		done
Natanael Copa's avatar
Natanael Copa committed
174 175
	done
	
176 177 178 179 180 181 182 183
	for i in $triggers; do
		local f=${i%=*}
		local p=${f%.trigger}
		[ "$f" = "$i" ] && die "$f: triggers must contain '='" 
		[ "$p" = "$f" ] && die "$f: triggers scripts must have .trigger suffix"
		if ! subpackages_has "$p" && [ "$p" != "$pkgname" ]; then
			die "$p: trigger script does not match pkgname or any subpackage"
		fi
Natanael Copa's avatar
Natanael Copa committed
184

185 186
		[ -e "$startdir"/$f ] || die "trigger script $f is missing"
	done
187 188
	if [ -n "$source" ]; then
		for i in $source; do
Natanael Copa's avatar
Natanael Copa committed
189 190 191 192
			if install_has "$i"; then
				warning "You should not have \$install in source"
				continue
			fi
193
			md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
194
			case "$i" in
195
				https://*) makedepends_has wget && warning "wget no longer need to be in makedepends when source has https://" ;;
196
			esac
197 198
		done
	fi
Natanael Copa's avatar
Natanael Copa committed
199

200 201
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
202
			source_has $i || die "$i exists in md5sums but is missing in source"
203 204
		done
	fi
205

206 207 208
	# 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
209 210

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
213 214 215
	return 0
}

216 217 218 219
sanitycheck() {
	default_sanitycheck
}

220
md5check() {
221
	local dummy f endreturnval originalparams origin file
222 223 224
	if [ -z "$source" ]; then
		return 0
	fi
225
	if [ -z "$md5sums" ]; then
226
		die "Use 'abuild checksum' to generate/update the checksum(s)"
227
	fi
228 229 230
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
231
	fetch || return 1
232
	msg "Checking md5sums..."
Natanael Copa's avatar
Natanael Copa committed
233
	cd "$srcdir" || return 1
234 235 236 237 238 239 240 241
	IFS=$'\n'
	endreturnval=0
	originalparams=$@
	set -- $source
	for src in $md5sums; do
		origin=$1; shift
		echo "$src" | md5sum -c
		if [ $? -ne 0 ]; then
242
			endreturnval=1
243 244 245 246 247 248 249 250 251 252 253
			is_remote $origin || continue
			echo "Because the remote file above failed the md5sum check it will be deleted."
			echo "Rebuilding will cause it to re-download which in some cases may fix the problem."
			file=`echo "$src" | sed 's/.*[ \t\n]\(.*\)/\1/'`
			echo "Deleting: $file"
			rm $file
		fi
	done
	unset IFS
	set -- $originalparams
	return $endreturnval
Natanael Copa's avatar
Natanael Copa committed
254 255
}

256 257
# verify upstream sources
sourcecheck() {
258 259 260 261 262 263 264 265 266 267
	local uri
	for uri in $source; do
		is_remote $uri || continue
		case "$uri" in
		saveas-*://*)
			uri=${uri#saveas-}
			uri=${uri%/*}
			;;
		esac
		wget -q -s "$uri" || return 1
268
	done
269
	return 0
270
}
271

272 273 274
# convert curl options to wget options and call wget instead of curl
wget_fallback() {
	local wget_opts= outfile= opt=
275
	while getopts "C:Lko:s" opt; do
276
		case $opt in
277
		'L') ;; # --location. wget does this by default
278
		'f') ;; # --fail. wget does this by default
279 280 281 282 283 284 285 286 287 288
		'C') wget_opts="$wget_opts -c";; # --continue-at
		's') wget_opts="$wget_opts -q";; # --silent
		'o') wget_opts="$wget_opts -O $OPTARG";; # --output
		'k') wget_opts="$wget_opts --no-check-certificate";; #gnu wget
		esac
	done
	shift $(( $OPTIND - 1 ))
	wget $wget_opts "$1"
}

Natanael Copa's avatar
Natanael Copa committed
289 290
uri_fetch() {
	local uri="$1"
291
	local d="${uri##*/}"	# $(basename $uri)
292
	local opts
293
	[ -n "$quiet" ] && opts="-s"
294 295

	local lockfile="$SRCDEST/$d".lock
296

297 298 299 300 301
	# fix saveas-*://* URIs
	case "$uri" in
		# remove 'saveas-' from beginning and
		# '/filename' from end of URI
		saveas-*://*) uri="${uri:7:$(expr ${#uri} - 7 - ${#d} - 1)}";;
302
	esac
303 304

	case "$uri" in
305
		https://*) opts="-k";;
306
	esac
307

Natanael Copa's avatar
Natanael Copa committed
308
	mkdir -p "$SRCDEST"
309 310 311

	CLEANUP_FILES="$CLEANUP_FILES $lockfile"
	(
312 313
	flock -n -x 9 || msg "Waiting for ${lockfile##*/}..."
	flock -x 9
314 315 316

	[ -f "$SRCDEST/$d" ] && exit 0 # use exit since its a subshell

317 318
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
319
		opts="$opts -C -"
Natanael Copa's avatar
Natanael Copa committed
320
	fi
Linux User's avatar
Linux User committed
321
	msg "Fetching $uri"
322 323

	# fallback to wget if curl is missing. useful for bootstrapping
324
	local fetcher=
325 326
	if ! [ -x "$(which curl)" ]; then
		fetcher=wget_fallback
327 328
	else
		fetcher=curl
329
		opts="$opts -L -f -k"
330 331 332
	fi

	$fetcher $opts -o "$SRCDEST/$d.part" "$uri" \
333
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
334

335
	) 9>$lockfile
336 337 338 339

	local rc=$?
	rm -f "$lockfile"
	return $rc
Natanael Copa's avatar
Natanael Copa committed
340 341
}

342 343
is_remote() {
	case "$1" in
344
		http://*|ftp://*|https://*|saveas-*://*)
345 346 347 348 349
			return 0;;
	esac
	return 1
}

350 351 352 353 354
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
355 356 357 358 359
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
360 361 362 363
	fi
	uri_fetch "$uri"
}

364
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
365 366
	local s
	mkdir -p "$srcdir"
367 368
	for s in $source; do
		if is_remote "$s"; then
369
			uri_fetch_mirror "$s" || return 1
370 371 372 373
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
374 375 376
	done
}

377 378 379 380
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
# verify that all init.d scripts are openrc runscripts
initdcheck() {
	local i
	for i in $source; do
		case $i in
		*.initd)
			head -n 1 "$srcdir"/$i | grep -q '/sbin/runscript' \
				&& continue
			error "$i is not an openrc #!/sbin/runscript"
			return 1
			;;
		esac
	done			
}

Natanael Copa's avatar
Natanael Copa committed
396
# unpack the sources
397
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
398
	local u
399 400
	if [ -z "$force" ]; then
		md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
401
		initdcheck || return 1
402
	fi
Natanael Copa's avatar
Natanael Copa committed
403 404 405 406
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
407 408 409
			*.tar)
				msg "Unpacking $s..."
				tar -C "$srcdir" -xf "$s" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
410
			*.tar.gz|*.tgz)
411
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
412 413
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
414
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
415
				tar -C "$srcdir" -jxf "$s" || return 1;;
416 417 418 419
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
420 421 422
			*.tar.xz)
				msg "Unpacking $s..."
				unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
Natanael Copa's avatar
Natanael Copa committed
423 424 425
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
426 427 428 429
		esac
	done
}

430 431 432 433
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
434 435
# cleanup source and package dir
clean() {
436
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
437
	rm -rf "$srcdir"
438
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
439 440 441 442 443 444
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
445 446 447 448
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
449 450 451
	done
}

452 453 454 455 456 457 458 459 460 461
listpkgnames() {
	local i
	for i in $pkgname $subpackages; do
		echo ${i%:*}
	done
	for i in $linguas; do
		echo $pkgname-lang-$i
	done
}

Natanael Copa's avatar
Natanael Copa committed
462 463
cleanpkg() {
	local i
464
	getpkgver || return 1
465
	msg "Cleaning built packages..."
466
	for i in $(listpkgnames); do
467 468
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
469
			"$abuildrepo"/$p.apk "$abuildrepo"/*/$p.apk
Natanael Copa's avatar
Natanael Copa committed
470
	done
471
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
472 473
}

474 475 476
# clean all packages except current
cleanoldpkg() {
	local i j
477
	getpkgver || return 1
478
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
479
	for i in $(listpkgnames); do
480 481 482 483 484
		local pn=${i%:*}
		for j in "$PKGDEST"/$pn-[0-9]*.apk ; do
			[ "$j" = "$PKGDEST/$pn-$pkgver-r$pkgrel.apk" ] \
				&& continue
			rm -f "$j" "$abuildrepo"/*/${j##*/}
485 486 487 488
		done
	done
	return 0
}
489 490 491

mkusers() {
	local i
492 493 494 495 496 497
	for i in $pkggroups; do
		if ! getent group $i >/dev/null; then
			msg "Creating group $i"
			$ADDGROUP -S $i || return 1
		fi
	done
498 499
	for i in $pkgusers; do
		if ! getent passwd $i >/dev/null; then
500
			local gopt=
501
			msg "Creating user $i"
502 503 504
			if getent group $i >/dev/null; then
				gopt="-G $i"
			fi
505
			$ADDUSER -S -D -H $gopt $i || return 1
506 507 508 509 510
		fi
	done
}


Natanael Copa's avatar
Natanael Copa committed
511 512
runpart() {
	local part=$1
513
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
514 515 516 517
	$part || die "$part failed"
}

# override those in your build script
518 519 520 521 522 523 524 525
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
526 527 528 529
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
530
build() {
531
	:
Natanael Copa's avatar
Natanael Copa committed
532 533
}

534 535 536
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
Natanael Copa's avatar
Natanael Copa committed
537
	mkdir -p "$PKGDEST"
538 539 540
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

541 542 543 544 545 546 547 548 549
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
}

550 551 552
postcheck() {
	local dir="$1" name="$2" i=
	msg "Running postcheck for $name"
553
	# checking for FHS compat
Natanael Copa's avatar
Natanael Copa committed
554 555 556 557 558 559 560 561
	if ! options_has "!fhs"; then
		for i in "$dir"/srv/* "$dir"/usr/local/* "$dir"/opt/*; do
			if [ -e "$i" ]; then
				error "Packages must not put anything under /srv, /usr/local or /opt"
				return 1
			fi
		done
	fi
562 563 564 565 566 567 568 569 570 571 572 573 574
	# look for *.la files
	i=$(find "$dir" -name '*.la' | sed "s|^$dir|\t|")
	if [ -n "$i" ] && ! options_has "libtool"; then
		error "Libtool archives (*.la) files found and \$options has no 'libtool' flag:"
		echo "$i"
		return 1
	fi
	# check directory permissions
	i=$(find "$dir" -type d -perm -777 | sed "s|^$dir|\t|")
	if [ -n "$i" ]; then
		warning "World writeable directories found:"
		echo "$i"
	fi
575 576 577 578 579 580 581 582 583
	# check so we dont have any suid root binaries that are not 
	i=$(find "$dir" -type f -perm +6000 \
		| xargs scanelf --nobanner --etype ET_EXEC \
		| sed "s|ET_EXEC $dir|\t|")
	if [ -n "$i" ]; then
		error "Found non-PIE files that has SUID:"
		echo "$i"
		return 1
	fi
584 585 586
	return 0
}

587
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
588 589
	local i
	cd "$startdir"
590
	for i in $subpackages; do
591 592
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
593
		msg "Running split function $func..."
594 595
		local dir="$pkgbasedir/${i%:*}" name="${i%:*}"
		subpkgdir="$dir" subpkgname="$name" \
596
			$0 $func prepare_package || return 1
597
		postcheck "$dir" "$name" || return 1
Natanael Copa's avatar
Natanael Copa committed
598
	done
599
	postcheck "$pkgdir" "$pkgname" || return 1
600 601
	# post check for /usr/share/locale
	if [ -d "$pkgdir"/usr/share/locale ]; then
602
		warning "Found /usr/share/locale"
603
		warning2 "Maybe add \$pkgname-lang to subpackages?"
604
	fi
Natanael Copa's avatar
Natanael Copa committed
605 606
}

607
default_lang() {
608
	pkgdesc="Languages for package $pkgname"
609 610 611 612 613 614 615 616 617 618 619 620 621
	install_if="$pkgname=$pkgver-r$pkgrel lang"
	arch="noarch"
	local dir
	for dir in ${langdir:-/usr/share/locale}; do
		mkdir -p "$subpkgdir"/${dir%/*}
		mv "$pkgdir"/"$dir" "$subpkgdir"/"$dir" || return 1
	done
}

lang() {
	default_lang
}

622
default_lang_subpkg() {
623 624 625 626
	if [ -z "$lang" ]; then
		error "lang is not set"
		return 1
	fi
627
	pkgdesc="$pkgname language pack for $lang"
628 629
	arch="noarch"
	install_if="$pkgname=$pkgver-r$pkgrel lang-$lang"
630 631 632 633 634
	local dir
	for dir in ${langdir:-/usr/share/locale}; do
		mkdir -p "$subpkgdir"/$dir
		mv "$pkgdir"/$dir/$lang* \
		"$subpkgdir"/$dir/ \
635
		|| return 1
636 637 638 639 640
	done
}

lang_subpkg() {
	default_lang_subpkg
641 642 643 644 645 646 647 648 649 650 651
}

prepare_language_packs() {
	for lang in $linguas; do
		lang="$lang" \
		subpkgname="$pkgname-lang-$lang" \
		subpkgdir="$pkgbasedir"/$subpkgname \
			$0 lang_subpkg prepare_package || return 1
	done
}

652 653 654 655 656 657 658 659 660 661 662 663
# echo '-dirty' if git is not clean
git_dirty() {
	if [ $(git status -s "$startdir" | wc -l) -ne 0 ]; then
		echo "-dirty"
	fi
}

# echo last commit hash id
git_last_commit() {
	git log --format=oneline -n 1 "$startdir" | awk '{print $1}'
}

664 665 666 667 668 669
get_maintainer() {
	if [ -z "$maintainer" ]; then
		maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
	fi
}

670
prepare_metafiles() {
671
	getpkgver || return 1
672 673
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
674
	local dir=${subpkgdir:-$pkgdir}
675
	local pkg="$name-$pkgver-r$pkgrel.apk"
676
	local pkginfo="$controldir"/.PKGINFO
677
	local sub
Natanael Copa's avatar
Natanael Copa committed
678 679 680
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
681
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
682 683
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
684
	local parch="$CARCH"
685 686 687 688 689

	# we need to wait with setting noarch til our build infra can handle it
#	if [ "$arch" = "noarch" ]; then
#		parch="noarch"
#	fi
Natanael Copa's avatar
Natanael Copa committed
690

691
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
692
	if [ -n "$FAKEROOTKEY" ]; then
693
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
694
	fi
695 696
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
697
pkgname = $name
698
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
699 700 701 702 703
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
Natanael Copa's avatar
Natanael Copa committed
704
arch = $parch
705
origin = $pkgname
Natanael Copa's avatar
Natanael Copa committed
706
EOF
707 708
	local i deps
	deps="$depends"
709
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
710 711
		for i in $install $triggers; do
			local s=${i%=*}
712
			[ "$name" != "${s%.*}" ] && continue
713
			if head -n 1 "$startdir/$s" | grep '^#!/bin/sh' >/dev/null ; then
714 715 716 717 718
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
719 720
	fi
	
721 722 723 724 725 726 727 728 729 730
	local last_commit="$(git_last_commit)$(git_dirty)"
	if [ -n "$last_commit" ]; then
		echo "commit = $last_commit" >> "$pkginfo"
	fi

	get_maintainer
	if [ -n "$maintainer" ]; then
		echo "maintainer = $maintainer" >> "$pkginfo"
	fi

731
	if [ -n "$replaces_priority" ]; then
732
		echo "replaces_priority = $replaces_priority" >> "$pkginfo"
733 734
	fi

Natanael Copa's avatar
Natanael Copa committed
735
	for i in $license; do
736
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
737 738
	done
	for i in $replaces; do
739
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
740
	done
741
	for i in $deps; do
742
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
743 744
	done
	for i in $conflicts; do
745
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
746 747
	done
	for i in $provides; do
748
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
749
	done
750 751 752 753 754 755
	for i in $triggers; do
		local f=${i%=*}
		local dirs=${i#*=}
		[ "${f%.trigger}" != "$name" ] && continue
		echo "triggers = ${dirs//:/ }" >> "$pkginfo"
	done
756 757 758
	if [ -n "$install_if" ]; then
		echo "install_if = $(echo $install_if)" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
759 760

	local metafiles=".PKGINFO"
761 762 763
	for i in $install $triggers; do
		local f=${i%=*}
		local n=${f%.*}
764
		if [ "$n" != "$name" ]; then
765 766
			continue
		fi
767
		script=${f#$name}
768
		msg "Adding $script"
769
		cp "$startdir/$f" "$controldir/$script" || return 1
770
		chmod +x "$controldir/$script"
771 772
		metafiles="$metafiles $script"
	done
773
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
774
}
775

776 777
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
778
	local etype= soname= file= sover=
779
	[ "$arch" = "noarch" ] && return 0
780
	options_has "!tracedeps" && return 0
781
	# lets tell all the places we should look for .so files - all rpaths
782 783
	scanelf --quiet --recursive --rpath "$dir" \
		| sed -e 's/[[:space:]].*//' -e 's/:/\n/' | sort -u \
784
		>"$controldir"/.rpaths
Natanael Copa's avatar
Natanael Copa committed
785 786
}

787 788 789 790 791 792 793 794 795 796 797 798
# check if dir has arch specific binaries
dir_has_arch_binaries() {
	local dir="$1"
	# if scanelf returns something, then we have binaries
	[ -n "$(scanelf -R "$dir" | head -n 1)" ] && return 0

	# look for static *.a
	[ -n "$(find "$dir" -type f -name '*.a' | head -n 1)" ] && return 0

	return 1
}

799 800 801 802 803
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

804 805 806 807 808 809 810
# check that noarch is set if needed
archcheck() {
	options_has "!archcheck" && return 0
	if dir_has_arch_binaries "${subpkgdir:-$pkgdir}"; then
		[ "$arch" != "noarch" ] && return 0
		error "Arch specific binaries found so arch must not be set to \"noarch\""
		return 1
811 812 813
	elif [ "$arch" != "noarch" ] && ! is_dev_pkg; then
		# we dont want -dev package go to noarch
		warning "No arch specific binaries found so arch should probably be set to \"noarch\""
814 815 816 817
	fi
	return 0
}

818
prepare_package() {
819
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
820
	stripbin
821 822
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
823 824 825 826 827 828 829 830
}

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

831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
# find real path to so files
real_so_path() {
	local so="$1"
	shift
	while [ $# -gt 0 ]; do
		[ -e "$1"/$so ] && realpath "$1/$so" && return 0
		shift
	done
	error "$so: path not found"
	return 1
}

# search rpaths and /usr/lib /lib for given so files
find_so_files() {
	local rpaths=$(cat "$1")
	shift
	while [ $# -gt 0 ]; do
		real_so_path "$1" /usr/lib /lib $rpaths || return 1
		shift
	done
	return 0
}

854 855 856
trace_apk_deps() {
	local name="$1"
	local dir="$2"
857
	local i= j= found= autodeps= deppkgs= missing= so_paths= self_provided=
858
	msg "Tracing dependencies..."
859 860 861
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
862
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
863 864 865 866
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
867
	if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
868 869
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
870
		msg "  added libgcc (due to libpthread)"
871
	fi
872
	[ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
873
		found=
874
		# first check if its provide by same apkbuild
875
		for j in "$dir"/../.control.*/.provides-so; do
876
			grep -q -w "^$i" "$j" || continue
877
			found=${j%/.provides-so}
878
			found=${found##*/.control.}
879
			break
880
		done
881 882 883 884 885 886
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
887
		fi
888 889 890
	done

	# find all packages that holds the so files
891 892 893 894 895
	if [ -f "$dir"/.rpaths ]; then
		so_files=$(find_so_files "$dir"/.rpaths $missing) || return 1
		deppkgs=$($APK info -q -W $so_files) || return 1
	fi

896
	for found in $self_provided $deppkgs; do
897
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
898 899 900
			warning "You can remove '$found' from depends"
			continue
		fi
901 902
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
903
			msg "  added $found"
904
		fi
905
	done
906

907
	echo "# automatically detected:" >> "$dir"/.PKGINFO
908
	if [ -f "$dir"/.provides-so ]; then
909
		sed 's/^\(.*\) \([0-9].*\)/provides = so:\1=\2/' "$dir"/.provides-so \
910
			>> "$dir"/.PKGINFO
911 912
	fi
	[ -z "$autodeps" ] && return 0
913 914 915 916 917
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
find_scanelf_paths() {
	local controldir="$1" datadir="$2"
	local paths="$datadir/lib:$datadir/usr/lib" i= rpath=
	if [ -n "$ldpath" ]; then
		paths="$paths:$ldpath"
	fi
	# search in all rpaths
	for rpath in "$pkgbasedir"/.control.*/.rpath; do
		[ -f "$rpath" ] || continue
		while read i; do
			if [ -d "$datadir/$i" ]; then
				paths="$paths:$datadir/$i"
			fi
		done < "$rpath"
	done
	echo "$paths"
}

scan_shared_objects() {
	local name="$1" controldir="$2" datadir="$3"

	# allow spaces in paths
	IFS=:
	set -- $(find_scanelf_paths "$controldir" "$datadir")
	unset IFS

	msg "Scanning shared objects"
	# lets tell all the .so files this package provides in .provides-so
	scanelf --nobanner --soname "$@" | while read etype soname file; do
		# if soname field is missing, soname will be the filepath
		# we only want shared libs
		sover=0
		case $soname in
		*.so|*.so.[0-9]*)
			soname=${soname##*/}
			case "$file" in
			*.so.[0-9]*) sover=${file##*.so.};;
			esac
			echo "$soname $sover"
			;;
		esac
	done > "$controldir"/.provides-so

	# now find the so dependencies
	scanelf --nobanner --recursive --needed "$datadir"  | tr ' ' ':' \
		| awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
		| sed 's:,:\n:g' | sort -u \
	| while read i; do
		# only add files that are not self provided
		grep -q -w "^$i" "$controldir"/.provides-so \
			|| echo $i
	done > "$controldir"/.needs-so
}

972
create_apks() {
973
	local file= dir= name= ver= apk= datadir=
974
	getpkgver || return 1
Natanael Copa's avatar
Natanael Copa committed
975
	mkdir -p "$PKGDEST"
976 977 978 979 980 981 982 983 984
	if [ "$arch" != "noarch" ] && ! options_has "!tracedeps"; then
		for file in "$pkgbasedir"/.control.*/.PKGINFO; do
			dir="${file%/.PKGINFO}"
			name="$(pkginfo_val pkgname $file)"
			datadir="$pkgbasedir"/$name
			subpkgname=$name
			scan_shared_objects "$name" "$dir" "$datadir"
		done
	fi
985
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
986 987 988 989 990 991
		dir="${file%/.PKGINFO}"
		name=$(pkginfo_val pkgname $file)
		ver=$(pkginfo_val pkgver $file)
		apk=$name-$ver.apk
		datadir="$pkgbasedir"/$name
		subpkgname=$name
992 993

		trace_apk_deps "$name" "$dir" || return 1
994
		msg "Compressing data..."
995
		(
996 997
		cd "$datadir"
		# data.tar.gz
998 999 1000 1001 1002 1003
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
1004

1005
		msg "Create checksum..."
1006 1007 1008 1009 1010
		# 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
1011
		cd "$dir"
1012 1013
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
1014
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
1015

1016
		msg "Create $apk"
1017 1018
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
1019
		)
1020
	done
1021
	subpkgname=
1022 1023
}

1024
clean_abuildrepo() {
1025
	local apk
1026 1027 1028
	cd "$abuildrepo" || return 1
	
	# remove compat symlink
1029
	for d in "$abuildrepo/$CARCH"; do
1030 1031
		[ -L "$d" ] && rm "$d"
	done
1032

1033 1034
	# remove broken links from abuildrepo
	for apk in *.apk */*.apk; do
1035 1036 1037 1038
		if [ -L "$apk" ] && [ ! -f "$apk" ]; then
			rm -f "$apk"
		fi
	done
1039
}
1040

1041
mklinks_abuildrepo() {
1042 1043
	local apk
	mkdir -p "$abuildrepo"/$CARCH
1044
	cd "$abuildrepo" || return 1
1045
	# create links for this package
1046
	for apk in $(listpkg); do
1047
		[ -f "$PKGDEST"/$apk ] || continue
1048
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk
1049
	done
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063
}

update_abuildrepo() {
	local d apk
	if ! apk_up2date || [ -n "$force" ]; then
		sanitycheck && builddeps && clean && fetch && unpack \
			&& prepare && mkusers && rootpkg || return 1
	fi

	clean_abuildrepo
	mklinks_abuildrepo

	cd "$abuildrepo"
	local index=$CARCH/APKINDEX.tar.gz
1064

1065
	msg "Updating the cached abuild repository index..."
1066 1067
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
1068 1069
	if [ -f "$index" ]; then
		oldindex="--index $index"
1070
	fi
1071
	$APK index --quiet $oldindex --output "$index".unsigned \
1072
		--description "$repo $(cd $startdir && git describe)" \
1073 1074
		--rewrite-arch $CARCH \
		$CARCH/*.apk || exit 1
1075
	msg "Signing the index..."
1076 1077 1078
	abuild-sign -q "$index".unsigned || exit 1
	mv "$index".unsigned "$index"
	chmod 644 "$index"
1079 1080
}

1081
# predefined splitfunc doc
1082
default_doc() {
1083
	depends="$depends_doc"
1084
	pkgdesc="$pkgdesc (documentation)"
1085
	arch=${arch_doc:-"noarch"}
1086

Natanael Copa's avatar
Natanael Copa committed
1087
	local i
1088
	for i in doc man info html sgml licenses gtk-doc; do
1089 1090 1091
		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
1092 1093
		fi
	done
1094 1095 1096
	
	rm -f "$subpkgdir/usr/share/info/dir"

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

Natanael Copa's avatar
Natanael Copa committed
1100 1101 1102
	return 0
}

1103 1104 1105 1106
doc() {
	default_doc
}

1107
# predefined splitfunc mod
1108
default_mod() {
1109
	depends="$kernel $depends_mod"
Natanael Copa's avatar
Natanael Copa committed
1110
	for i in firmware modules; do
1111 1112 1113
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
1114
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
1115 1116 1117 1118
		fi
	done
}

1119 1120 1121 1122
mod() {
	default_mod
}

1123
# predefined splitfunc dev
1124
default_dev() {
1125
	local i= j=
1126
	depends="$pkgname $depends_dev"
1127 1128
	pkgdesc="$pkgdesc (development files)"

1129 1130 1131 1132
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
1133
	cd "$pkgdir" || return 0
1134 1135
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
1136
			usr/share/vala/vapi usr/share/gir-[0-9]*\
1137
			$(find -name include -type d) 		\
1138
			$(find usr/ -name '*.[acho]' 2>/dev/null); do
1139 1140
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
1141
			mkdir -p "$d"
1142
			mv "$pkgdir/$i" "$d"
1143
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
1144 1145
		fi
	done
1146 1147 1148 1149 1150 1151 1152
	# move *.so links needed when linking the apps to -dev packages
	for i in lib/*.so usr/lib/*.so; do
		if [ -L "$i" ]; then
			mkdir -p "$subpkgdir"/"${i%/*}"
			mv "$i" "$subpkgdir/$i" || return 1
		fi
	done