abuild.in 35.5 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
SUDO=${SUDO:-"sudo"}
22
FAKEROOT=${FAKEROOT:-"fakeroot"}
23
APK=${APK:-apk}
24
apk_opt_wait="--wait 30"
25

Natanael Copa's avatar
Natanael Copa committed
26
# read config
Natanael Copa's avatar
Natanael Copa committed
27
ABUILD_CONF=${ABUILD_CONF:-"$sysconfdir/abuild.conf"}
Natanael Copa's avatar
Natanael Copa committed
28 29
[ -f "$ABUILD_CONF" ] && . "$ABUILD_CONF"

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

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

#colors
if [ -n "$USE_COLORS" ]; then
    default_colors
Natanael Copa's avatar
Natanael Copa committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
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
}
75 76

	
Linux User's avatar
Linux User committed
77
set_xterm_title() {
78
	if [ "$TERM" = xterm ] && [ -n "$USE_COLORS" ]; then
79
		 printf "\033]0;$1\007" >&2
Linux User's avatar
Linux User committed
80 81 82 83 84
	fi
} 

cleanup() {
	set_xterm_title ""
85
	if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
86 87
		msg "Uninstalling dependencies..."
		$SUDO $APK del --quiet $apk_opt_wait $uninstall_after
88
	fi
Linux User's avatar
Linux User committed
89 90
}

Natanael Copa's avatar
Natanael Copa committed
91
die() {
Linux User's avatar
Linux User committed
92
	error "$@"
Linux User's avatar
Linux User committed
93
	cleanup
Natanael Copa's avatar
Natanael Copa committed
94 95 96
	exit 1
}

97
# check if apkbuild is basicly sane
98
default_sanitycheck() {
99
	local i= j= suggestion=
100
	msg "Checking sanity of $APKBUILD..."
101 102 103
	[ -z "$pkgname" ] && die "Missing pkgname in APKBUILD"
	[ -z "${pkgname##* *}" ] && die "pkgname contains spaces"
	[ -z "$pkgver" ] && die "Missing pkgver in APKBUILD"
104
	if [ "$pkgver" != "volatile" ] && [ -z "$nodeps" ]; then
105
		$APK version --check -q "$pkgver" ||\
106 107
			die "$pkgver is not a valid version"
	fi
108
	[ -z "$pkgrel" ] && die "Missing pkgrel in APKBUILD"
109 110 111 112
	[ -z "$pkgdesc" ] && die "Missing pkgdesc in APKBUILD"
	[ -z "$url" ] && die "Missing url in APKBUILD"
	[ -z "$license" ] && die "Missing license in APKBULID"

113 114 115 116 117 118 119
	# 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

120 121 122 123 124 125 126 127 128 129
	# 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
130
	for i in $install; do
131
		local n=${i%.*}
132
		local suff=${i##*.}
133 134 135 136 137
		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
138
			die "$i: install script does not match pkgname or any subpackage"
139
		fi
140
		[ -e "$startdir/$i" ] || die "install script $i is missing"
Natanael Copa's avatar
Natanael Copa committed
141 142
	done
	
143 144 145 146 147 148 149 150
	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
151

152 153
		[ -e "$startdir"/$f ] || die "trigger script $f is missing"
	done
154 155
	if [ -n "$source" ]; then
		for i in $source; do
Natanael Copa's avatar
Natanael Copa committed
156 157 158 159
			if install_has "$i"; then
				warning "You should not have \$install in source"
				continue
			fi
160
			md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
161 162 163
			case "$i" in
				https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
			esac
164 165
		done
	fi
Natanael Copa's avatar
Natanael Copa committed
166

167 168
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
169
			source_has $i || die "$i exists in md5sums but is missing in source"
170 171
		done
	fi
172

173 174 175
	# 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
176 177

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
180 181 182
	return 0
}

183 184 185 186
sanitycheck() {
	default_sanitycheck
}

187
md5check() {
188
	local dummy f endreturnval originalparams origin file
189 190 191
	if [ -z "$source" ]; then
		return 0
	fi
192
	if [ -z "$md5sums" ]; then
193
		die "Use 'abuild checksum' to generate/update the checksum(s)"
194
	fi
195 196 197
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
198
	fetch || return 1
199
	msg "Checking md5sums..."
Natanael Copa's avatar
Natanael Copa committed
200
	cd "$srcdir" || return 1
201 202 203 204 205 206 207 208
	IFS=$'\n'
	endreturnval=0
	originalparams=$@
	set -- $source
	for src in $md5sums; do
		origin=$1; shift
		echo "$src" | md5sum -c
		if [ $? -ne 0 ]; then
209
			endreturnval=1
210 211 212 213 214 215 216 217 218 219 220
			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
221 222
}

223 224
# verify upstream sources
sourcecheck() {
225 226 227 228 229 230 231 232 233 234
	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
235
	done
236
	return 0
237
}
238

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
# convert curl options to wget options and call wget instead of curl
wget_fallback() {
	local wget_opts= outfile= opt=
	while getopts "C:ko:s" opt; do
		case $opt in
		'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
254 255
uri_fetch() {
	local uri="$1"
256
	local d="${uri##*/}"	# $(basename $uri)
257
	local opts
258
	[ -n "$quiet" ] && opts="-s"
259 260
	[ -f "$SRCDEST/$d" ] && return 0

261 262 263 264 265
	# 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)}";;
266
	esac
267 268

	case "$uri" in
269
		https://*) opts="-k";;
270
	esac
271

Natanael Copa's avatar
Natanael Copa committed
272
	mkdir -p "$SRCDEST"
273 274
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
275
		opts="$opts -C -"
Natanael Copa's avatar
Natanael Copa committed
276
	fi
Linux User's avatar
Linux User committed
277
	msg "Fetching $uri"
278 279 280 281 282 283 284 285

	# fallback to wget if curl is missing. useful for bootstrapping
	local fetcher=curl
	if ! [ -x "$(which curl)" ]; then
		fetcher=wget_fallback
	fi

	$fetcher $opts -o "$SRCDEST/$d.part" "$uri" \
286
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
287 288
}

289 290
is_remote() {
	case "$1" in
291
		http://*|ftp://*|https://*|saveas-*://*)
292 293 294 295 296
			return 0;;
	esac
	return 1
}

297 298 299 300 301
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
302 303 304 305 306
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
307 308 309 310
	fi
	uri_fetch "$uri"
}

311
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
312 313
	local s
	mkdir -p "$srcdir"
314 315
	for s in $source; do
		if is_remote "$s"; then
316
			uri_fetch_mirror "$s" || return 1
317 318 319 320
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
321 322 323
	done
}

