abuild.in 35 KB
Newer Older
Natanael Copa's avatar
Natanael Copa committed
1 2
#!/bin/sh

3
# script to build apk packages (light version of makepkg)
Natanael Copa's avatar
Natanael Copa committed
4 5 6 7 8 9 10
# Copyright (c) 2008 Natanael Copa <natanael.copa@gmail.com>
#
# Distributed under GPL-2
#
# Depends on: busybox utilities, fakeroot, 
#

Natanael Copa's avatar
Natanael Copa committed
11 12
abuild_ver=@VERSION@
sysconfdir=@sysconfdir@
13
abuildrepo_base=@abuildrepo@
Natanael Copa's avatar
Natanael Copa committed
14
datadir=@datadir@
Natanael Copa's avatar
Natanael Copa committed
15

16
program=${0##*/}
17
abuild_path=$(readlink -f $0)
Natanael Copa's avatar
Natanael Copa committed
18 19

# defaults
20
BUILD_BASE="build-base"
21
SUDO=${SUDO:-"sudo"}
22
FAKEROOT=${FAKEROOT:-"fakeroot"}
23
APK=${APK:-apk}
24
apk_opt_wait="--wait 30"
25

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

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

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

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

# functions
msg() {
	local prompt="$GREEN>>>${NORMAL}"
	local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
	local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
	[ -z "$quiet" ] && printf "${prompt} ${name}${fake}: $@\n" >&2
}

warning() {
	local prompt="${YELLOW}>>> WARNING:${NORMAL}"
	local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
	local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
	printf "${prompt} ${name}${fake}: $@\n" >&2
}

error() {
	local prompt="${RED}>>> ERROR:${NORMAL}"
	local fake="${FAKEROOTKEY:+${BLUE}*${NORMAL}}"
	local name="${STRONG}${subpkgname:-$pkgname}${NORMAL}"
	printf "${prompt} ${name}${fake}: $@\n" >&2
}
Natanael Copa's avatar
Natanael Copa committed
75 76

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

cleanup() {
	set_xterm_title ""
85
	if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
86 87
		msg "Uninstalling dependencies..."
		$SUDO $APK del --quiet $apk_opt_wait $uninstall_after
88
	fi
Linux User's avatar
Linux User committed
89 90
}

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

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

113 114 115 116 117 118 119
	# check so no package names starts with -
	for i in $pkgname $subpackages; do
		case $i in
		-*) die "${i%:*} is not a valid package name";;
		esac
	done

120 121 122 123 124 125 126 127 128 129
	# 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
130
	for i in $install; do
131
		local n=${i%.*}
132
		local suff=${i##*.}
133 134 135 136 137
		case "$suff" in
		pre-install|post-install|pre-upgrade|post-upgrade|pre-deinstall|post-deinstall);;
		*) die "$i: unknown install script suffix"
		esac
		if ! subpackages_has "$n" && [ "$n" != "$pkgname" ]; then
138
			die "$i: install script does not match pkgname or any subpackage"
139
		fi
140
		[ -e "$startdir/$i" ] || die "install script $i is missing"
Natanael Copa's avatar
Natanael Copa committed
141 142
	done
	
143 144 145 146 147 148 149 150
	for i in $triggers; do
		local f=${i%=*}
		local p=${f%.trigger}
		[ "$f" = "$i" ] && die "$f: triggers must contain '='" 
		[ "$p" = "$f" ] && die "$f: triggers scripts must have .trigger suffix"
		if ! subpackages_has "$p" && [ "$p" != "$pkgname" ]; then
			die "$p: trigger script does not match pkgname or any subpackage"
		fi
Natanael Copa's avatar
Natanael Copa committed
151

152 153
		[ -e "$startdir"/$f ] || die "trigger script $f is missing"
	done
154 155
	if [ -n "$source" ]; then
		for i in $source; do
Natanael Copa's avatar
Natanael Copa committed
156 157 158 159
			if install_has "$i"; then
				warning "You should not have \$install in source"
				continue
			fi
160
			md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
