abuild.in 33.6 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
}
Natanael Copa's avatar
Natanael Copa committed
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 98
# check if apkbuild is basicly sane
sanitycheck() {
99
	local i 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" ||\
Natanael Copa's avatar
Natanael Copa committed
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 120 121 122
	# 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
123 124 125 126 127 128 129
	for i in $install; do
		[ -e "$startdir/$i" ] || die "install script $startdir/$i is missing"
	done
	
	[ -n "${triggers%%:*}" ] && [ ! -e "$startdir"/${triggers%%:*} ] \
		&& die "trigger script $startdir/${triggers%%:*} is missing"

130 131
	if [ -n "$source" ]; then
		for i in $source; do
Natanael Copa's avatar
Natanael Copa committed
132 133 134 135
			if install_has "$i"; then
				warning "You should not have \$install in source"
				continue
			fi
136
			md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
Natanael Copa's avatar
Natanael Copa committed
137 138 139
			case "$i" in
				https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
			esac
140 141
		done
	fi
Natanael Copa's avatar
Natanael Copa committed
142

143 144
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
145
			source_has $i || die "$i exists in md5sums but is missing in source"
146 147
		done
	fi
148

149 150 151
	# 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
152 153

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
156 157 158 159
	return 0
}

md5check() {
160
	local dummy f endreturnval originalparams origin file
161 162 163
	if [ -z "$source" ]; then
		return 0
	fi
164
	if [ -z "$md5sums" ]; then
165
		die "Use 'abuild checksum' to generate/update the checksum(s)"
166
	fi
167 168 169
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
170
	fetch || return 1
171
	msg "Checking md5sums..."
Natanael Copa's avatar
Natanael Copa committed
172
	cd "$srcdir" || return 1
173 174 175 176 177 178 179 180
	IFS=$'\n'
	endreturnval=0
	originalparams=$@
	set -- $source
	for src in $md5sums; do
		origin=$1; shift
		echo "$src" | md5sum -c
		if [ $? -ne 0 ]; then
181
			endreturnval=1
182 183 184 185 186 187 188 189 190 191 192
			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
193 194
}

Natanael Copa's avatar
Natanael Copa committed
195 196
# verify upstream sources
sourcecheck() {
197 198 199 200 201 202 203 204 205 206
	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
Natanael Copa's avatar
Natanael Copa committed
207
	done
208
	return 0
Natanael Copa's avatar
Natanael Copa committed
209
}
210

Natanael Copa's avatar
Natanael Copa committed
211 212
uri_fetch() {
	local uri="$1"
213
	local d="${uri##*/}"	# $(basename $uri)
214 215 216 217
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

218 219 220 221 222
	# 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)}";;
Natanael Copa's avatar
Natanael Copa committed
223
	esac
224 225 226 227 228

	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
Natanael Copa's avatar
Natanael Copa committed
229
	
Natanael Copa's avatar
Natanael Copa committed
230
	mkdir -p "$SRCDEST"
231 232 233
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
234
	fi
Linux User's avatar
Linux User committed
235
	msg "Fetching $uri"
236 237
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
238 239
}

240 241
is_remote() {
	case "$1" in
242
		http://*|ftp://*|https://*|saveas-*://*)
243 244 245 246 247
			return 0;;
	esac
	return 1
}

248 249 250 251 252
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
253 254 255 256 257
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
258 259 260 261
	fi
	uri_fetch "$uri"
}

262
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
263 264
	local s
	mkdir -p "$srcdir"
265 266
	for s in $source; do
		if is_remote "$s"; then
267
			uri_fetch_mirror "$s" || return 1
268 269 270 271
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
272 273 274
	done
}

275 276 277 278
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
# 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
294
# unpack the sources
295
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
296
	local u
297 298
	if [ -z "$force" ]; then
		md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
299
		initdcheck || return 1
300
	fi
Natanael Copa's avatar
Natanael Copa committed
301 302 303 304
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
305
			*.tar.gz|*.tgz)
306
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
307 308
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
309
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
310
				tar -C "$srcdir" -jxf "$s" || return 1;;