324 325 326 327
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
# 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
343
# unpack the sources
344
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
345
	local u
346 347
	if [ -z "$force" ]; then
		md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
348
		initdcheck || return 1
349
	fi
Natanael Copa's avatar
Natanael Copa committed
350 351 352 353
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
354
			*.tar.gz|*.tgz)
355
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
356 357
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
358
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
359
				tar -C "$srcdir" -jxf "$s" || return 1;;
360 361 362 363
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
364 365 366
			*.tar.xz)
				msg "Unpacking $s..."
				unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
Natanael Copa's avatar
Natanael Copa committed
367 368 369
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
370 371 372 373
		esac
	done
}

374 375 376 377
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
378 379
# cleanup source and package dir
clean() {
380
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
381
	rm -rf "$srcdir"
382
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
383 384 385 386 387 388
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
389 390 391 392
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
393 394 395
	done
}

396 397 398 399 400 401 402 403 404 405
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
406 407
cleanpkg() {
	local i
408
	getpkgver || return 1
409
	msg "Cleaning built packages..."
410
	for i in $(listpkgnames); do
411 412
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
413
			"$abuildrepo"/$p.apk "$abuildrepo"/*/$p.apk
Natanael Copa's avatar
Natanael Copa committed
414
	done
415
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
416 417
}

418 419 420
# clean all packages except current
cleanoldpkg() {
	local i j
421
	getpkgver || return 1
422
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
423
	for i in $(listpkgnames); do
424 425 426 427 428
		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##*/}
429 430 431 432
		done
	done
	return 0
}
433 434 435 436 437

mkusers() {
	local i
	for i in $pkgusers; do
		if ! getent passwd $i >/dev/null; then
438
			local gopt=
439
			msg "Creating user $i"
440 441 442 443
			if getent group $i >/dev/null; then
				gopt="-G $i"
			fi
			$SUDO adduser -D -H $gopt $i || return 1
444 445 446 447 448 449 450 451 452 453 454
		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
455 456
runpart() {
	local part=$1
457
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
458 459 460 461
	$part || die "$part failed"
}

# override those in your build script
462 463 464 465 466 467 468 469
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
470 471 472 473
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
474
build() {
475
	:
Natanael Copa's avatar
Natanael Copa committed
476 477
}

478 479 480
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
Natanael Copa's avatar
Natanael Copa committed
481
	mkdir -p "$PKGDEST"
482 483 484
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

485 486 487 488 489 490 491 492 493
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
}

