abuild.in 29.9 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
set_xterm_title() {
77
	if [ "$TERM" = xterm ] && [ -n "$USE_COLORS" ]; 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
}

Natanael Copa's avatar
Natanael Copa committed
173 174
# verify upstream sources
sourcecheck() {
175 176 177 178 179 180 181 182 183 184
	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
185
	done
186
	return 0
Natanael Copa's avatar
Natanael Copa committed
187
}
188

Natanael Copa's avatar
Natanael Copa committed
189 190
uri_fetch() {
	local uri="$1"
191
	local d="${uri##*/}"	# $(basename $uri)
192 193 194 195
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

196 197 198 199 200
	# 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
201
	esac
202 203 204 205 206

	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
Natanael Copa's avatar
Natanael Copa committed
207
	
Natanael Copa's avatar
Natanael Copa committed
208
	mkdir -p "$SRCDEST"
209 210 211
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
212
	fi
Linux User's avatar
Linux User committed
213
	msg "Fetching $uri"
214 215
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
216 217
}

218 219
is_remote() {
	case "$1" in
220
		http://*|ftp://*|https://*|saveas-*://*)
221 222 223 224 225
			return 0;;
	esac
	return 1
}

226 227 228 229 230
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
231 232 233 234 235
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
236 237 238 239
	fi
	uri_fetch "$uri"
}

240
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
241 242
	local s
	mkdir -p "$srcdir"
243 244
	for s in $source; do
		if is_remote "$s"; then
245
			uri_fetch_mirror "$s" || return 1
246 247 248 249
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
250 251 252
	done
}

253 254 255 256
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
257
# unpack the sources
258
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
259
	local u
260 261 262
	if [ -z "$force" ]; then
		md5check || return 1
	fi
Natanael Copa's avatar
Natanael Copa committed
263 264 265 266
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
267
			*.tar.gz|*.tgz)
268
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
269 270
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
271
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
272
				tar -C "$srcdir" -jxf "$s" || return 1;;
273 274 275 276
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
277 278 279
			*.tar.xz)
				msg "Unpacking $s..."
				unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
Natanael Copa's avatar
Natanael Copa committed
280 281 282
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
283 284 285 286
		esac
	done
}

287 288 289 290
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
291 292
# cleanup source and package dir
clean() {
293
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
294
	rm -rf "$srcdir"
295
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
296 297 298 299 300 301
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
302 303 304 305
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
306 307 308 309 310
	done
}

cleanpkg() {
	local i
311
	getpkgver || return 1
312
	msg "Cleaning built packages..."
313
	for i in $pkgname $subpackages; do
314 315
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
316
			"$abuildrepo"/$p.apk
Natanael Copa's avatar
Natanael Copa committed
317
	done
318
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
319 320
}

321 322 323
# clean all packages except current
cleanoldpkg() {
	local i j
324
	getpkgver || return 1
325 326 327 328 329 330 331 332 333
	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
}
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351

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
352 353
runpart() {
	local part=$1
354
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
355 356 357 358
	$part || die "$part failed"
}

# override those in your build script
359 360 361 362 363 364 365 366
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
367 368 369 370
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
371
build() {
372
	:
Natanael Copa's avatar
Natanael Copa committed
373 374
}

375 376 377 378 379 380
# 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
381 382 383 384 385 386 387 388 389
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
}

390
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
391
	if [ -z "$subpackages" ]; then
392 393
		return 0
	fi
Natanael Copa's avatar
Natanael Copa committed
394 395
	local i
	cd "$startdir"
396
	for i in $subpackages; do
Natanael Copa's avatar
Natanael Copa committed
397 398
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
399
		msg "Running split function $func..."
400
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
401
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
402 403 404
	done
}