311 312 313 314
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
315 316 317
			*.tar.xz)
				msg "Unpacking $s..."
				unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
Natanael Copa's avatar
Natanael Copa committed
318 319 320
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
321 322 323 324
		esac
	done
}

325 326 327 328
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
329 330
# cleanup source and package dir
clean() {
331
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
332
	rm -rf "$srcdir"
333
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
334 335 336 337 338 339
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
340 341 342 343
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
344 345 346
	done
}

347 348 349 350 351 352 353 354 355 356
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
357 358
cleanpkg() {
	local i
359
	getpkgver || return 1
360
	msg "Cleaning built packages..."
361
	for i in $(listpkgnames); do
362 363
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
364
			"$abuildrepo"/$p.apk "$abuildrepo"/*/$p.apk
Natanael Copa's avatar
Natanael Copa committed
365
	done
366
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
367 368
}

369 370 371
# clean all packages except current
cleanoldpkg() {
	local i j
372
	getpkgver || return 1
373
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
374
	for i in $(listpkgnames); do
375 376 377 378 379
		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##*/}
380 381 382 383
		done
	done
	return 0
}
384 385 386 387 388

mkusers() {
	local i
	for i in $pkgusers; do
		if ! getent passwd $i >/dev/null; then
389
			local gopt=
390
			msg "Creating user $i"
391 392 393 394
			if getent group $i >/dev/null; then
				gopt="-G $i"
			fi
			$SUDO adduser -D -H $gopt $i || return 1
395 396 397 398 399 400 401 402 403 404 405
		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
406 407
runpart() {
	local part=$1
408
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
409 410 411 412
	$part || die "$part failed"
}

# override those in your build script
413 414 415 416 417 418 419 420
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
421 422 423 424
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
425
build() {
426
	:
Natanael Copa's avatar
Natanael Copa committed
427 428
}

429 430 431
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
Natanael Copa's avatar
Natanael Copa committed
432
	mkdir -p "$PKGDEST"
433 434 435
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

Natanael Copa's avatar
Natanael Copa committed
436 437 438 439 440 441 442 443 444
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
}

445
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
446 447
	local i
	cd "$startdir"
448
	for i in $subpackages; do
Natanael Copa's avatar
Natanael Copa committed
449 450
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
451
		msg "Running split function $func..."
452
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
453
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
454 455 456
	done
}

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478
lang_subpkg() {
	if [ -z "$lang" ]; then
		error "lang is not set"
		return 1
	fi
	arch="noarch"
	install_if="$pkgname=$pkgver-r$pkgrel lang-$lang"
	mkdir -p "$subpkgdir"/usr/share/locale
	mv "$pkgdir"/usr/share/locale/$lang* \
		"$subpkgdir"/usr/share/locale/ \
		|| return 1
}

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
}

479 480 481 482 483 484 485 486 487 488 489 490
# 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}'
}

491 492 493 494 495 496
get_maintainer() {
	if [ -z "$maintainer" ]; then
		maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
	fi
}

