abuild.in 35.8 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
89 90 91
	if [ -n "$CLEANUP_FILES" ]; then
		rm -f $CLEANUP_FILES
	fi
Linux User's avatar
Linux User committed
92 93
}

Natanael Copa's avatar
Natanael Copa committed
94
die() {
Linux User's avatar
Linux User committed
95
	error "$@"
Linux User's avatar
Linux User committed
96
	cleanup
Natanael Copa's avatar
Natanael Copa committed
97 98 99
	exit 1
}

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

116 117 118 119 120 121 122
	# 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

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

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

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

176 177 178
	# 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
179 180

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
183 184 185
	return 0
}

186 187 188 189
sanitycheck() {
	default_sanitycheck
}

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

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

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

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

267 268 269 270 271
	# 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)}";;
272
	esac
273 274

	case "$uri" in
275
		https://*) opts="-k";;
276
	esac
277

Natanael Copa's avatar
Natanael Copa committed
278
	mkdir -p "$SRCDEST"
279 280 281 282

	CLEANUP_FILES="$CLEANUP_FILES $lockfile"
	(
	flock -n -x 200 || msg "Waiting for ${lockfile##*/}..."
283
	flock -x 200
284 285 286

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

287 288
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
289
		opts="$opts -C -"
Natanael Copa's avatar
Natanael Copa committed
290
	fi
Linux User's avatar
Linux User committed
291
	msg "Fetching $uri"
292 293

	# fallback to wget if curl is missing. useful for bootstrapping
294
	local fetcher=
295 296
	if ! [ -x "$(which curl)" ]; then
		fetcher=wget_fallback
297 298
	else
		fetcher=curl
299
		opts="$opts -L -f"
300 301 302
	fi

	$fetcher $opts -o "$SRCDEST/$d.part" "$uri" \
303
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
304 305 306 307 308 309

	) 200>$lockfile

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

312 313
is_remote() {
	case "$1" in
314
		http://*|ftp://*|https://*|saveas-*://*)
315 316 317 318 319
			return 0;;
	esac
	return 1
}

320 321 322 323 324
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
325 326 327 328 329
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
330 331 332 333
	fi
	uri_fetch "$uri"
}

334
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
335 336
	local s
	mkdir -p "$srcdir"
337 338
	for s in $source; do
		if is_remote "$s"; then
339
			uri_fetch_mirror "$s" || return 1
340 341 342 343
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
344 345 346
	done
}

347 348 349 350
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
# 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
366
# unpack the sources
367
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
368
	local u
369 370
	if [ -z "$force" ]; then
		md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
371
		initdcheck || return 1
372
	fi
Natanael Copa's avatar
Natanael Copa committed
373 374 375 376
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
377
			*.tar.gz|*.tgz)
378
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
379 380
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
381
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
382
				tar -C "$srcdir" -jxf "$s" || return 1;;
383 384 385 386
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
387 388 389
			*.tar.xz)
				msg "Unpacking $s..."
				unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
Natanael Copa's avatar
Natanael Copa committed
390 391 392
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
393 394 395 396
		esac
	done
}

397 398 399 400
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
401 402
# cleanup source and package dir
clean() {
403
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
404
	rm -rf "$srcdir"
405
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
406 407 408 409 410 411
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
412 413 414 415
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
416 417 418
	done
}

419 420 421 422 423 424 425 426 427 428
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
429 430
cleanpkg() {
	local i
431
	getpkgver || return 1
432
	msg "Cleaning built packages..."
433
	for i in $(listpkgnames); do
434 435
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
436
			"$abuildrepo"/$p.apk "$abuildrepo"/*/$p.apk
Natanael Copa's avatar
Natanael Copa committed
437
	done
438
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
439 440
}

441 442 443
# clean all packages except current
cleanoldpkg() {
	local i j
444
	getpkgver || return 1
445
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
446
	for i in $(listpkgnames); do
447 448 449 450 451
		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##*/}
452 453 454 455
		done
	done
	return 0
}
456 457 458 459 460

mkusers() {
	local i
	for i in $pkgusers; do
		if ! getent passwd $i >/dev/null; then
461
			local gopt=
462
			msg "Creating user $i"
463 464 465 466
			if getent group $i >/dev/null; then
				gopt="-G $i"
			fi
			$SUDO adduser -D -H $gopt $i || return 1
467 468 469 470 471 472 473 474 475 476 477
		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
478 479
runpart() {
	local part=$1
480
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
481 482 483 484
	$part || die "$part failed"
}

# override those in your build script
485 486 487 488 489 490 491 492
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
493 494 495 496
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
497
build() {
498
	:
Natanael Copa's avatar
Natanael Copa committed
499 500
}

501 502 503
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
Natanael Copa's avatar
Natanael Copa committed
504
	mkdir -p "$PKGDEST"
505 506 507
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

508 509 510 511 512 513 514 515 516
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
}

517
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
518 519
	local i
	cd "$startdir"
520
	for i in $subpackages; do
521 522
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
523
		msg "Running split function $func..."
524
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
525
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
526
	done
527 528 529 530 531 532 533
	# 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
