abuild.in 29.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=@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

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

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

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

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

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

cleanup() {
	set_xterm_title ""
84
	if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
85
		$SUDO $APK del $uninstall_after
86
	fi
Linux User's avatar
Linux User committed
87 88
}

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

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

111 112 113 114 115 116 117 118 119 120
	# 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
121 122 123 124 125 126 127
	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"

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

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

147 148 149
	# 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
150 151

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

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

md5check() {
Natanael Copa's avatar
Natanael Copa committed
158
	local dummy f
159 160 161
	if [ -z "$source" ]; then
		return 0
	fi
162
	if [ -z "$md5sums" ]; then
163
		die "Use 'abuild checksum' to generate/update the checksum(s)"
164
	fi
165 166 167
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
168
	fetch || return 1
169
	msg "Checking md5sums..."
170
	cd "$srcdir" && echo "$md5sums" | md5sum -c 
Natanael Copa's avatar
Natanael Copa committed
171 172 173 174
}

uri_fetch() {
	local uri="$1"
175
	local d="${uri##*/}"	# $(basename $uri)
176 177 178 179
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

180 181 182 183 184
	# 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
185
	esac
186 187 188 189 190

	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
Natanael Copa's avatar
Natanael Copa committed
191
	
Natanael Copa's avatar
Natanael Copa committed
192
	mkdir -p "$SRCDEST"
193 194 195
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
196
	fi
Linux User's avatar
Linux User committed
197
	msg "Fetching $uri"
198 199
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
200 201
}

202 203
is_remote() {
	case "$1" in
204
		http://*|ftp://*|https://*|saveas-*://*)
205 206 207 208 209
			return 0;;
	esac
	return 1
}

210 211 212 213 214
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
215 216 217 218 219
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
220 221 222 223
	fi
	uri_fetch "$uri"
}

224
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
225 226
	local s
	mkdir -p "$srcdir"
227 228
	for s in $source; do
		if is_remote "$s"; then
229
			uri_fetch_mirror "$s" || return 1
230 231 232 233
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
234 235 236
	done
}

237 238 239 240
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
241
# unpack the sources
242
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
243
	local u
244 245 246
	if [ -z "$force" ]; then
		md5check || return 1
	fi
Natanael Copa's avatar
Natanael Copa committed
247 248 249 250
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
251
			*.tar.gz|*.tgz)
252
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
253 254
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
255
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
256
				tar -C "$srcdir" -jxf "$s" || return 1;;
257 258 259 260
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
261 262 263
			*.tar.xz)
				msg "Unpacking $s..."
				unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
Natanael Copa's avatar
Natanael Copa committed
264 265 266
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
267 268 269 270
		esac
	done
}

271 272 273 274
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
275 276
# cleanup source and package dir
clean() {
277
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
278
	rm -rf "$srcdir"
279
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
280 281 282 283 284 285
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
286 287 288 289
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
290 291 292 293 294
	done
}

cleanpkg() {
	local i
295
	getpkgver || return 1
296
	msg "Cleaning built packages..."
297
	for i in $pkgname $subpackages; do
298 299
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
300
			"$abuildrepo"/$p.apk
Natanael Copa's avatar
Natanael Copa committed
301
	done
302
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
303 304
}

305 306 307
# clean all packages except current
cleanoldpkg() {
	local i j
308
	getpkgver || return 1
309 310 311 312 313 314 315 316 317
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
	for i in $pkgname $subpackages; do
		for j in "$PKGDEST"/${i%:*}-[0-9]*.apk; do
			[ "$j" != "$PKGDEST/${i%:*}-$pkgver-r$pkgrel.apk" ] \
				&& rm -f "$j"
		done
	done
	return 0
}
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335

mkusers() {
	local i
	for i in $pkgusers; do
		if ! getent passwd $i >/dev/null; then
			msg "Creating user $i"
			$SUDO adduser -D -H $i || return 1
		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
336 337
runpart() {
	local part=$1
338
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
339 340 341 342
	$part || die "$part failed"
}

# override those in your build script
343 344 345 346 347 348 349 350
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
351 352 353 354
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
355
build() {
356
	:
Natanael Copa's avatar
Natanael Copa committed
357 358
}

359 360 361 362 363 364
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

Natanael Copa's avatar
Natanael Copa committed
365 366 367 368 369 370 371 372 373
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
}

374
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
375
	if [ -z "$subpackages" ]; then