497
prepare_metafiles() {
498
	getpkgver || return 1
499 500
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
501
	local dir=${subpkgdir:-$pkgdir}
502
	local pkg="$name-$pkgver-r$pkgrel.apk"
503
	local pkginfo="$controldir"/.PKGINFO
504
	local sub
Natanael Copa's avatar
Natanael Copa committed
505 506 507
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
508
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
509 510
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
511
	local parch="$CARCH"
512 513 514 515 516

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

518
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
519
	if [ -n "$FAKEROOTKEY" ]; then
520
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
521
	fi
522 523
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
524
pkgname = $name
525
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
526 527 528 529 530
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
Natanael Copa's avatar
Natanael Copa committed
531
arch = $parch
Natanael Copa's avatar
Natanael Copa committed
532
origin = $pkgname
Natanael Copa's avatar
Natanael Copa committed
533
EOF
534 535
	local i deps
	deps="$depends"
536
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
Natanael Copa's avatar
Natanael Copa committed
537 538
		for i in $install ${triggers%%:*}; do
			if head -n 1 "$startdir/$i" | grep '^#!/bin/sh' >/dev/null ; then
539 540 541 542 543
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
544 545
	fi
	
546 547 548 549 550 551 552 553 554 555
	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
556
	for i in $license; do
557
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
558 559
	done
	for i in $replaces; do
560
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
561
	done
562
	for i in $deps; do
563
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
564 565
	done
	for i in $conflicts; do
566
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
567 568
	done
	for i in $provides; do
569
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
570
	done
Natanael Copa's avatar
Natanael Copa committed
571 572 573
	if [ -n "$triggers" ]; then
		echo "triggers = ${triggers#*:}" >> "$pkginfo"
	fi
574 575 576
	if [ -n "$install_if" ]; then
		echo "install_if = $(echo $install_if)" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
577 578

	local metafiles=".PKGINFO"
Natanael Copa's avatar
Natanael Copa committed
579
	for i in $install ${triggers%%:*}; do
580 581
		script=${i#$name}
		case "$script" in
Natanael Copa's avatar
Natanael Copa committed
582
			.pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
583 584
				msg "Adding $script"
				;;
Natanael Copa's avatar
Natanael Copa committed
585
			*) 	error "$script: Invalid install/trigger script"
586 587 588
				return 1
				;;
		esac
Natanael Copa's avatar
Natanael Copa committed
589
		cp "$startdir/$i" "$controldir/$script" || return 1
590
		chmod +x "$controldir/$script"
591 592
		metafiles="$metafiles $script"
	done
593
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
594
}
595

596 597
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
598
	[ "$arch" = "noarch" ] && return 0
599
	options_has "!tracedeps" && return 0
600
	# lets tell all the .so files this package provides in .provides-so
601 602
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
603 604 605 606 607
	# 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
608
	scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
609 610 611
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
612 613
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
614
	done
Natanael Copa's avatar
Natanael Copa committed
615 616
}

617 618 619 620 621 622 623 624 625 626 627 628
# 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
}

629 630 631 632 633
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

634 635 636 637 638 639 640
# 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
641 642 643
	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\""
644 645 646 647
	fi
	return 0
}

648
prepare_package() {
649
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
650
	stripbin
651 652
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
653 654 655 656 657 658 659 660
}

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

661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
# 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
}

