abuild.in 32.3 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
}
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" ||\
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"
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() {
158
	local dummy f endreturnval originalparams origin file
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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
	IFS=$'\n'
	endreturnval=0
	originalparams=$@
	set -- $source
	for src in $md5sums; do
		origin=$1; shift
		echo "$src" | md5sum -c
		if [ $? -ne 0 ]; then
			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
			endreturnval=1
		fi
	done
	unset IFS
	set -- $originalparams
	return $endreturnval
Natanael Copa's avatar
Natanael Copa committed
190 191
}

192 193
# verify upstream sources
sourcecheck() {
194 195 196 197 198 199 200 201 202 203
	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
204
	done
205
	return 0
206
}
207

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

215 216 217 218 219
	# 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)}";;
220
	esac
221 222 223 224 225

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

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

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

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

272 273 274 275
fetch() {
	default_fetch
}

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

322 323 324 325
unpack() {
	default_unpack
}

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

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

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

366 367 368
# clean all packages except current
cleanoldpkg() {
	local i j
369
	getpkgver || return 1
370
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
371
	for i in $(listpkgnames); do
372 373 374 375 376 377 378
		for j in "$PKGDEST"/${i%:*}-[0-9]*.apk; do
			[ "$j" != "$PKGDEST/${i%:*}-$pkgver-r$pkgrel.apk" ] \
				&& rm -f "$j"
		done
	done
	return 0
}
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396

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
397 398
runpart() {
	local part=$1
399
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
400 401 402 403
	$part || die "$part failed"
}

# override those in your build script
404 405 406 407 408 409 410 411
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
412 413 414 415
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
416
build() {
417
	:
Natanael Copa's avatar
Natanael Copa committed
418 419
}

420 421 422 423 424 425
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

426 427 428 429 430 431 432 433 434
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
}

435
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
436 437
	local i
	cd "$startdir"
438
	for i in $subpackages; do
439 440
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
441
		msg "Running split function $func..."
442
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
443
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
444 445 446
	done
}

447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
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
}

469 470 471 472 473 474 475 476 477 478 479 480
# 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}'
}

481 482 483 484 485 486
get_maintainer() {
	if [ -z "$maintainer" ]; then
		maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
	fi
}

487
prepare_metafiles() {
488
	getpkgver || return 1
489 490
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
491
	local dir=${subpkgdir:-$pkgdir}
492
	local pkg="$name-$pkgver-r$pkgrel.apk"
493
	local pkginfo="$controldir"/.PKGINFO
494
	local sub
Natanael Copa's avatar
Natanael Copa committed
495 496 497
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
498
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
499 500
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
501
	local parch="$CARCH"
502 503 504 505 506

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

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

	local metafiles=".PKGINFO"
Natanael Copa's avatar
Natanael Copa committed
568
	for i in $install ${triggers%%:*}; do
569 570
		script=${i#$name}
		case "$script" in
Natanael Copa's avatar
Natanael Copa committed
571
			.pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
572 573
				msg "Adding $script"
				;;
Natanael Copa's avatar
Natanael Copa committed
574
			*) 	error "$script: Invalid install/trigger script"
575 576 577
				return 1
				;;
		esac
Natanael Copa's avatar
Natanael Copa committed
578
		cp "$startdir/$i" "$controldir/$script" || return 1
579
		chmod +x "$controldir/$script"
580 581
		metafiles="$metafiles $script"
	done
582
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
583
}
584

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

606 607 608 609 610 611 612 613 614 615 616 617
# 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
}

618 619 620 621 622
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

623 624 625 626 627 628 629
# 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
630 631 632
	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\""
633 634 635 636
	fi
	return 0
}

637
prepare_package() {
638
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
639
	stripbin
640 641
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
642 643 644 645 646 647 648 649
}

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

650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
# 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
}