405
prepare_metafiles() {
406
	getpkgver || return 1
407 408
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
409
	local dir=${subpkgdir:-$pkgdir}
410
	local pkg="$name-$pkgver-r$pkgrel.apk"
411
	local pkginfo="$controldir"/.PKGINFO
412
	local sub
Natanael Copa's avatar
Natanael Copa committed
413 414 415
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
416
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
417 418
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
419 420 421 422
	local parch="$CARCH"
	if [ "$arch" = "noarch" ]; then
		parch="noarch"
	fi
Natanael Copa's avatar
Natanael Copa committed
423

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

	local metafiles=".PKGINFO"
Natanael Copa's avatar
Natanael Copa committed
474
	for i in $install ${triggers%%:*}; do
475 476
		script=${i#$name}
		case "$script" in
Natanael Copa's avatar
Natanael Copa committed
477
			.pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
478 479
				msg "Adding $script"
				;;
Natanael Copa's avatar
Natanael Copa committed
480
			*) 	error "$script: Invalid install/trigger script"
481 482 483
				return 1
				;;
		esac
Natanael Copa's avatar
Natanael Copa committed
484
		cp "$startdir/$i" "$controldir/$script" || return 1
485
		chmod +x "$controldir/$script"
486 487
		metafiles="$metafiles $script"
	done
488
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
489
}
490

491 492 493
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
	options_has "!tracedeps" && return 0
494
	# lets tell all the .so files this package provides in .provides-so
495 496
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
497 498 499 500 501
	# 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
502
	scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
503 504 505
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
506 507
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
508
	done
Natanael Copa's avatar
Natanael Copa committed
509 510
}

511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
# 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
}

538
prepare_package() {
539
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
540
	stripbin
541 542
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
543 544 545 546 547 548 549 550
}

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

551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
# 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
}