494
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
495 496
	local i
	cd "$startdir"
497
	for i in $subpackages; do
498 499
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
500
		msg "Running split function $func..."
501
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
502
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
503
	done
504 505 506 507 508 509 510
	# post check
	i=$(find "$pkgdir" -name '*.la')
	if [ -n "$i" ] && ! options_has "libtool"; then
		error "Libtool archives (*.la) files found and \$options has no 'libtool' flag:"
		echo "$i" | sed 's/^/  /'
		return 1
	fi
511 512
	# post check for /usr/share/locale
	if [ -d "$pkgdir"/usr/share/locale ]; then
513 514
		warning "Found /usr/share/locale"
		warning "Maybe add \$pkgname-lang to subpackages?"
515
	fi
Natanael Copa's avatar
Natanael Copa committed
516 517
}

518
default_lang() {
519
	pkgdesc="Languages for package $pkgname"
520 521 522 523 524 525 526 527 528 529 530 531 532
	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
}

533
default_lang_subpkg() {
534 535 536 537
	if [ -z "$lang" ]; then
		error "lang is not set"
		return 1
	fi
538
	pkgdesc="$pkgname language pack for $lang"
539 540
	arch="noarch"
	install_if="$pkgname=$pkgver-r$pkgrel lang-$lang"
541 542 543 544 545
	local dir
	for dir in ${langdir:-/usr/share/locale}; do
		mkdir -p "$subpkgdir"/$dir
		mv "$pkgdir"/$dir/$lang* \
		"$subpkgdir"/$dir/ \
546
		|| return 1
547 548 549 550 551
	done
}

lang_subpkg() {
	default_lang_subpkg
552 553 554 555 556 557 558 559 560 561 562
}

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
}

563 564 565 566 567 568 569 570 571 572 573 574
# 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}'
}

575 576 577 578 579 580
get_maintainer() {
	if [ -z "$maintainer" ]; then
		maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
	fi
}

581
prepare_metafiles() {
582
	getpkgver || return 1
583 584
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
585
	local dir=${subpkgdir:-$pkgdir}
586
	local pkg="$name-$pkgver-r$pkgrel.apk"
587
	local pkginfo="$controldir"/.PKGINFO
588
	local sub
Natanael Copa's avatar
Natanael Copa committed
589 590 591
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
592
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
593 594
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
595
	local parch="$CARCH"
596 597 598 599 600

	# 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
601

602
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
603
	if [ -n "$FAKEROOTKEY" ]; then
604
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
605
	fi
606 607
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
608
pkgname = $name
609
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
610 611 612 613 614
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
Natanael Copa's avatar
Natanael Copa committed
615
arch = $parch
616
origin = $pkgname
Natanael Copa's avatar
Natanael Copa committed
617
EOF
618 619
	local i deps
	deps="$depends"
620
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
621 622
		for i in $install $triggers; do
			local s=${i%=*}
623
			[ "$name" != "${s%.*}" ] && continue
624
			if head -n 1 "$startdir/$s" | grep '^#!/bin/sh' >/dev/null ; then
625 626 627 628 629
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
630 631
	fi
	
632 633 634 635 636 637 638 639 640 641
	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

Natanael Copa's avatar
Natanael Copa committed
642
	for i in $license; do
643
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
644 645
	done
	for i in $replaces; do
646
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
647
	done
648
	for i in $deps; do
649
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
650 651
	done
	for i in $conflicts; do
652
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
653 654
	done
	for i in $provides; do
655
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
656
	done
657 658 659 660 661 662
	for i in $triggers; do
		local f=${i%=*}
		local dirs=${i#*=}
		[ "${f%.trigger}" != "$name" ] && continue
		echo "triggers = ${dirs//:/ }" >> "$pkginfo"
	done
663 664 665
	if [ -n "$install_if" ]; then
		echo "install_if = $(echo $install_if)" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
666 667

	local metafiles=".PKGINFO"
668 669 670
	for i in $install $triggers; do
		local f=${i%=*}
		local n=${f%.*}
671
		if [ "$n" != "$name" ]; then
672 673
			continue
		fi
674
		script=${f#$name}
675
		msg "Adding $script"
676
		cp "$startdir/$f" "$controldir/$script" || return 1
677
		chmod +x "$controldir/$script"
678 679
		metafiles="$metafiles $script"
	done
680
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
681
}
682

683 684
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
685
	[ "$arch" = "noarch" ] && return 0
686
	options_has "!tracedeps" && return 0
687
	# lets tell all the .so files this package provides in .provides-so
688 689
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
690 691 692 693 694
	# lets tell all the places we should look for .so files - all rpaths
	scanelf -q -Rr "$dir" | sed -e 's/[[:space:]].*//' -e 's/:/\n/' \
		| sort | uniq \
		>"$controldir"/.rpaths
	# now find the so dependencies
695
	scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
696 697 698
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
699 700
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
701
	done
Natanael Copa's avatar
Natanael Copa committed
702 703
}

704 705 706 707 708 709 710 711 712 713 714 715
# 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
}