Natanael Copa's avatar
Natanael Copa committed
161 162 163
			case "$i" in
				https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
			esac
164 165
		done
	fi
Natanael Copa's avatar
Natanael Copa committed
166

167 168
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
169
			source_has $i || die "$i exists in md5sums but is missing in source"
170 171
		done
	fi
172

173 174 175
	# 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
176 177

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
180 181 182
	return 0
}

183 184 185 186
sanitycheck() {
	default_sanitycheck
}

187
md5check() {
188
	local dummy f endreturnval originalparams origin file
189 190 191
	if [ -z "$source" ]; then
		return 0
	fi
192
	if [ -z "$md5sums" ]; then
193
		die "Use 'abuild checksum' to generate/update the checksum(s)"
194
	fi
195 196 197
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
198
	fetch || return 1
199
	msg "Checking md5sums..."
Natanael Copa's avatar
Natanael Copa committed
200
	cd "$srcdir" || return 1
201 202 203 204 205 206 207 208
	IFS=$'\n'
	endreturnval=0
	originalparams=$@
	set -- $source
	for src in $md5sums; do
		origin=$1; shift
		echo "$src" | md5sum -c
		if [ $? -ne 0 ]; then
209
			endreturnval=1
210 211 212 213 214 215 216 217 218 219 220
			is_remote $origin || continue
			echo "Because the remote file above failed the md5sum check it will be deleted."
			echo "Rebuilding will cause it to re-download which in some cases may fix the problem."
			file=`echo "$src" | sed 's/.*[ \t\n]\(.*\)/\1/'`
			echo "Deleting: $file"
			rm $file
		fi
	done
	unset IFS
	set -- $originalparams
	return $endreturnval
Natanael Copa's avatar
Natanael Copa committed
221 222
}

Natanael Copa's avatar
Natanael Copa committed
223 224
# verify upstream sources
sourcecheck() {
225 226 227 228 229 230 231 232 233 234
	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
235
	done
236
	return 0
Natanael Copa's avatar
Natanael Copa committed
237
}
238

Natanael Copa's avatar
Natanael Copa committed
239 240
uri_fetch() {
	local uri="$1"
241
	local d="${uri##*/}"	# $(basename $uri)
242 243 244 245
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

246 247 248 249 250
	# 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
251
	esac
252 253 254 255 256

	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
Natanael Copa's avatar
Natanael Copa committed
257
	
Natanael Copa's avatar
Natanael Copa committed
258
	mkdir -p "$SRCDEST"
259 260 261
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
262
	fi
Linux User's avatar
Linux User committed
263
	msg "Fetching $uri"
264 265
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
266 267
}

268 269
is_remote() {
	case "$1" in
270
		http://*|ftp://*|https://*|saveas-*://*)
271 272 273 274 275
			return 0;;
	esac
	return 1
}

276 277 278 279 280
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
281 282 283 284 285
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
286 287 288 289
	fi
	uri_fetch "$uri"
}

290
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
291 292
	local s
	mkdir -p "$srcdir"
293 294
	for s in $source; do
		if is_remote "$s"; then
295
			uri_fetch_mirror "$s" || return 1
296 297 298 299
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
300 301 302
	done
}

303 304 305 306
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
# 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
322
# unpack the sources
323
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
324
	local u
325 326
	if [ -z "$force" ]; then
		md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
327
		initdcheck || return 1
328
	fi
Natanael Copa's avatar
Natanael Copa committed
329 330 331 332
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
333
			*.tar.gz|*.tgz)
334
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
335 336
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
337
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
338
				tar -C "$srcdir" -jxf "$s" || return 1;;
339 340 341 342
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
343 344 345
			*.tar.xz)
				msg "Unpacking $s..."
				unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
Natanael Copa's avatar
Natanael Copa committed
346 347 348
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
349 350 351 352
		esac
	done
}

353 354 355 356
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
357 358
# cleanup source and package dir
clean() {
359
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
360
	rm -rf "$srcdir"
361
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
362 363 364 365 366 367
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
368 369 370 371
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
372 373 374
	done
}