376 377
		return 0
	fi
Natanael Copa's avatar
Natanael Copa committed
378 379
	local i
	cd "$startdir"
380
	for i in $subpackages; do
Natanael Copa's avatar
Natanael Copa committed
381 382
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
383
		msg "Running split function $func..."
384
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
385
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
386 387 388
	done
}

389
prepare_metafiles() {
390
	getpkgver || return 1
391 392
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
393
	local dir=${subpkgdir:-$pkgdir}
394
	local pkg="$name-$pkgver-r$pkgrel.apk"
395
	local pkginfo="$controldir"/.PKGINFO
396
	local sub
Natanael Copa's avatar
Natanael Copa committed
397 398 399
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
400
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
401 402
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
403 404 405 406
	local parch="$CARCH"
	if [ "$arch" = "noarch" ]; then
		parch="noarch"
	fi
Natanael Copa's avatar
Natanael Copa committed
407

408
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
409
	if [ -n "$FAKEROOTKEY" ]; then
410
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
411
	fi
412 413
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
414
pkgname = $name
415
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
416 417 418 419 420
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
Natanael Copa's avatar
Natanael Copa committed
421
arch = $parch
Natanael Copa's avatar
Natanael Copa committed
422
EOF
423 424
	local i deps
	deps="$depends"
425
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
Natanael Copa's avatar
Natanael Copa committed
426 427
		for i in $install ${triggers%%:*}; do
			if head -n 1 "$startdir/$i" | grep '^#!/bin/sh' >/dev/null ; then
428 429 430 431 432
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
433 434
	fi
	
Natanael Copa's avatar
Natanael Copa committed
435
	for i in $license; do
436
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
437 438
	done
	for i in $replaces; do
439
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
440
	done
441
	for i in $deps; do
442
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
443 444
	done
	for i in $conflicts; do
445
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
446 447
	done
	for i in $provides; do
448
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
449 450
	done
	for i in $backup; do
451
		echo "backup = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
452
	done
Natanael Copa's avatar
Natanael Copa committed
453 454 455
	if [ -n "$triggers" ]; then
		echo "triggers = ${triggers#*:}" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
456 457

	local metafiles=".PKGINFO"
Natanael Copa's avatar
Natanael Copa committed
458
	for i in $install ${triggers%%:*}; do
459 460
		script=${i#$name}
		case "$script" in
Natanael Copa's avatar
Natanael Copa committed
461
			.pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
462 463
				msg "Adding $script"
				;;
Natanael Copa's avatar
Natanael Copa committed
464
			*) 	error "$script: Invalid install/trigger script"
465 466 467
				return 1
				;;
		esac
Natanael Copa's avatar
Natanael Copa committed
468
		cp "$startdir/$i" "$controldir/$script" || return 1
469
		chmod +x "$controldir/$script"
470 471
		metafiles="$metafiles $script"
	done
472
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
473
}
474

475 476 477
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
	options_has "!tracedeps" && return 0
478
	# lets tell all the .so files this package provides in .provides-so
479 480
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
481 482 483 484 485
	# 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
486
	scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
487 488 489
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
490 491
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
492
	done
Natanael Copa's avatar
Natanael Copa committed
493 494
}

495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
# 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
}

# 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
	else
		[ "$arch" = "noarch" ] && return 0
		error "No arch specific binaries found so arch should be set to \"noarch\""
		return 1
	fi
	return 0
}

522
prepare_package() {
523
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
524
	stripbin
525 526
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
527 528 529 530 531 532 533 534
}

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

535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
# 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
}