534 535
	# post check for /usr/share/locale
	if [ -d "$pkgdir"/usr/share/locale ]; then
536 537
		warning "Found /usr/share/locale"
		warning "Maybe add \$pkgname-lang to subpackages?"
538
	fi
Natanael Copa's avatar
Natanael Copa committed
539 540
}

541
default_lang() {
542
	pkgdesc="Languages for package $pkgname"
543 544 545 546 547 548 549 550 551 552 553 554 555
	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
}

556
default_lang_subpkg() {
557 558 559 560
	if [ -z "$lang" ]; then
		error "lang is not set"
		return 1
	fi
561
	pkgdesc="$pkgname language pack for $lang"
562 563
	arch="noarch"
	install_if="$pkgname=$pkgver-r$pkgrel lang-$lang"
564 565 566 567 568
	local dir
	for dir in ${langdir:-/usr/share/locale}; do
		mkdir -p "$subpkgdir"/$dir
		mv "$pkgdir"/$dir/$lang* \
		"$subpkgdir"/$dir/ \
569
		|| return 1
570 571 572 573 574
	done
}

lang_subpkg() {
	default_lang_subpkg
575 576 577 578 579 580 581 582 583 584 585
}

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
}

586 587 588 589 590 591 592 593 594 595 596 597
# 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}'
}

598 599 600 601 602 603
get_maintainer() {
	if [ -z "$maintainer" ]; then
		maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
	fi
}

604
prepare_metafiles() {
605
	getpkgver || return 1
606 607
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
608
	local dir=${subpkgdir:-$pkgdir}
609
	local pkg="$name-$pkgver-r$pkgrel.apk"
610
	local pkginfo="$controldir"/.PKGINFO
611
	local sub
Natanael Copa's avatar
Natanael Copa committed
612 613 614
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
615
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
616 617
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
618
	local parch="$CARCH"
619 620 621 622 623

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

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

	local metafiles=".PKGINFO"
691 692 693
	for i in $install $triggers; do
		local f=${i%=*}
		local n=${f%.*}
694
		if [ "$n" != "$name" ]; then
695 696
			continue
		fi
697
		script=${f#$name}
698
		msg "Adding $script"
699
		cp "$startdir/$f" "$controldir/$script" || return 1
700
		chmod +x "$controldir/$script"
701 702
		metafiles="$metafiles $script"
	done
703
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
704
}
705

706 707
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
708
	[ "$arch" = "noarch" ] && return 0
709
	options_has "!tracedeps" && return 0
710
	# lets tell all the .so files this package provides in .provides-so
711 712
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
713 714 715 716 717
	# 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
718
	scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
719 720 721
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
722 723
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
724
	done
Natanael Copa's avatar
Natanael Copa committed
725 726
}

727 728 729 730 731 732 733 734 735 736 737 738
# 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
}

739 740 741 742 743
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

744 745 746 747 748 749 750
# 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
751 752 753
	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\""
754 755 756 757
	fi
	return 0
}

758
prepare_package() {
759
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
760
	stripbin
761 762
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
763 764 765 766 767 768 769 770
}

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

771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
# 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
}