684 685 686
trace_apk_deps() {
	local name="$1"
	local dir="$2"
Natanael Copa's avatar
Natanael Copa committed
687
	local i= j= found= autodeps= deppkgs= missing= so_paths= self_provided=
688
	msg "Tracing dependencies for $name..."
689 690 691
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
692
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
693 694 695 696
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
697
	if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
698 699
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
700
		msg "  added libgcc (due to libpthread)"
701
	fi
702
	[ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
703
		found=
704
		# first check if its provide by same apkbuild
705
		for j in "$dir"/../.control.*/.provides-so; do
706
			grep -w "$i" "$j" >/dev/null || continue
707
			found=${j%/.provides-so}
708
			found=${found##*/.control.}
709
			break
710
		done
711 712 713 714 715 716
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
717
		fi
718 719 720
	done

	# find all packages that holds the so files
721 722 723 724 725
	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

726
	for found in $self_provided $deppkgs; do
727
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
728 729 730
			warning "You can remove '$found' from depends"
			continue
		fi
731 732
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
733
			msg "  added $found"
734
		fi
735
	done
736

737 738 739 740 741 742 743
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

744 745
create_apks() {
	local file
746
	getpkgver || return 1
Natanael Copa's avatar
Natanael Copa committed
747
	mkdir -p "$PKGDEST"
748
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
749 750 751
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
752
		local apk=$name-$ver.apk
753
		local datadir="$pkgbasedir"/$name
754 755

		trace_apk_deps "$name" "$dir" || return 1
756
		msg "Creating $apk..."
757
		(
758 759
		cd "$datadir"
		# data.tar.gz
760 761 762 763 764 765
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
766 767 768 769 770 771

		# 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
772
		cd "$dir"
773 774
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
775
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
776

777 778 779
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
780 781 782
	done
}

783
clean_abuildrepo() {
784
	local apk
785 786 787
	cd "$abuildrepo" || return 1
	
	# remove compat symlink
788
	for d in "$abuildrepo/$CARCH"; do
789 790
		[ -L "$d" ] && rm "$d"
	done
791

792 793
	# remove broken links from abuildrepo
	for apk in *.apk */*.apk; do
794 795 796 797
		if [ -L "$apk" ] && [ ! -f "$apk" ]; then
			rm -f "$apk"
		fi
	done
798
}
799

800
mklinks_abuildrepo() {
801 802
	local apk
	mkdir -p "$abuildrepo"/$CARCH
803
	cd "$abuildrepo" || return 1
804
	# create links for this package
805
	for apk in $(listpkg); do
806
		[ -f "$PKGDEST"/$apk ] || continue
807
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk
808
	done
809 810 811 812 813 814 815 816 817 818 819 820 821 822
}

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
823

824
	msg "Updating the cached abuild repository index..."
825 826
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
827 828
	if [ -f "$index" ]; then
		oldindex="--index $index"
829
	fi
830
	$APK index --quiet $oldindex --output "$index".unsigned \
831
		--description "$repo $(cd $startdir && git describe)" \
832 833
		--rewrite-arch $CARCH \
		$CARCH/*.apk || exit 1
834
	msg "Signing the index..."
835 836 837
	abuild-sign -q "$index".unsigned || exit 1
	mv "$index".unsigned "$index"
	chmod 644 "$index"
838 839
}

840
# predefined splitfunc doc
841
default_doc() {
842
	depends="$depends_doc"
843
	install="$install_doc"
Natanael Copa's avatar
Natanael Copa committed
844
	triggers="$triggers_doc"
845
	pkgdesc="$pkgdesc (documentation)"
846
	arch=${arch_doc:-"noarch"}
847

Natanael Copa's avatar
Natanael Copa committed
848
	local i
849
	for i in doc man info html sgml licenses gtk-doc; do
850 851 852
		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
853 854
		fi
	done
855 856 857
	
	rm -f "$subpkgdir/usr/share/info/dir"

858 859 860
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
861 862 863 864

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

Natanael Copa's avatar
Natanael Copa committed
865
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
866 867 868
	return 0
}

869 870 871 872
doc() {
	default_doc
}

873
# predefined splitfunc mod
874
default_mod() {
875
	depends="$kernel $depends_mod"
876
	install="$install_mod"
Natanael Copa's avatar
Natanael Copa committed
877
	for i in firmware modules; do
878 879 880
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
881
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
882 883 884 885
		fi
	done
}

886 887 888 889
mod() {
	default_mod
}

890
# predefined splitfunc dev
891
default_dev() {
892
	local i= j=
893
	depends="$pkgname $depends_dev"
894
	install="$install_dev"
Natanael Copa's avatar
Natanael Copa committed
895
	triggers="$triggers_dev"
896 897
	pkgdesc="$pkgdesc (development files)"

898 899 900 901
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
902
	cd "$pkgdir" || return 0
903 904
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
905
			usr/share/vala/vapi usr/share/gir-[0-9]*\
906 907 908
			$(find -name include -type d) 		\
			$(find usr/ -name '*.[acho]' -o -name '*.la' \
			2>/dev/null); do
909 910
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
911
			mkdir -p "$d"
912
			mv "$pkgdir/$i" "$d"
913
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
914 915
		fi
	done
916 917 918 919 920 921 922
	# 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
923
	return 0
Natanael Copa's avatar
Natanael Copa committed
924 925
}

926 927 928 929
dev() {
	default_dev
}

930 931 932 933
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

934 935 936 937 938 939 940 941
do_fakeroot() {
	if [ -n "$FAKEROOT" ]; then
		$FAKEROOT -- "$@"
	else
		"$@"
	fi
}

Natanael Copa's avatar
Natanael Copa committed
942 943
# build and package in fakeroot
rootpkg() {
944 945 946 947
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
948
		do_build=package
949
	fi
Natanael Copa's avatar
Natanael Copa committed
950
	cd "$startdir"
951
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
952
	do_fakeroot "$abuild_path" $color_opt $do_build \
953
		prepare_subpackages \
954
		prepare_language_packs \
955
		prepare_package \
956
		create_apks
Natanael Copa's avatar
Natanael Copa committed
957 958 959
}

srcpkg() {
960
	getpkgver || return 1
961
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
962
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
963 964 965 966 967
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
968
	msg "Creating source package $p.src.tar.gz..."
969
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
970 971
}

Natanael Copa's avatar
Natanael Copa committed
972 973
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
974
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
975 976
}

977
# check if package is up to date
978
apk_up2date() {
979
	getpkgver || return 1
980
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
981
	local i s
Natanael Copa's avatar
Natanael Copa committed
982
	cd "$startdir"
983 984 985 986 987
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

988
	for i in $source APKBUILD; do
989 990 991 992 993 994
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
995 996 997 998 999 1000 1001
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

1002
abuildindex_up2date() {
1003
	local i
1004
	getpkgver || return 1
1005 1006 1007 1008 1009 1010 1011
	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
1012
			
1013 1014 1015 1016 1017
	# 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
1018
		
1019 1020 1021 1022
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
1023
	check_arch || return 0
1024 1025 1026 1027 1028 1029 1030
	apk_up2date && abuildindex_up2date
}

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

1033 1034 1035 1036 1037 1038 1039
# 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
	(
1040 1041
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
1042 1043 1044 1045
		pkgname=
		subpackages=
		depends=
		makedepends=
1046
		. $i
1047
		dir=${i%/APKBUILD}
1048
		deps=
1049
		# filter out conflicts from deps and version info
1050 1051 1052 1053
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
1054
			deps="$deps ${j%%[<>=]*}"
1055
		done
1056
		for j in $pkgname $subpackages; do
1057
			echo "o ${j%%:*} $dir"
1058
			set -- $deps
1059 1060 1061 1062 1063 1064 1065
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
1066 1067
		done
	done
1068
	)
1069 1070
}

1071
deptrace() {
1072 1073 1074
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
1075
		deps="$deps ${i%%[<>=]*}"
1076
	done
1077
	[ -z "$deps" ] && return 0
1078 1079 1080
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
1081
			$APK info -q | sort |  sed 's/^/i /'
1082
		fi
1083
	) | awk -v pkgs="$deps" '
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108

	function depgraph(pkg,	a, i) {
		if (visited[pkg])
			return 0;
		visited[pkg] = 1;
		split(deps[pkg], a, ",");
		for (i in a)
			depgraph(a[i]);
		print pkg ":" origin[pkg];

	}

	$1 == "i" { visited[$2] = 1 }
	$1 == "o" { origin[$2] = $3 }
	$1 == "d" { deps[$2] = $3 }
	END {
		split(pkgs, pkgarray);
		for (i in pkgarray)
			depgraph(pkgarray[i]);
	}
	'
}

# build and install dependencies
builddeps() {
1109
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
1110
	local filtered_deps= conflicts=
1111
	[ -n "$nodeps" ] && return 0
1112
	msg "Analyzing dependencies..."
1113

1114 1115 1116
	# add depends unless it is a subpackage or package itself
	for i in $BUILD_BASE $depends $makedepends; do
		[ "$pkgname" = "${i%%[<>=]*}" ] && continue
1117
		subpackages_has ${i%%[<>=]*} || deps="$deps $i"
1118 1119
	done

1120
	installed_deps=$($APK info -e $deps)
1121
	# find which deps are missing
1122
	for i in $deps; do
1123
		if [ "${i#\!}" != "$i" ]; then
1124
			$APK info -q -e "${i#\!}" \
1125
				&& conflicts="$conflicts ${i#\!}"
1126
		elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
1127 1128 1129
			missing="$missing $i"
		fi
	done
1130
	
1131
	if [ -n "$conflicts" ]; then
1132 1133
		error "Conflicting package(s) installed:$conflicts"
		return 1
1134 1135 1136 1137 1138 1139 1140 1141
	fi
	
	if [ -z "$install_deps" ] && [ -z "$recursive" ]; then
		# if we dont have any missing deps we are done now
		[ -z "$missing" ] && return 0
		error "Missing dependencies: $missing Use -r to autoinstall or -R to build"
		return 1
	fi