558 559 560
trace_apk_deps() {
	local name="$1"
	local dir="$2"
561
	local i= j= found= autodeps= deppkgs= missing= so_paths=
562
	msg "Tracing dependencies for $name..."
563 564 565
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
566
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
567 568 569 570 571 572 573
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
	if grep -q -w '^libpthread.so.*' "$dir"/.needs-so 2>/dev/null \
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
574
		msg "  added libgcc (due to libpthread)"
575
	fi
576
	for i in $(cat "$dir"/.needs-so 2>/dev/null); do
577
		found=
578
		# first check if its provide by same apkbuild
579
		for j in "$dir"/../.control.*/.provides-so; do
580
			grep -w "$i" "$j" >/dev/null || continue
581
			found=${j%/.provides-so}
582
			found=${found##*/.control.}
583
			break
584
		done
585 586 587 588 589 590
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
591
		fi
592 593 594 595 596 597 598
	done

	# find all packages that holds the so files
	so_files=$(find_so_files "$dir"/.rpaths $missing) || return 1
	deppkgs=$($APK info -q -W $so_files) || return 1
	
	for found in $self_provided $deppkgs; do
599
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
600 601 602
			warning "You can remove '$found' from depends"
			continue
		fi
603 604
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
605
			msg "  added $found"
606
		fi
607
	done
608

609 610 611 612 613 614 615
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

616 617
create_apks() {
	local file
618
	getpkgver || return 1
619
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
620 621 622
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
623
		local apk=$name-$ver.apk
624
		local datadir="$pkgbasedir"/$name
625 626

		trace_apk_deps "$name" "$dir" || return 1
627
		msg "Creating $apk..."
628
		(
629 630
		cd "$datadir"
		# data.tar.gz
631 632 633 634 635 636
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
637 638 639 640 641 642

		# 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
643
		cd "$dir"
644 645
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
646
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
647

648 649 650
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
651 652 653
	done
}

654
update_abuildrepo() {
655
	if ! apk_up2date || [ -n "$force" ]; then
656
		sanitycheck && builddeps && clean && fetch && unpack \
Natanael Copa's avatar
Natanael Copa committed
657
			&& prepare && mkusers && rootpkg || return 1
658
	fi
659

660
	local apk
661 662
	mkdir -p "$abuildrepo" || return 1
	cd "$abuildrepo"
663 664 665 666 667 668 669 670 671

	# remove broken links
	for apk in *.apk; do
		if [ -L "$apk" ] && [ ! -f "$apk" ]; then
			rm -f "$apk"
		fi
	done

	# create links for this package
672
	for apk in $(listpkg); do
673
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$apk
674 675
	done

676
	msg "Updating the cached abuild repository index..."
677 678 679 680 681
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
	if [ -f APKINDEX.tar.gz ]; then
		oldindex="--index APKINDEX.tar.gz"
	fi
682
	$APK index $oldindex --output APKINDEX.tar.gz.unsigned \
683 684
		--description "$repo $(cd $startdir && git describe)" \
		*.apk || exit 1
685 686 687
	msg "Signing the index..."
	abuild-sign -q APKINDEX.tar.gz.unsigned || exit 1
	mv APKINDEX.tar.gz.unsigned APKINDEX.tar.gz
Natanael Copa's avatar
Natanael Copa committed
688
	chmod 644 APKINDEX.tar.gz
689 690
}

691
# predefined splitfunc doc
692
default_doc() {
693
	depends="$depends_doc"
694
	install="$install_doc"
Natanael Copa's avatar
Natanael Copa committed
695
	triggers="$triggers_doc"
696
	arch=${arch_doc:-"noarch"}
Natanael Copa's avatar
Natanael Copa committed
697
	local i
698
	for i in doc man info html sgml licenses gtk-doc; do
699 700 701
		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
702 703
		fi
	done
704 705 706
	
	rm -f "$subpkgdir/usr/share/info/dir"

707 708 709
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
710 711 712 713

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

Natanael Copa's avatar
Natanael Copa committed
714
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
715 716 717
	return 0
}

718 719 720 721
doc() {
	default_doc
}

722
# predefined splitfunc mod
723
default_mod() {
724
	depends="$kernel $depends_mod"
725
	install="$install_mod"
Natanael Copa's avatar
Natanael Copa committed
726
	for i in firmware modules; do
727 728 729
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
730
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
731 732 733 734
		fi
	done
}

735 736 737 738
mod() {
	default_mod
}

739
# predefined splitfunc dev
740
default_dev() {
741
	local i= j=
742
	depends="$pkgname $depends_dev"
743
	install="$install_dev"
Natanael Copa's avatar
Natanael Copa committed
744
	triggers="$triggers_dev"
745 746 747 748
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
749
	cd "$pkgdir" || return 0
750 751
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
752
			usr/share/vala/vapi usr/share/gir-[0-9]*\
753 754 755
			$(find -name include -type d) 		\
			$(find usr/ -name '*.[acho]' -o -name '*.la' \
			2>/dev/null); do
756 757
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
758
			mkdir -p "$d"
759
			mv "$pkgdir/$i" "$d"
760
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
761 762
		fi
	done
763 764 765 766 767 768 769
	# 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
770
	return 0
Natanael Copa's avatar
Natanael Copa committed
771 772
}

773 774 775 776
dev() {
	default_dev
}

777 778 779 780
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

Natanael Copa's avatar
Natanael Copa committed
781 782
# build and package in fakeroot
rootpkg() {
783 784 785 786
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
787
		do_build=package
788
	fi
Natanael Copa's avatar
Natanael Copa committed
789
	cd "$startdir"
790
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
791
	$FAKEROOT -- "$abuild_path" $color_opt $do_build \
792 793
		prepare_subpackages \
		prepare_package \
794
		create_apks
Natanael Copa's avatar
Natanael Copa committed
795 796 797
}

srcpkg() {
798
	getpkgver || return 1
799
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
800
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
801 802 803 804 805
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
806
	msg "Creating source package $p.src.tar.gz..."
807
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
808 809
}

Natanael Copa's avatar
Natanael Copa committed
810 811
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
812
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
813 814
}

815
# check if package is up to date
816
apk_up2date() {
817
	getpkgver || return 1
818
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
819
	local i s
Natanael Copa's avatar
Natanael Copa committed
820
	cd "$startdir"
821 822 823 824 825
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

826
	for i in $source APKBUILD; do
827 828 829 830 831 832
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
833 834 835 836 837 838 839
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

840
abuildindex_up2date() {
841
	local i apk
842
	getpkgver || return 1
843 844
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
845
		[ "$abuildrepo"/APKINDEX.tar.gz -nt "$abuildrepo"/$apk ] || return 1
846 847 848 849 850
	done
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
851
	check_arch || return 0
852 853 854 855 856 857 858
	apk_up2date && abuildindex_up2date
}

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

861 862 863 864 865 866 867
# 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
	(
868 869
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
870 871 872 873
		pkgname=
		subpackages=
		depends=
		makedepends=
874
		. $i
875
		dir=${i%/APKBUILD}
876
		deps=
877
		# filter out conflicts from deps and version info
878 879 880 881
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
882
			deps="$deps ${j%%[<>=]*}"
883
		done
884
		for j in $pkgname $subpackages; do
885
			echo "o ${j%%:*} $dir"
886
			set -- $deps
887 888 889 890 891 892 893
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
894 895
		done
	done
896
	)
897 898
}

899
deptrace() {
900 901 902
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
903
		deps="$deps ${i%%[<>=]*}"
904
	done
905
	[ -z "$deps" ] && return 0
906 907 908
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
909
			$APK info -q | sort |  sed 's/^/i /'
910
		fi
911
	) | awk -v pkgs="$deps" '
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936

	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() {
937
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
938
	local filtered_deps= conflicts=
939
	[ -n "$nodeps" ] && return 0
940
	msg "Analyzing dependencies..."
941

942 943 944
	# add depends unless it is a subpackage or package itself
	for i in $BUILD_BASE $depends $makedepends; do
		[ "$pkgname" = "${i%%[<>=]*}" ] && continue
945
		subpackages_has ${i%%[<>=]*} || deps="$deps $i"
946 947
	done

948
	installed_deps=$($APK info -e $deps)
949
	# find which deps are missing
950
	for i in $deps; do
951
		if [ "${i#\!}" != "$i" ]; then
952
			$APK info -q -e "${i#\!}" \
953
				&& conflicts="$conflicts ${i#\!}"
954
		elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
955 956 957
			missing="$missing $i"
		fi
	done
958
	
959
	if [ -n "$conflicts" ]; then
960 961
		error "Conflicting package(s) installed:$conflicts"
		return 1
962 963 964 965 966 967 968 969
	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
970

971
	uninstall_after=".makedepends-$pkgname $uninstall_after"
972
	if [ -n "$install_deps" ] && [ -z "$recursive" ] && [ -n "$deps" ]; then
973 974
		# make a --simluate run first to detect missing deps
		# apk-tools --virtual is no goot at reporting those.
975
		$SUDO $APK add --repository "$abuildrepo" \
976 977
			--wait 30 \
			--simulate --quiet $deps || return 1
978
		$SUDO $APK add --repository "$abuildrepo" \
979
			--wait 30 \
980 981
			--virtual .makedepends-$pkgname $deps \
			&& return 0
982
	fi
983 984
	
	[ -z "$recursive" ] && return 1
985 986 987

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
988
		local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
989
		if [ -z "$m" ]; then
990 991 992
			missing="$missing $i"
		fi
	done
993 994
	
	for i in $(deptrace $missing); do
995 996 997
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
998 999 1000 1001 1002 1003 1004 1005 1006 1007

		# ignore if dependency is in other repo
		[ -d "$dir" ] || continue
		
		# break cricular deps
		list_has $pkg $ABUILD_VISITED && continue
		export ABUILD_VISITED="$ABUILD_VISITED $pkg"

		msg "Entering $dir"
		cd "$dir" && $0 $forceroot $keep $quiet $install_deps \
1008
			$recursive $upgrade $color_opt abuildindex || return 1
1009
	done
1010
	$SUDO $APK add -u --repository "$abuildrepo" \
1011
		--wait 30 \
1012
		--virtual .makedepends-$pkgname $deps
1013 1014
}

1015
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
1016 1017
checksum() {
	local s files
1018
	[ -z "$source" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
1019
	fetch
1020
	msg "Updating the md5sums in APKBUILD..."
Natanael Copa's avatar
Natanael Copa committed
1021 1022 1023 1024
	for s in $source; do
		files="$files ${s##*/}"
	done
	md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
1025 1026
	sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
	echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
1027 1028
}

1029
stripbin() {
1030
	local bin
1031 1032 1033
	if options_has "!strip" || [ "$arch" = "noarch" ]; then
		return 0
	fi
1034 1035
	cd "${subpkgdir:-$pkgdir}" || return 1
	msg "Stripping binaries"
1036 1037 1038
	scanelf --recursive --nobanner --etype "ET_DYN,ET_EXEC" . \
		| sed -e 's:^ET_DYN ::' -e 's:^ET_EXEC ::' \
		| xargs -r strip
1039 1040
}

1041 1042 1043
# simply list target apks
listpkg() {
	local i 
1044
	getpkgver || return 1
1045 1046 1047 1048 1049
	for i in $pkgname $subpackages; do
		echo "${i%:*}-$pkgver-r$pkgrel.apk"
	done
}

Natanael Copa's avatar
Natanael Copa committed
1050 1051 1052 1053 1054 1055 1056 1057
source_has() {
	local i
	for i in $source; do
		[ "$1" = "${i##*/}" ] && return 0
	done
	return 1
}

Natanael Copa's avatar
abuild:  
Natanael Copa committed
1058 1059 1060 1061 1062 1063 1064 1065
subpackages_has() {
	local i
	for i in $subpackages; do
		[ "$1" = "${i%:*}" ] && return 0
	done
	return 1
}

1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
list_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

1077 1078 1079 1080 1081 1082
# same as list_has but we filter version info
deplist_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
1083
		i=${i%%[<>=]*}
1084 1085 1086 1087 1088 1089
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

1090 1091 1092 1093
options_has() {
	list_has "$1" $options
}

1094
depends_has() {
1095
	deplist_has "$1" $depends
1096 1097
}

Natanael Copa's avatar
Natanael Copa committed
1098
makedepends_has() {
1099
	deplist_has "$1" $makedepends
Natanael Copa's avatar
Natanael Copa committed
1100 1101
}

Natanael Copa's avatar
Natanael Copa committed
1102 1103 1104 1105
md5sums_has() {
	list_has "$1" $md5sums
}

Natanael Copa's avatar
Natanael Copa committed
1106 1107 1108 1109
install_has() {
	list_has "$1" $install
}

Natanael Copa's avatar
abuild:  
Natanael Copa committed
1110 1111
# install package after build
post_add() {
1112
	getpkgver || return 1
Natanael Copa's avatar
abuild:  
Natanael Copa committed
1113 1114 1115 1116 1117 1118
	local pkgf="$PKGDEST/$1-$pkgver-r$pkgrel.apk"
	local deps i
	if ! subpackages_has $1 && [ "$1" != "$pkgname" ]; then
		die "$1 is not built by this APKBUILD"
	fi
	# recursively install dependencies that are provided by this APKBUILD
1119
	deps=$($APK index "$pkgf" 2>/dev/null | awk -F: '$1=="D" { print $2 }')
Natanael Copa's avatar
abuild:  
Natanael Copa committed
1120 1121 1122 1123 1124
	for i in $deps; do
		if subpackages_has $i || [ "$i" = "$pkgname" ]; then
			post_add $i || return 1
		fi
	done