375 376 377 378 379 380 381 382 383 384
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
385 386
cleanpkg() {
	local i
387
	getpkgver || return 1
388
	msg "Cleaning built packages..."
389
	for i in $(listpkgnames); do
390 391
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
392
			"$abuildrepo"/$p.apk "$abuildrepo"/*/$p.apk
Natanael Copa's avatar
Natanael Copa committed
393
	done
394
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
395 396
}

397 398 399
# clean all packages except current
cleanoldpkg() {
	local i j
400
	getpkgver || return 1
401
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
402
	for i in $(listpkgnames); do
403 404 405 406 407
		local pn=${i%:*}
		for j in "$PKGDEST"/$pn-[0-9]*.apk ; do
			[ "$j" = "$PKGDEST/$pn-$pkgver-r$pkgrel.apk" ] \
				&& continue
			rm -f "$j" "$abuildrepo"/*/${j##*/}
408 409 410 411
		done
	done
	return 0
}
412 413 414 415 416

mkusers() {
	local i
	for i in $pkgusers; do
		if ! getent passwd $i >/dev/null; then
417
			local gopt=
418
			msg "Creating user $i"
419 420 421 422
			if getent group $i >/dev/null; then
				gopt="-G $i"
			fi
			$SUDO adduser -D -H $gopt $i || return 1
423 424 425 426 427 428 429 430 431 432 433
		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
434 435
runpart() {
	local part=$1
436
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
437 438 439 440
	$part || die "$part failed"
}

# override those in your build script
441 442 443 444 445 446 447 448
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
449 450 451 452
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
453
build() {
454
	:
Natanael Copa's avatar
Natanael Copa committed
455 456
}

457 458 459
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
Natanael Copa's avatar
Natanael Copa committed
460
	mkdir -p "$PKGDEST"
461 462 463
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

Natanael Copa's avatar
Natanael Copa committed
464 465 466 467 468 469 470 471 472
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
}

473
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
474 475
	local i
	cd "$startdir"
476
	for i in $subpackages; do
Natanael Copa's avatar
Natanael Copa committed
477 478
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
479
		msg "Running split function $func..."
480
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
481
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
482
	done
483 484 485 486 487 488 489
	# post check
	i=$(find "$pkgdir" -name '*.la')
	if [ -n "$i" ] && ! options_has "libtool"; then
		error "Libtool archives (*.la) files found and \$options has no 'libtool' flag:"
		echo "$i" | sed 's/^/  /'
		return 1
	fi
490 491 492 493
	# post check for /usr/share/locale
	if [ -d "$pkgdir"/usr/share/locale ]; then
		warning "Found /usr/share/local. Maybe add \$pkgname-lang to subpackages?"
	fi
Natanael Copa's avatar
Natanael Copa committed
494 495
}