716 717 718 719 720
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

721 722 723 724 725 726 727
# 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
728 729 730
	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\""
731 732 733 734
	fi
	return 0
}

735
prepare_package() {
736
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
737
	stripbin
738 739
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
740 741 742 743 744 745 746 747
}

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

748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
# 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
}

771 772 773
trace_apk_deps() {
	local name="$1"
	local dir="$2"
774
	local i= j= found= autodeps= deppkgs= missing= so_paths= self_provided=
775
	msg "Tracing dependencies for $name..."
776 777 778
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
779
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
780 781 782 783
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
784
	if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
785 786
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
787
		msg "  added libgcc (due to libpthread)"
788
	fi
789
	[ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
790
		found=
791
		# first check if its provide by same apkbuild
792
		for j in "$dir"/../.control.*/.provides-so; do
793
			grep -w "$i" "$j" >/dev/null || continue
794
			found=${j%/.provides-so}
795
			found=${found##*/.control.}
796
			break
797
		done
798 799 800 801 802 803
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
804
		fi
805 806 807
	done

	# find all packages that holds the so files
808 809 810 811 812
	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

813
	for found in $self_provided $deppkgs; do
814
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
815 816 817
			warning "You can remove '$found' from depends"
			continue
		fi
818 819
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
820
			msg "  added $found"
821
		fi
822
	done
823

824 825 826 827 828 829 830
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

831 832
create_apks() {
	local file
833
	getpkgver || return 1
Natanael Copa's avatar
Natanael Copa committed
834
	mkdir -p "$PKGDEST"
835
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
836 837 838
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
839
		local apk=$name-$ver.apk
840
		local datadir="$pkgbasedir"/$name
841 842

		trace_apk_deps "$name" "$dir" || return 1
843
		msg "Creating $apk..."
844
		(
845 846
		cd "$datadir"
		# data.tar.gz
847 848 849 850 851 852
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
853 854 855 856 857 858

		# 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
859
		cd "$dir"
860 861
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
862
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
863

864 865 866
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
867 868 869
	done
}

870
clean_abuildrepo() {
871
	local apk
872 873 874
	cd "$abuildrepo" || return 1
	
	# remove compat symlink
875
	for d in "$abuildrepo/$CARCH"; do
876 877
		[ -L "$d" ] && rm "$d"
	done
878

879 880
	# remove broken links from abuildrepo
	for apk in *.apk */*.apk; do
881 882 883 884
		if [ -L "$apk" ] && [ ! -f "$apk" ]; then
			rm -f "$apk"
		fi
	done
885
}
886

887
mklinks_abuildrepo() {
888 889
	local apk
	mkdir -p "$abuildrepo"/$CARCH
890
	cd "$abuildrepo" || return 1
891
	# create links for this package
892
	for apk in $(listpkg); do
893
		[ -f "$PKGDEST"/$apk ] || continue
894
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk
895
	done
896 897 898 899 900 901 902 903 904 905 906 907 908 909
}

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
910

911
	msg "Updating the cached abuild repository index..."
912 913
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
914 915
	if [ -f "$index" ]; then
		oldindex="--index $index"
916
	fi
917
	$APK index --quiet $oldindex --output "$index".unsigned \
918
		--description "$repo $(cd $startdir && git describe)" \
919 920
		--rewrite-arch $CARCH \
		$CARCH/*.apk || exit 1
921
	msg "Signing the index..."
922 923 924
	abuild-sign -q "$index".unsigned || exit 1
	mv "$index".unsigned "$index"
	chmod 644 "$index"
925 926
}

927
# predefined splitfunc doc
928
default_doc() {
929
	depends="$depends_doc"
930
	pkgdesc="$pkgdesc (documentation)"
931
	arch=${arch_doc:-"noarch"}
932

Natanael Copa's avatar
Natanael Copa committed
933
	local i
934
	for i in doc man info html sgml licenses gtk-doc; do
935 936 937
		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
938 939
		fi
	done
940 941 942
	
	rm -f "$subpkgdir/usr/share/info/dir"

943 944 945
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
946 947 948 949

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

Natanael Copa's avatar
Natanael Copa committed
950
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
951 952 953
	return 0
}

954 955 956 957
doc() {
	default_doc
}

958
# predefined splitfunc mod
959
default_mod() {
960
	depends="$kernel $depends_mod"
Natanael Copa's avatar
Natanael Copa committed
961
	for i in firmware modules; do
962 963 964
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
965
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
966 967 968 969
		fi
	done
}

970 971 972 973
mod() {
	default_mod
}

974
# predefined splitfunc dev
975
default_dev() {
976
	local i= j=
977
	depends="$pkgname $depends_dev"
978 979
	pkgdesc="$pkgdesc (development files)"

980 981 982 983
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
984
	cd "$pkgdir" || return 0
985 986
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
987
			usr/share/vala/vapi usr/share/gir-[0-9]*\
988
			$(find -name include -type d) 		\
989
			$(find usr/ -name '*.[acho]' 2>/dev/null); do
990 991
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
992
			mkdir -p "$d"
993
			mv "$pkgdir/$i" "$d"
994
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
995 996
		fi
	done
997 998 999 1000 1001 1002 1003
	# 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
Natanael Copa's avatar
Natanael Copa committed
1004
	return 0
Natanael Copa's avatar
Natanael Copa committed
1005 1006
}

1007 1008 1009 1010
dev() {
	default_dev
}

1011 1012 1013 1014
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

1015 1016 1017 1018 1019 1020 1021 1022
do_fakeroot() {
	if [ -n "$FAKEROOT" ]; then
		$FAKEROOT -- "$@"
	else
		"$@"
	fi
}

Natanael Copa's avatar
Natanael Copa committed
1023 1024
# build and package in fakeroot
rootpkg() {
1025 1026 1027 1028
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
1029
		do_build=package
1030
	fi
Natanael Copa's avatar
Natanael Copa committed
1031
	cd "$startdir"
1032
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
1033
	do_fakeroot "$abuild_path" $color_opt $do_build \
1034
		prepare_subpackages \
1035
		prepare_language_packs \
1036
		prepare_package \
1037
		create_apks
Natanael Copa's avatar
Natanael Copa committed
1038 1039 1040
}

srcpkg() {
1041
	getpkgver || return 1
1042
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
1043
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
1044 1045 1046 1047 1048
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
1049
	msg "Creating source package $p.src.tar.gz..."
1050
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
1051 1052
}

Natanael Copa's avatar
Natanael Copa committed
1053 1054
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
1055
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
1056 1057
}

1058
# check if package is up to date
1059
apk_up2date() {
1060
	getpkgver || return 1
1061
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
1062
	local i s
Natanael Copa's avatar
Natanael Copa committed
1063
	cd "$startdir"
1064 1065 1066 1067 1068
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

1069
	for i in $source APKBUILD; do
1070 1071 1072 1073 1074 1075
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
1076 1077 1078 1079 1080 1081 1082
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

1083
abuildindex_up2date() {
1084
	local i
1085
	getpkgver || return 1
1086 1087 1088 1089 1090 1091 1092
	local dir="$abuildrepo"/$CARCH
	local apk="${pkgname%:*}-$pkgver-r$pkgrel.apk"
	local idx="$dir"/APKINDEX.tar.gz
	local file="$dir"/$apk

	# check if index is missing
	[ -f "$idx" ] || return 1
1093
			
1094 1095 1096 1097 1098
	# if link or file is missing, then we need update abuildrepo index
	[ -f "$file" ] || return 1

	# if file exists and is newer than index, then we need update index
	[ "$file" -nt "$idx" ] && return 1
1099
		
1100 1101 1102 1103
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
1104
	check_arch || return 0
1105 1106 1107 1108 1109 1110 1111
	apk_up2date && abuildindex_up2date
}

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

1114 1115 1116 1117 1118 1119 1120
# 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
	(
1121 1122
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
1123 1124 1125 1126
		pkgname=
		subpackages=
		depends=
		makedepends=
1127
		. $i
1128
		dir=${i%/APKBUILD}
1129
		deps=
1130
		# filter out conflicts from deps and version info
1131 1132 1133 1134
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
1135
			deps="$deps ${j%%[<>=]*}"
1136
		done
1137
		for j in $pkgname $subpackages; do
1138
			echo "o ${j%%:*} $dir"
1139
			set -- $deps
1140