abuild.in 39.1 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
785 786 787 788 789 790 791
	if grep -q -x '/usr/lib' "$controldir"/.rpaths; then
		warning "Redundat /usr/lib in rpath found"
	fi
	if grep '^/home/' "$controldir"/.rpaths; then
		error "Has /home/... in rpath"
		return 1
	fi
Natanael Copa's avatar
Natanael Copa committed
792 793
}

794 795 796 797 798 799 800 801 802 803 804 805
# 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
}

806 807 808 809 810
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

811 812 813 814 815 816 817
# 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
818 819 820
	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\""
821 822 823 824
	fi
	return 0
}

825
prepare_package() {
826
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
827
	stripbin
828 829
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
830 831 832 833 834 835 836 837
}

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

838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
# 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
}

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

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

	# find all packages that holds the so files
898 899 900 901 902
	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

903
	for found in $self_provided $deppkgs; do
904
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
905 906 907
			warning "You can remove '$found' from depends"
			continue
		fi
908 909
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
910
			msg "  added $found"
911
		fi
912
	done
913

914
	echo "# automatically detected:" >> "$dir"/.PKGINFO
915
	if [ -f "$dir"/.provides-so ]; then
916
		sed 's/^\(.*\) \([0-9].*\)/provides = so:\1=\2/' "$dir"/.provides-so \
917
			>> "$dir"/.PKGINFO
918 919
	fi
	[ -z "$autodeps" ] && return 0
920 921 922 923 924
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

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 972 973 974 975 976 977 978
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
}

979
create_apks() {
980
	local file= dir= name= ver= apk= datadir=
981
	getpkgver || return 1
Natanael Copa's avatar
Natanael Copa committed
982
	mkdir -p "$PKGDEST"
983 984 985 986 987 988 989 990 991
	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
992
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
993 994 995 996 997 998
		dir="${file%/.PKGINFO}"
		name=$(pkginfo_val pkgname $file)
		ver=$(pkginfo_val pkgver $file)
		apk=$name-$ver.apk
		datadir="$pkgbasedir"/$name
		subpkgname=$name
999 1000

		trace_apk_deps "$name" "$dir" || return 1
1001
		msg "Compressing data..."
1002
		(
1003 1004
		cd "$datadir"
		# data.tar.gz
1005 1006 1007 1008 1009 1010
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
1011

1012
		msg "Create checksum..."
1013 1014 1015 1016 1017
		# 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
1018
		cd "$dir"
1019 1020
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
1021
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
1022

1023
		msg "Create $apk"
1024 1025
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
1026
		)
1027
	done
1028
	subpkgname=
1029 1030
}

1031
clean_abuildrepo() {
1032
	local apk
1033 1034 1035
	cd "$abuildrepo" || return 1
	
	# remove compat symlink
1036
	for d in "$abuildrepo/$CARCH"; do
1037 1038
		[ -L "$d" ] && rm "$d"
	done
1039

1040 1041
	# remove broken links from abuildrepo
	for apk in *.apk */*.apk; do
1042 1043 1044 1045
		if [ -L "$apk" ] && [ ! -f "$apk" ]; then
			rm -f "$apk"
		fi
	done
1046
}
1047

1048
mklinks_abuildrepo() {
1049 1050
	local apk
	mkdir -p "$abuildrepo"/$CARCH
1051
	cd "$abuildrepo" || return 1
1052
	# create links for this package
1053
	for apk in $(listpkg); do
1054
		[ -f "$PKGDEST"/$apk ] || continue
1055
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk
1056
	done
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070
}

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
1071

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

1088
# predefined splitfunc doc
1089
default_doc() {
1090
	depends="$depends_doc"
1091
	pkgdesc="$pkgdesc (documentation)"
1092
	arch=${arch_doc:-"noarch"}
1093

Natanael Copa's avatar
Natanael Copa committed
1094
	local i
1095
	for i in doc man info html sgml licenses gtk-doc; do
1096 1097 1098
		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
1099 1100
		fi
	done
1101 1102 1103
	
	rm -f "$subpkgdir/usr/share/info/dir"

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

Natanael Copa's avatar
Natanael Copa committed
1107 1108 1109
	return 0
}

1110 1111 1112 1113
doc() {
	default_doc
}

1114
# predefined splitfunc mod
1115
default_mod() {
1116
	depends="$kernel $depends_mod"
Natanael Copa's avatar
Natanael Copa committed
1117
	for i in firmware modules; do
1118 1119 1120
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
1121
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
1122 1123 1124 1125
		fi
	done
}

1126 1127 1128 1129
mod() {
	default_mod
}

1130
# predefined splitfunc dev
1131
default_dev() {
1132
	local i= j=
1133
	depends="$pkgname $depends_dev"
1134 1135
	pkgdesc="$pkgdesc (development files)"

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