496
default_lang() {
497
	pkgdesc="Languages for package $pkgname"
498 499 500 501 502 503 504 505 506 507 508 509 510
	install_if="$pkgname=$pkgver-r$pkgrel lang"
	arch="noarch"
	local dir
	for dir in ${langdir:-/usr/share/locale}; do
		mkdir -p "$subpkgdir"/${dir%/*}
		mv "$pkgdir"/"$dir" "$subpkgdir"/"$dir" || return 1
	done
}

lang() {
	default_lang
}

511
default_lang_subpkg() {
512 513 514 515
	if [ -z "$lang" ]; then
		error "lang is not set"
		return 1
	fi
516
	pkgdesc="$pkgname language pack for $lang"
517 518
	arch="noarch"
	install_if="$pkgname=$pkgver-r$pkgrel lang-$lang"
519 520 521 522 523
	local dir
	for dir in ${langdir:-/usr/share/locale}; do
		mkdir -p "$subpkgdir"/$dir
		mv "$pkgdir"/$dir/$lang* \
		"$subpkgdir"/$dir/ \
524
		|| return 1
525 526 527 528 529
	done
}

lang_subpkg() {
	default_lang_subpkg
530 531 532 533 534 535 536 537 538 539 540
}

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
}

541 542 543 544 545 546 547 548 549 550 551 552
# 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}'
}

553 554 555 556 557 558
get_maintainer() {
	if [ -z "$maintainer" ]; then
		maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
	fi
}

559
prepare_metafiles() {
560
	getpkgver || return 1
561 562
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
563
	local dir=${subpkgdir:-$pkgdir}
564
	local pkg="$name-$pkgver-r$pkgrel.apk"
565
	local pkginfo="$controldir"/.PKGINFO
566
	local sub
Natanael Copa's avatar
Natanael Copa committed
567 568 569
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
570
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
571 572
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
573
	local parch="$CARCH"
574 575 576 577 578

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

580
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
581
	if [ -n "$FAKEROOTKEY" ]; then
582
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
583
	fi
584 585
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
586
pkgname = $name
587
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
588 589 590 591 592
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
Natanael Copa's avatar
Natanael Copa committed
593
arch = $parch
Natanael Copa's avatar
Natanael Copa committed
594
origin = $pkgname
Natanael Copa's avatar
Natanael Copa committed
595
EOF
596 597
	local i deps
	deps="$depends"
598
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
599 600
		for i in $install $triggers; do
			local s=${i%=*}
601
			[ "$name" != "${s%.*}" ] && continue
602
			if head -n 1 "$startdir/$s" | grep '^#!/bin/sh' >/dev/null ; then
603 604 605 606 607
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
608 609
	fi
	
610 611 612 613 614 615 616 617 618 619
	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
620
	for i in $license; do
621
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
622 623
	done
	for i in $replaces; do
624
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
625
	done
626
	for i in $deps; do
627
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
628 629
	done
	for i in $conflicts; do
630
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
631 632
	done
	for i in $provides; do
633
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
634
	done
635 636 637 638 639 640
	for i in $triggers; do
		local f=${i%=*}
		local dirs=${i#*=}
		[ "${f%.trigger}" != "$name" ] && continue
		echo "triggers = ${dirs//:/ }" >> "$pkginfo"
	done
641 642 643
	if [ -n "$install_if" ]; then
		echo "install_if = $(echo $install_if)" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
644 645

	local metafiles=".PKGINFO"
646 647 648
	for i in $install $triggers; do
		local f=${i%=*}
		local n=${f%.*}
649
		if [ "$n" != "$name" ]; then
650 651
			continue
		fi
652
		script=${f#$name}
653
		msg "Adding $script"
654
		cp "$startdir/$f" "$controldir/$script" || return 1
655
		chmod +x "$controldir/$script"
656 657
		metafiles="$metafiles $script"
	done
658
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
659
}
660

661 662
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
663
	[ "$arch" = "noarch" ] && return 0
664
	options_has "!tracedeps" && return 0
665
	# lets tell all the .so files this package provides in .provides-so
666 667
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
668 669 670 671 672
	# 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
673
	scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
674 675 676
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
677 678
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
679
	done
Natanael Copa's avatar
Natanael Copa committed
680 681
}

682 683 684 685 686 687 688 689 690 691 692 693
# 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
}

694 695 696 697 698
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

699 700 701 702 703 704 705
# 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
706 707 708
	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\""
709 710 711 712
	fi
	return 0
}

713
prepare_package() {
714
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
715
	stripbin
716 717
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
718 719 720 721 722 723 724 725
}

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

726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
# 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
}

749 750 751
trace_apk_deps() {
	local name="$1"
	local dir="$2"
Natanael Copa's avatar
Natanael Copa committed
752
	local i= j= found= autodeps= deppkgs= missing= so_paths= self_provided=
753
	msg "Tracing dependencies for $name..."
754 755 756
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
757
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
758 759 760 761
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
762
	if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
763 764
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
765
		msg "  added libgcc (due to libpthread)"
766
	fi
767
	[ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
768
		found=
769
		# first check if its provide by same apkbuild
770
		for j in "$dir"/../.control.*/.provides-so; do
771
			grep -w "$i" "$j" >/dev/null || continue
772
			found=${j%/.provides-so}
773
			found=${found##*/.control.}
774
			break
775
		done
776 777 778 779 780 781
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
782
		fi
783 784 785
	done

	# find all packages that holds the so files
786 787 788 789 790
	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

791
	for found in $self_provided $deppkgs; do
792
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
793 794 795
			warning "You can remove '$found' from depends"
			continue
		fi
796 797
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
798
			msg "  added $found"
799
		fi
800
	done
801

802 803 804 805 806 807 808
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

809 810
create_apks() {
	local file
811
	getpkgver || return 1
Natanael Copa's avatar
Natanael Copa committed
812
	mkdir -p "$PKGDEST"
813
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
814 815 816
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
817
		local apk=$name-$ver.apk
818
		local datadir="$pkgbasedir"/$name
819 820

		trace_apk_deps "$name" "$dir" || return 1
821
		msg "Creating $apk..."
822
		(
823 824
		cd "$datadir"
		# data.tar.gz
825 826 827 828 829 830
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
831 832 833 834 835 836

		# 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
837
		cd "$dir"
838 839
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
840
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
841

842 843 844
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
845 846 847
	done
}

848
clean_abuildrepo() {
849
	local apk
850 851 852
	cd "$abuildrepo" || return 1
	
	# remove compat symlink
853
	for d in "$abuildrepo/$CARCH"; do
854 855
		[ -L "$d" ] && rm "$d"
	done
856

857 858
	# remove broken links from abuildrepo
	for apk in *.apk */*.apk; do
859 860 861 862
		if [ -L "$apk" ] && [ ! -f "$apk" ]; then
			rm -f "$apk"
		fi
	done
863
}
864

865
mklinks_abuildrepo() {
866 867
	local apk
	mkdir -p "$abuildrepo"/$CARCH
868
	cd "$abuildrepo" || return 1
869
	# create links for this package
870
	for apk in $(listpkg); do
871
		[ -f "$PKGDEST"/$apk ] || continue
872
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk
873
	done
874 875 876 877 878 879 880 881 882 883 884 885 886 887
}

update_abuildrepo() {
	local d apk
	if ! apk_up2date || [ -n "$force" ]; then
		sanitycheck && builddeps && clean && fetch && unpack \
			&& prepare && mkusers && rootpkg || return 1
	fi

	clean_abuildrepo
	mklinks_abuildrepo

	cd "$abuildrepo"
	local index=$CARCH/APKINDEX.tar.gz
888

889
	msg "Updating the cached abuild repository index..."
890 891
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
892 893
	if [ -f "$index" ]; then
		oldindex="--index $index"
894
	fi
895
	$APK index --quiet $oldindex --output "$index".unsigned \
896
		--description "$repo $(cd $startdir && git describe)" \
897 898
		--rewrite-arch $CARCH \
		$CARCH/*.apk || exit 1
899
	msg "Signing the index..."
900 901 902
	abuild-sign -q "$index".unsigned || exit 1
	mv "$index".unsigned "$index"
	chmod 644 "$index"
903 904
}

905
# predefined splitfunc doc
906
default_doc() {
907
	depends="$depends_doc"
908
	pkgdesc="$pkgdesc (documentation)"
909
	arch=${arch_doc:-"noarch"}
910

Natanael Copa's avatar
Natanael Copa committed
911
	local i
912
	for i in doc man info html sgml licenses gtk-doc; do
913 914 915
		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
916 917
		fi
	done
918 919 920
	
	rm -f "$subpkgdir/usr/share/info/dir"

921 922 923
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
924 925 926 927

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

Natanael Copa's avatar
Natanael Copa committed
928
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
929 930 931
	return 0
}

932 933 934 935
doc() {
	default_doc
}

936
# predefined splitfunc mod
937
default_mod() {
938
	depends="$kernel $depends_mod"
Natanael Copa's avatar
Natanael Copa committed
939
	for i in firmware modules; do
940 941 942
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
943
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
944 945 946 947
		fi
	done
}

948 949 950 951
mod() {
	default_mod
}

952
# predefined splitfunc dev
953
default_dev() {
954
	local i= j=
955
	depends="$pkgname $depends_dev"
956 957
	pkgdesc="$pkgdesc (development files)"

958 959 960 961
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
962
	cd "$pkgdir" || return 0
963 964
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
965
			usr/share/vala/vapi usr/share/gir-[0-9]*\
966
			$(find -name include -type d) 		\
967
			$(find usr/ -name '*.[acho]' 2>/dev/null); do
968 969
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
970
			mkdir -p "$d"
971
			mv "$pkgdir/$i" "$d"
972
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
973 974
		fi
	done
975 976 977 978 979 980 981
	# 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
982
	return 0
Natanael Copa's avatar
Natanael Copa committed
983 984
}

985 986 987 988
dev() {
	default_dev
}

989 990 991 992
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

993 994 995 996 997 998 999 1000
do_fakeroot() {
	if [ -n "$FAKEROOT" ]; then
		$FAKEROOT -- "$@"
	else
		"$@"
	fi
}

Natanael Copa's avatar
Natanael Copa committed
1001 1002
# build and package in fakeroot
rootpkg() {
1003 1004 1005 1006
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
1007
		do_build=package
1008
	fi
Natanael Copa's avatar
Natanael Copa committed
1009
	cd "$startdir"
1010
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
1011
	do_fakeroot "$abuild_path" $color_opt $do_build \
1012
		prepare_subpackages \
1013
		prepare_language_packs \
1014
		prepare_package \
1015
		create_apks
Natanael Copa's avatar
Natanael Copa committed
1016 1017 1018
}

srcpkg() {
1019
	getpkgver || return 1
1020
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
1021
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
1022 1023 1024 1025 1026
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
1027
	msg "Creating source package $p.src.tar.gz..."
1028
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
1029 1030
}

Natanael Copa's avatar
Natanael Copa committed
1031 1032
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
1033
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
1034 1035
}

1036
# check if package is up to date
1037
apk_up2date() {
1038
	getpkgver || return 1
1039
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
1040
	local i s
Natanael Copa's avatar
Natanael Copa committed
1041
	cd "$startdir"
1042 1043 1044 1045 1046
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

1047
	for i in $source APKBUILD; do
1048 1049 1050 1051 1052 1053
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
1054 1055 1056 1057 1058 1059 1060
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

1061
abuildindex_up2date() {
1062
	local i
1063
	getpkgver || return 1
1064 1065 1066 1067 1068 1069 1070
	local dir="$abuildrepo"/$CARCH
	local apk="${pkgname%:*}-$pkgver-r$pkgrel.apk"
	local idx="$dir"/APKINDEX.tar.gz
	local file="$dir"/$apk

	# check if index is missing
	[ -f "$idx" ] || return 1
1071
			
1072 1073 1074 1075 1076
	# if link or file is missing, then we need update abuildrepo index
	[ -f "$file" ] || return 1

	# if file exists and is newer than index, then we need update index
	[ "$file" -nt "$idx" ] && return 1
1077
		
1078 1079 1080 1081
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
1082
	check_arch || return 0
1083 1084 1085 1086 1087 1088 1089
	apk_up2date && abuildindex_up2date
}

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

1092 1093 1094 1095 1096 1097 1098
# 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
	(
1099 1100
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
1101 1102 1103 1104
		pkgname=
		subpackages=
		depends=
		makedepends=
1105
		. $i
1106
		dir=${i%/APKBUILD}
1107
		deps=
1108
		# filter out conflicts from deps and version info
1109 1110 1111 1112
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
1113
			deps="$deps ${j%%[<>=]*}"
1114
		done
1115
		for j in $pkgname $subpackages; do
1116
			echo "o ${j%%:*} $dir"
1117
			set -- $deps
1118 1119 1120 1121 1122 1123 1124
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
1125 1126
		done
	done
1127
	)
1128 1129
}

1130
deptrace() {
1131 1132 1133
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
1134
		deps="$deps ${i%%[<>=]*}"
1135
	done
1136
	[ -z "$deps" ] && return 0
1137 1138 1139
	( 	depparse_aports 
		if [ -