574 575 576
trace_apk_deps() {
	local name="$1"
	local dir="$2"
577
	local i= j= found= autodeps= deppkgs= missing= so_paths=
578
	msg "Tracing dependencies for $name..."
579 580 581
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
582
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
583 584 585 586 587 588 589
		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"
590
		msg "  added libgcc (due to libpthread)"
591
	fi
592
	for i in $(cat "$dir"/.needs-so 2>/dev/null); do
593
		found=
594
		# first check if its provide by same apkbuild
595
		for j in "$dir"/../.control.*/.provides-so; do
596
			grep -w "$i" "$j" >/dev/null || continue
597
			found=${j%/.provides-so}
598
			found=${found##*/.control.}
599
			break
600
		done
601 602 603 604 605 606
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
607
		fi
608 609 610 611 612 613 614
	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
615
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
616 617 618
			warning "You can remove '$found' from depends"
			continue
		fi
619 620
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
621
			msg "  added $found"
622
		fi
623
	done
624

625 626 627 628 629 630 631
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

632 633
create_apks() {
	local file
634
	getpkgver || return 1
635
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
636 637 638
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
639
		local apk=$name-$ver.apk
640
		local datadir="$pkgbasedir"/$name
641 642

		trace_apk_deps "$name" "$dir" || return 1
643
		msg "Creating $apk..."
644
		(
645 646
		cd "$datadir"
		# data.tar.gz
647 648 649 650 651 652
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
653 654 655 656 657 658

		# 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
659
		cd "$dir"
660 661
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
662
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
663

664 665 666
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
667 668 669
	done
}

670
update_abuildrepo() {
671
	if ! apk_up2date || [ -n "$force" ]; then
672
		sanitycheck && builddeps && clean && fetch && unpack \
Natanael Copa's avatar
Natanael Copa committed
673
			&& prepare && mkusers && rootpkg || return 1
674
	fi
675

676
	local apk
677 678
	mkdir -p "$abuildrepo" || return 1
	cd "$abuildrepo"
679 680 681 682 683 684 685 686 687

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

	# create links for this package
688
	for apk in $(listpkg); do
689
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$apk
690 691
	done

692
	msg "Updating the cached abuild repository index..."
693 694 695 696 697
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
	if [ -f APKINDEX.tar.gz ]; then
		oldindex="--index APKINDEX.tar.gz"
	fi
698
	$APK index $oldindex --output APKINDEX.tar.gz.unsigned \
699 700
		--description "$repo $(cd $startdir && git describe)" \
		*.apk || exit 1
701 702 703
	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
704
	chmod 644 APKINDEX.tar.gz
705 706
}

707
# predefined splitfunc doc
708
default_doc() {
709
	depends="$depends_doc"
710
	install="$install_doc"
Natanael Copa's avatar
Natanael Copa committed
711
	triggers="$triggers_doc"
712
	arch=${arch_doc:-"noarch"}
Natanael Copa's avatar
Natanael Copa committed
713
	local i
714
	for i in doc man info html sgml licenses gtk-doc; do
715 716 717
		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
718 719
		fi
	done
720 721 722
	
	rm -f "$subpkgdir/usr/share/info/dir"

723 724 725
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
726 727 728 729

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

Natanael Copa's avatar
Natanael Copa committed
730
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
731 732 733
	return 0
}

734 735 736 737
doc() {
	default_doc
}

738
# predefined splitfunc mod
739
default_mod() {
740
	depends="$kernel $depends_mod"
741
	install="$install_mod"
Natanael Copa's avatar
Natanael Copa committed
742
	for i in firmware modules; do
743 744 745
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
746
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
747 748 749 750
		fi
	done
}

751 752 753 754
mod() {
	default_mod
}

755
# predefined splitfunc dev
756
default_dev() {
757
	local i= j=
758
	depends="$pkgname $depends_dev"
759
	install="$install_dev"
Natanael Copa's avatar
Natanael Copa committed
760
	triggers="$triggers_dev"
761 762 763 764
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
765
	cd "$pkgdir" || return 0
766 767
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
768
			usr/share/vala/vapi usr/share/gir-[0-9]*\
769 770 771
			$(find -name include -type d) 		\
			$(find usr/ -name '*.[acho]' -o -name '*.la' \
			2>/dev/null); do
772 773
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
774
			mkdir -p "$d"
775
			mv "$pkgdir/$i" "$d"
776
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
777 778
		fi
	done
779 780 781 782 783 784 785
	# 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
786
	return 0
Natanael Copa's avatar
Natanael Copa committed
787 788
}

789 790 791 792
dev() {
	default_dev
}

793 794 795 796
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

Natanael Copa's avatar
Natanael Copa committed
797 798
# build and package in fakeroot
rootpkg() {
799 800 801 802
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
803
		do_build=package
804
	fi
Natanael Copa's avatar
Natanael Copa committed
805
	cd "$startdir"
806
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
807
	$FAKEROOT -- "$abuild_path" $color_opt $do_build \
808 809
		prepare_subpackages \
		prepare_package \
810
		create_apks
Natanael Copa's avatar
Natanael Copa committed
811 812 813
}

srcpkg() {
814
	getpkgver || return 1
815
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
816
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
817 818 819 820 821
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
822
	msg "Creating source package $p.src.tar.gz..."
823
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
824 825
}

Natanael Copa's avatar
Natanael Copa committed
826 827
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
828
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
829 830
}

831
# check if package is up to date
832
apk_up2date() {
833
	getpkgver || return 1
834
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
835
	local i s
Natanael Copa's avatar
Natanael Copa committed
836
	cd "$startdir"
837 838 839 840 841
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

842
	for i in $source APKBUILD; do
843 844 845 846 847 848
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
849 850 851 852 853 854 855
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

856
abuildindex_up2date() {
857
	local i apk
858
	getpkgver || return 1
859 860
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
861
		[ "$abuildrepo"/APKINDEX.tar.gz -nt "$abuildrepo"/$apk ] || return 1
862 863 864 865 866
	done
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
867
	check_arch || return 0
868 869 870 871 872 873 874
	apk_up2date && abuildindex_up2date
}

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

877 878 879 880 881 882 883
# 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
	(
884 885
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
886 887 888 889
		pkgname=
		subpackages=
		depends=
		makedepends=
890
		. $i
891
		dir=${i%/APKBUILD}
892
		deps=
893
		# filter out conflicts from deps and version info
894 895 896 897
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
898
			deps="$deps ${j%%[<>=]*}"
899
		done
900
		for j in $pkgname $subpackages; do
901
			echo "o ${j%%:*} $dir"
902
			set -- $deps
903 904 905 906 907 908 909
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
910 911
		done
	done
912
	)
913 914
}

915
deptrace() {
916 917 918
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
919
		deps="$deps ${i%%[<>=]*}"
920
	done
921
	[ -z "$deps" ] && return 0
922 923 924
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
925
			$APK info -q | sort |  sed 's/^/i /'
926
		fi
927
	) | awk -v pkgs="$deps" '
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952

	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() {
953
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
954
	local filtered_deps= conflicts=
955
	[ -n "$nodeps" ] && return 0
956
	msg "Analyzing dependencies..."
957

958 959 960
	# add depends unless it is a subpackage or package itself
	for i in $BUILD_BASE $depends $makedepends; do
		[ "$pkgname" = "${i%%[<>=]*}" ] && continue
961
		subpackages_has ${i%%[<>=]*} || deps="$deps $i"
962 963
	done

964
	installed_deps=$($APK info -e $deps)
965
	# find which deps are missing
966
	for i in $deps; do
967
		if [ "${i#\!}" != "$i" ]; then
968
			$APK info -q -e "${i#\!}" \
969
				&& conflicts="$conflicts ${i#\!}"
970
		elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
971 972 973
			missing="$missing $i"
		fi
	done
974
	
975
	if [ -n "$conflicts" ]; then
976 977
		error "Conflicting package(s) installed:$conflicts"
		return 1
978 979 980 981 982 983 984 985
	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
986

987
	uninstall_after=".makedepends-$pkgname $uninstall_after"
988
	if [ -n "$install_deps" ] && [ -z "$recursive" ] && [ -n "$deps" ]; then
989 990
		# make a --simluate run first to detect missing deps
		# apk-tools --virtual is no goot at reporting those.
991
		$SUDO $APK add --repository "$abuildrepo" \
992 993
			--wait 30 \
			--simulate --quiet $deps || return 1
994
		$SUDO $APK add --repository "$abuildrepo" \
995
			--wait 30 \
996 997
			--virtual .makedepends-$pkgname $deps \
			&& return 0
998
	fi
999 1000
	
	[ -z "$recursive" ] && return 1
1001 1002 1003

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
1004
		local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
1005
		if [ -z "$m" ]; then
1006 1007 1008
			missing="$missing $i"
		fi
	done
1009 1010
	
	for i in $(deptrace $missing); do
1011 1012 1013
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023

		# 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 \
1024
			$recursive $upgrade $color_opt abuildindex || return 1
1025
	done
1026
	$SUDO $APK add -u --repository "$abuildrepo" \
1027
		--wait 30 \
1028
		--virtual .makedepends-$pkgname $deps
1029 1030
}

1031
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
1032 1033
checksum() {
	local s files
1034
	[ -z "$source" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
1035
	fetch
1036
	msg "Updating the md5sums in APKBUILD..."
Natanael Copa's avatar
Natanael Copa committed
1037 1038 1039 1040
	for s in $source; do
		files="$files ${s##*/}"
	done
	md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
1041 1042
	sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
	echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
1043 1044
}

1045
stripbin() {
1046
	local bin
1047 1048 1049
	if options_has "!strip" || [ "$arch" = "noarch" ]; then
		return 0
	fi
1050 1051
	cd "${subpkgdir:-$pkgdir}" || return 1
	msg "Stripping binaries"
1052 1053 1054
	scanelf --recursive --nobanner --etype "ET_DYN,ET_EXEC" . \
		| sed -e 's:^ET_DYN ::' -e 's:^ET_EXEC ::' \
		| xargs -r strip
1055 1056
}

1057 1058 1059
# simply list target apks
listpkg() {
	local i 
1060
	getpkgver || return 1
1061 1062 1063 1064 1065
	for i in $pkgname $subpackages; do
		echo "${i%:*}-$pkgver-r$pkgrel.apk"
	done
}

Natanael Copa's avatar
Natanael Copa committed
1066 1067 1068 1069 1070 1071 1072 1073
source_has() {
	local i
	for i in $source; do
		[ "$1" = "${i##*/}" ] && return 0
	done
	return 1
}

Natanael Copa's avatar
abuild:  
Natanael Copa committed
1074 1075 1076 1077 1078 1079 1080 1081
subpackages_has() {
	local i
	for i in $subpackages; do
		[ "$1" = "${i%:*}" ] && return 0
	done
	return 1
}

1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
list_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

1093 1094 1095 1096 1097 1098
# same as list_has but we filter version info
deplist_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
1099
		i=${i%%[<>=]*}
1100 1101 1102 1103 1104 1105
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

1106 1107 1108 1109
options_has() {
	list_has "$1" $options
}

1110
depends_has() {
1111
	deplist_has "$1" $depends
1112 1113
}

Natanael Copa's avatar
Natanael Copa committed
1114
makedepends_has() {
1115
	deplist_has "$1" $makedepends
Natanael Copa's avatar
Natanael Copa committed
1116 1117
}

Natanael Copa's avatar
Natanael Copa committed
1118 1119 1120 1121
md5sums_has() {
	list_has "$1" $md5sums