794 795 796
trace_apk_deps() {
	local name="$1"
	local dir="$2"
797
	local i= j= found= autodeps= deppkgs= missing= so_paths= self_provided=
798
	msg "Tracing dependencies for $name..."
799 800 801
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
802
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
803 804 805 806
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
807
	if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
808 809
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
810
		msg "  added libgcc (due to libpthread)"
811
	fi
812
	[ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
813
		found=
814
		# first check if its provide by same apkbuild
815
		for j in "$dir"/../.control.*/.provides-so; do
816
			grep -w "$i" "$j" >/dev/null || continue
817
			found=${j%/.provides-so}
818
			found=${found##*/.control.}
819
			break
820
		done
821 822 823 824 825 826
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
827
		fi
828 829 830
	done

	# find all packages that holds the so files
831 832 833 834 835
	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

836
	for found in $self_provided $deppkgs; do
837
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
838 839 840
			warning "You can remove '$found' from depends"
			continue
		fi
841 842
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
843
			msg "  added $found"
844
		fi
845
	done
846

847 848 849 850 851 852 853
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

854 855
create_apks() {
	local file
856
	getpkgver || return 1
Natanael Copa's avatar
Natanael Copa committed
857
	mkdir -p "$PKGDEST"
858
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
859 860 861
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
862
		local apk=$name-$ver.apk
863
		local datadir="$pkgbasedir"/$name
864 865

		trace_apk_deps "$name" "$dir" || return 1
866
		msg "Creating $apk..."
867
		(
868 869
		cd "$datadir"
		# data.tar.gz
870 871 872 873 874 875
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
876 877 878 879 880 881

		# 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
882
		cd "$dir"
883 884
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
885
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
886

887 888 889
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
890 891 892
	done
}

893
clean_abuildrepo() {
894
	local apk
895 896 897
	cd "$abuildrepo" || return 1
	
	# remove compat symlink
898
	for d in "$abuildrepo/$CARCH"; do
899 900
		[ -L "$d" ] && rm "$d"
	done
901

902 903
	# remove broken links from abuildrepo
	for apk in *.apk */*.apk; do
904 905 906 907
		if [ -L "$apk" ] && [ ! -f "$apk" ]; then
			rm -f "$apk"
		fi
	done
908
}
909

910
mklinks_abuildrepo() {
911 912
	local apk
	mkdir -p "$abuildrepo"/$CARCH
913
	cd "$abuildrepo" || return 1
914
	# create links for this package
915
	for apk in $(listpkg); do
916
		[ -f "$PKGDEST"/$apk ] || continue
917
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk
918
	done
919 920 921 922 923 924 925 926 927 928 929 930 931 932
}

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
933

934
	msg "Updating the cached abuild repository index..."
935 936
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
937 938
	if [ -f "$index" ]; then
		oldindex="--index $index"
939
	fi
940
	$APK index --quiet $oldindex --output "$index".unsigned \
941
		--description "$repo $(cd $startdir && git describe)" \
942 943
		--rewrite-arch $CARCH \
		$CARCH/*.apk || exit 1
944
	msg "Signing the index..."
945 946 947
	abuild-sign -q "$index".unsigned || exit 1
	mv "$index".unsigned "$index"
	chmod 644 "$index"
948 949
}

950
# predefined splitfunc doc
951
default_doc() {
952
	depends="$depends_doc"
953
	pkgdesc="$pkgdesc (documentation)"
954
	arch=${arch_doc:-"noarch"}
955

Natanael Copa's avatar
Natanael Copa committed
956
	local i
957
	for i in doc man info html sgml licenses gtk-doc; do
958 959 960
		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
961 962
		fi
	done
963 964 965
	
	rm -f "$subpkgdir/usr/share/info/dir"

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

Natanael Copa's avatar
Natanael Copa committed
969 970 971
	return 0
}

972 973 974 975
doc() {
	default_doc
}

976
# predefined splitfunc mod
977
default_mod() {
978
	depends="$kernel $depends_mod"
Natanael Copa's avatar
Natanael Copa committed
979
	for i in firmware modules; do
980 981 982
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
983
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
984 985 986 987
		fi
	done
}

988 989 990 991
mod() {
	default_mod
}

992
# predefined splitfunc dev
993
default_dev() {
994
	local i= j=
995
	depends="$pkgname $depends_dev"
996 997
	pkgdesc="$pkgdesc (development files)"

998 999 1000 1001
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
1002
	cd "$pkgdir" || return 0
1003 1004
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
1005
			usr/share/vala/vapi usr/share/gir-[0-9]*\
1006
			$(find -name include -type d) 		\
1007
			$(find usr/ -name '*.[acho]' 2>/dev/null); do
1008 1009
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
1010
			mkdir -p "$d"
1011
			mv "$pkgdir/$i" "$d"
1012
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
1013 1014
		fi
	done
1015 1016 1017 1018 1019 1020 1021
	# 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
1022
	return 0
Natanael Copa's avatar
Natanael Copa committed
1023 1024
}

1025 1026 1027 1028
dev() {
	default_dev
}

1029 1030 1031 1032
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

1033 1034 1035 1036 1037 1038 1039 1040
do_fakeroot() {
	if [ -n "$FAKEROOT" ]; then
		$FAKEROOT -- "$@"
	else
		"$@"
	fi
}

Natanael Copa's avatar
Natanael Copa committed
1041 1042
# build and package in fakeroot
rootpkg() {
1043 1044 1045 1046
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
1047
		do_build=package
1048
	fi
Natanael Copa's avatar
Natanael Copa committed
1049
	cd "$startdir"
1050
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
1051
	do_fakeroot "$abuild_path" $color_opt $do_build \
1052
		prepare_subpackages \
1053
		prepare_language_packs \
1054
		prepare_package \
1055
		create_apks
Natanael Copa's avatar
Natanael Copa committed
1056 1057 1058
}

srcpkg() {
1059
	getpkgver || return 1
1060
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
1061
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
1062 1063 1064 1065 1066
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
1067
	msg "Creating source package $p.src.tar.gz..."
1068
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
1069 1070
}

Natanael Copa's avatar
Natanael Copa committed
1071 1072
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
1073
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
1074 1075
}

1076
# check if package is up to date
1077
apk_up2date() {
1078
	getpkgver || return 1
1079
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
1080
	local i s
Natanael Copa's avatar
Natanael Copa committed
1081
	cd "$startdir"
1082 1083 1084 1085 1086
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

1087
	for i in $source APKBUILD; do
1088 1089 1090 1091 1092 1093
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
1094 1095 1096 1097 1098 1099 1100
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

1101
abuildindex_up2date() {
1102
	local i
1103
	getpkgver || return 1
1104 1105 1106 1107 1108 1109 1110
	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
1111
			
1112 1113 1114 1115 1116
	# 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
1117
		
1118 1119 1120 1121
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
1122
	check_arch || return 0
1123 1124 1125 1126 1127 1128 1129
	apk_up2date && abuildindex_up2date
}

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

1132 1133 1134 1135 1136 1137 1138
# source all APKBUILDs and output:
#  1) origin of package