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

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

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

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

	# fallback to wget if curl is missing. useful for bootstrapping
281
	local fetcher=
282 283
	if ! [ -x "$(which curl)" ]; then
		fetcher=wget_fallback
284 285 286
	else
		fetcher=curl
		opts="$opts -L"
287 288 289
	fi

	$fetcher $opts -o "$SRCDEST/$d.part" "$uri" \
290
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
291 292
}

293 294
is_remote() {
	case "$1" in
295
		http://*|ftp://*|https://*|saveas-*://*)
296 297 298 299 300
			return 0;;
	esac
	return 1
}

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

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

328 329 330 331
fetch() {
	default_fetch
}

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

378 379 380 381
unpack() {
	default_unpack
}

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

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

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

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

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

# override those in your build script
466 467 468 469 470 471 472 473
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
474 475 476 477
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
478
build() {
479
	:
Natanael Copa's avatar
Natanael Copa committed
480 481
}

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

489 490 491 492 493 494 495 496 497
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
}

498
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
499 500
	local i
	cd "$startdir"
501
	for i in $subpackages; do
502 503
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
504
		msg "Running split function $func..."
505
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
506
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
507
	done
508 509 510 511 512 513 514
	# 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
515 516
	# post check for /usr/share/locale
	if [ -d "$pkgdir"/usr/share/locale ]; then
517 518
		warning "Found /usr/share/locale"
		warning "Maybe add \$pkgname-lang to subpackages?"
519
	fi
Natanael Copa's avatar
Natanael Copa committed
520 521
}

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

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

lang_subpkg() {
	default_lang_subpkg
556 557 558 559 560 561 562 563 564 565 566
}

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
}

567 568 569 570 571 572 573 574 575 576 577 578
# 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}'
}

579 580 581 582 583 584
get_maintainer() {
	if [ -z "$maintainer" ]; then
		maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
	fi
}

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

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

606
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
607
	if [ -n "$FAKEROOTKEY" ]; then
608
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
609
	fi
610 611
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
612
pkgname = $name
613
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
614 615 616 617 618
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
Natanael Copa's avatar
Natanael Copa committed
619
arch = $parch
620
origin = $pkgname
Natanael Copa's avatar
Natanael Copa committed
621
EOF
622 623
	local i deps
	deps="$depends"
624
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
625 626
		for i in $install $triggers; do
			local s=${i%=*}
627
			[ "$name" != "${s%.*}" ] && continue
628
			if head -n 1 "$startdir/$s" | grep '^#!/bin/sh' >/dev/null ; then
629 630 631 632 633
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
634 635
	fi
	
636 637 638 639 640 641 642 643 644 645
	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
646
	for i in $license; do
647
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
648 649
	done
	for i in $replaces; do
650
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
651
	done
652
	for i in $deps; do
653
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
654 655
	done
	for i in $conflicts; do
656
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
657 658
	done
	for i in $provides; do
659
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
660
	done
661 662 663 664 665 666
	for i in $triggers; do
		local f=${i%=*}
		local dirs=${i#*=}
		[ "${f%.trigger}" != "$name" ] && continue
		echo "triggers = ${dirs//:/ }" >> "$pkginfo"
	done
667 668 669
	if [ -n "$install_if" ]; then
		echo "install_if = $(echo $install_if)" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
670 671

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

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

708 709 710 711 712 713 714 715 716 717 718 719
# 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
}

720 721 722 723 724
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

725 726 727 728 729 730 731
# 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
732 733 734
	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\""
735 736 737 738
	fi
	return 0
}

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

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

752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
# 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
}

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

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

	# find all packages that holds the so files
812 813 814 815 816
	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

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

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

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

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

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

868 869 870
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
871 872 873
	done
}

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

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

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

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
914

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

931
# predefined splitfunc doc
932
default_doc() {
933
	depends="$depends_doc"
934
	pkgdesc="$pkgdesc (documentation)"
935
	arch=${arch_doc:-"noarch"}
936

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

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 951 952
	return 0
}

953 954 955 956
doc() {
	default_doc
}

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

969 970 971 972
mod() {
	default_mod
}

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

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

1006 1007 1008 1009
dev() {
	default_dev
}

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

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

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

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

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

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

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

1082
abuildindex_up2date() {
1083
	local i
1084
	getpkgver || return 1
1085 1086 1087 1088 1089 1090 1091
	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
1092
			
1093 1094 1095 1096 1097
	# 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
1098
		
1099 1100 1101 1102
	return 0
}

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

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

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