abuild.in 32.4 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() {
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
}

Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
204
	done
205
	return 0
Natanael Copa's avatar
Natanael Copa committed
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)}";;
Natanael Copa's avatar
Natanael Copa committed
220
	esac
221 222 223 224 225

	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
Natanael Copa's avatar
Natanael Copa committed
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 *
}

Natanael Copa's avatar
Natanael Copa committed
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
Natanael Copa's avatar
Natanael Copa committed
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"
Natanael Copa's avatar
Natanael Copa committed
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"
}

902 903 904 905 906 907 908 909
do_fakeroot() {
	if [ -n "$FAKEROOT" ]; then
		$FAKEROOT -- "$@"
	else
		"$@"
	fi
}

Natanael Copa's avatar
Natanael Copa committed
910 911
# build and package in fakeroot
rootpkg() {
912 913 914 915
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
916
		do_build=package
917
	fi
Natanael Copa's avatar
Natanael Copa committed
918
	cd "$startdir"
919
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
920
	do_fakeroot "$abuild_path" $color_opt $do_build \
921
		prepare_subpackages \
922
		prepare_language_packs \
923
		prepare_package \
924
		create_apks
Natanael Copa's avatar
Natanael Copa committed
925 926 927
}

srcpkg() {
928
	getpkgver || return 1
929
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
930
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
931 932 933 934 935
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
936
	msg "Creating source package $p.src.tar.gz..."
937
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
938 939
}

Natanael Copa's avatar
Natanael Copa committed
940 941
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
942
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
943 944
}

945
# check if package is up to date
946
apk_up2date() {
947
	getpkgver || return 1
948
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
949
	local i s
Natanael Copa's avatar
Natanael Copa committed
950
	cd "$startdir"
951 952 953 954 955
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

956
	for i in $source APKBUILD; do
957 958 959 960 961 962
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
963 964 965 966 967 968 969
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

970
abuildindex_up2date() {
971
	local i apk
972
	getpkgver || return 1
973 974
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
975
		[ "$abuildrepo"/APKINDEX.tar.gz -nt "$abuildrepo"/$apk ] || return 1
976 977 978 979 980
	done
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
981
	check_arch || return 0
982 983 984 985 986 987 988
	apk_up2date && abuildindex_up2date
}

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

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

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

	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() {
1067
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
1068
	local filtered_deps= conflicts=
1069
	[ -n "$nodeps" ] && return 0
1070
	msg "Analyzing dependencies..."
1071

1072 1073 1074
	# add depends unless it is a subpackage or package itself
	for i in $BUILD_BASE $depends $makedepends; do
		[ "$pkgname" = "${i%%[<>=]*}" ] && continue
1075
		subpackages_has ${i%%[<>=]*} || deps="$deps $i"
1076 1077
	done

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

1101
	uninstall_after=".makedepends-$pkgname $uninstall_after"
1102
	if [ -n "$install_deps" ] && [ -z "$recursive" ] && [ -n "$deps" ]; then
1103 1104
		# make a --simluate run first to detect missing deps
		# apk-tools --virtual is no goot at reporting those.
1105
		$SUDO $APK add --repository "$abuildrepo" \
1106 1107
			--wait 30 \
			--simulate --quiet $deps || return 1
1108
		$SUDO $APK add --repository "$abuildrepo" \
1109
			--wait 30 \
1110 1111
			--virtual .makedepends-$pkgname $deps \
			&& return 0
1112
	fi
1113 1114
	
	[ -z "$recursive" ] && return 1
1115 1116 1117

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
1118
		local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
1119
		if [ -z "$m" ]; then
1120 1121 1122
			missing="$missing $i"
		fi
	done
1123 1124
	
	for i in $(deptrace $missing); do
1125 1126 1127
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137

		# 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 \
1138
			$recursive $upgrade $color_opt abuildindex || return 1
1139
	done
1140
	$SUDO $APK add -u --repository "$abuildrepo" \
1141
		--wait 30 \
1142
		--virtual .makedepends-$pkgname $deps
1143 1144
}