673 674 675
trace_apk_deps() {
	local name="$1"
	local dir="$2"
676
	local i= j= found= autodeps= deppkgs= missing= so_paths= self_provided=
677
	msg "Tracing dependencies for $name..."
678 679 680
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
681
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
682 683 684 685
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
686
	if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
687 688
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
689
		msg "  added libgcc (due to libpthread)"
690
	fi
691
	[ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
692
		found=
693
		# first check if its provide by same apkbuild
694
		for j in "$dir"/../.control.*/.provides-so; do
695
			grep -w "$i" "$j" >/dev/null || continue
696
			found=${j%/.provides-so}
697
			found=${found##*/.control.}
698
			break
699
		done
700 701 702 703 704 705
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
706
		fi
707 708 709
	done

	# find all packages that holds the so files
710 711 712 713 714
	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

715
	for found in $self_provided $deppkgs; do
716
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
717 718 719
			warning "You can remove '$found' from depends"
			continue
		fi
720 721
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
722
			msg "  added $found"
723
		fi
724
	done
725

726 727 728 729 730 731 732
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

733 734
create_apks() {
	local file
735
	getpkgver || return 1
736
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
737 738 739
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
740
		local apk=$name-$ver.apk
741
		local datadir="$pkgbasedir"/$name
742 743

		trace_apk_deps "$name" "$dir" || return 1
744
		msg "Creating $apk..."
745
		(
746 747
		cd "$datadir"
		# data.tar.gz
748 749 750 751 752 753
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
754 755 756 757 758 759

		# 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
760
		cd "$dir"
761 762
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
763
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
764

765 766 767
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
768 769 770
	done
}

771
update_abuildrepo() {
772
	if ! apk_up2date || [ -n "$force" ]; then
773
		sanitycheck && builddeps && clean && fetch && unpack \
Natanael Copa's avatar
Natanael Copa committed
774
			&& prepare && mkusers && rootpkg || return 1
775
	fi
776

777
	local apk
778 779
	mkdir -p "$abuildrepo" || return 1
	cd "$abuildrepo"
780 781 782 783 784 785 786 787 788

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

	# create links for this package
789
	for apk in $(listpkg); do
790
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$apk
791 792
	done

793
	msg "Updating the cached abuild repository index..."
794 795 796 797 798
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
	if [ -f APKINDEX.tar.gz ]; then
		oldindex="--index APKINDEX.tar.gz"
	fi
799
	$APK index $oldindex --output APKINDEX.tar.gz.unsigned \
800 801
		--description "$repo $(cd $startdir && git describe)" \
		*.apk || exit 1
802 803 804
	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
805
	chmod 644 APKINDEX.tar.gz
806 807
}

808
# predefined splitfunc doc
809
default_doc() {
810
	depends="$depends_doc"
811
	install="$install_doc"
Natanael Copa's avatar
Natanael Copa committed
812
	triggers="$triggers_doc"
813
	pkgdesc="$pkgdesc (documentation)"
814
	arch=${arch_doc:-"noarch"}
815

Natanael Copa's avatar
Natanael Copa committed
816
	local i
817
	for i in doc man info html sgml licenses gtk-doc; do
818 819 820
		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
821 822
		fi
	done
823 824 825
	
	rm -f "$subpkgdir/usr/share/info/dir"

826 827 828
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
829 830 831 832

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

Natanael Copa's avatar
Natanael Copa committed
833
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
834 835 836
	return 0
}

837 838 839 840
doc() {
	default_doc
}

841
# predefined splitfunc mod
842
default_mod() {
843
	depends="$kernel $depends_mod"
844
	install="$install_mod"
Natanael Copa's avatar
Natanael Copa committed
845
	for i in firmware modules; do
846 847 848
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
849
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
850 851 852 853
		fi
	done
}

854 855 856 857
mod() {
	default_mod
}

858
# predefined splitfunc dev
859
default_dev() {
860
	local i= j=
861
	depends="$pkgname $depends_dev"
862
	install="$install_dev"
Natanael Copa's avatar
Natanael Copa committed
863
	triggers="$triggers_dev"
864 865
	pkgdesc="$pkgdesc (development files)"

866 867 868 869
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
870
	cd "$pkgdir" || return 0
871 872
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
873
			usr/share/vala/vapi usr/share/gir-[0-9]*\
874 875 876
			$(find -name include -type d) 		\
			$(find usr/ -name '*.[acho]' -o -name '*.la' \
			2>/dev/null); do
877 878
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
879
			mkdir -p "$d"
880
			mv "$pkgdir/$i" "$d"
881
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
882 883
		fi
	done
884 885 886 887 888 889 890
	# 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
891
	return 0
Natanael Copa's avatar
Natanael Copa committed
892 893
}

894 895 896 897
dev() {
	default_dev
}

898 899 900 901
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

Natanael Copa's avatar
Natanael Copa committed
902 903
# build and package in fakeroot
rootpkg() {
904 905 906 907
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
908
		do_build=package
909
	fi
Natanael Copa's avatar
Natanael Copa committed
910
	cd "$startdir"
911
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
912
	$FAKEROOT -- "$abuild_path" $color_opt $do_build \
913
		prepare_subpackages \
914
		prepare_language_packs \
915
		prepare_package \
916
		create_apks
Natanael Copa's avatar
Natanael Copa committed
917 918 919
}

srcpkg() {
920
	getpkgver || return 1
921
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
922
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
923 924 925 926 927
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
928
	msg "Creating source package $p.src.tar.gz..."
929
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
930 931
}

Natanael Copa's avatar
Natanael Copa committed
932 933
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
934
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
935 936
}

937
# check if package is up to date
938
apk_up2date() {
939
	getpkgver || return 1
940
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
941
	local i s
Natanael Copa's avatar
Natanael Copa committed
942
	cd "$startdir"
943 944 945 946 947
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

948
	for i in $source APKBUILD; do
949 950 951 952 953 954
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
955 956 957 958 959 960 961
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

962
abuildindex_up2date() {
963
	local i apk
964
	getpkgver || return 1
965 966
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
967
		[ "$abuildrepo"/APKINDEX.tar.gz -nt "$abuildrepo"/$apk ] || return 1
968 969 970 971 972
	done
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
973
	check_arch || return 0
974 975 976 977 978 979 980
	apk_up2date && abuildindex_up2date
}

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

983 984 985 986 987 988 989
# 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
	(
990 991
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
992 993 994 995
		pkgname=
		subpackages=
		depends=
		makedepends=
996
		. $i
997
		dir=${i%/APKBUILD}
998
		deps=
999
		# filter out conflicts from deps and version info
1000 1001 1002 1003
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
1004
			deps="$deps ${j%%[<>=]*}"
1005
		done
1006
		for j in $pkgname $subpackages; do
1007
			echo "o ${j%%:*} $dir"
1008
			set -- $deps
1009 1010 1011 1012 1013 1014 1015
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
1016 1017
		done
	done
1018
	)
1019 1020
}

1021
deptrace() {
1022 1023 1024
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
1025
		deps="$deps ${i%%[<>=]*}"
1026
	done
1027
	[ -z "$deps" ] && return 0
1028 1029 1030
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
1031
			$APK info -q | sort |  sed 's/^/i /'
1032
		fi
1033
	) | awk -v pkgs="$deps" '
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058

	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() {
1059
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
1060
	local filtered_deps= conflicts=
1061
	[ -n "$nodeps" ] && return 0
1062
	msg "Analyzing dependencies..."
1063

1064 1065 1066
	# add depends unless it is a subpackage or package itself
	for i in $BUILD_BASE $depends $makedepends; do
		[ "$pkgname" = "${i%%[<>=]*}" ] && continue
1067
		subpackages_has ${i%%[<>=]*} || deps="$deps $i"
1068 1069
	done

1070
	installed_deps=$($APK info -e $deps)
1071
	# find which deps are missing
1072
	for i in $deps; do
1073
		if [ "${i#\!}" != "$i" ]; then
1074
			$APK info -q -e "${i#\!}" \
1075
				&& conflicts="$conflicts ${i#\!}"
1076
		elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
1077 1078 1079
			missing="$missing $i"
		fi
	done
1080
	
1081
	if [ -n "$conflicts" ]; then
1082 1083
		error "Conflicting package(s) installed:$conflicts"
		return 1
1084 1085 1086 1087 1088 1089 1090 1091
	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
1092

1093
	uninstall_after=".makedepends-$pkgname $uninstall_after"
1094
	if [ -n "$install_deps" ] && [ -z "$recursive" ] && [ -n "$deps" ]; then
1095 1096
		# make a --simluate run first to detect missing deps
		# apk-tools --virtual is no goot at reporting those.
1097
		$SUDO $APK add --repository "$abuildrepo" \
1098 1099
			--wait 30 \
			--simulate --quiet $deps || return 1
1100
		$SUDO $APK add --repository "$abuildrepo" \
1101
			--wait 30 \
1102 1103
			--virtual .makedepends-$pkgname $deps \
			&& return 0
1104
	fi
1105 1106
	
	[ -z "$recursive" ] && return 1
1107 1108 1109

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
1110
		local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
1111
		if [ -z "$m" ]; then
1112 1113 1114
			missing="$missing $i"
		fi
	done
1115 1116
	
	for i in $(deptrace $missing); do
1117 1118 1119
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
1120 1121 1122 1123 1124 1125 1126 1127 1128 1129

		# 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 \
1130
			$recursive $upgrade $color_opt abuildindex || return 1
1131
	done
1132
	$SUDO $APK add -u --repository "$abuildrepo" \
1133
		--wait 30 \
1134
		--virtual .makedepends-$pkgname $deps
1135 1136
}

1137
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
1138 1139
checksum() {
	local s files
1140
	[ -z "$source" ] && return 0
Natanael Copa's avatar