abuild.in 34.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_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
}
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 98
# check if apkbuild is basicly sane
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" ||\
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 120 121 122
	# 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
123
	for i in $install; do
124
		local n=${i%.*}
125
		local suff=${i##*.}
126 127 128 129 130
		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
131
			die "$i: install script does not match pkgname or any subpackage"
132
		fi
133
		[ -e "$startdir/$i" ] || die "install script $i is missing"
Natanael Copa's avatar
Natanael Copa committed
134 135
	done
	
136 137 138 139 140 141 142 143
	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
144

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

160 161
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
162
			source_has $i || die "$i exists in md5sums but is missing in source"
163 164
		done
	fi
165

166 167 168
	# 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
169 170

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
173 174 175 176
	return 0
}

md5check() {
177
	local dummy f endreturnval originalparams origin file
178 179 180
	if [ -z "$source" ]; then
		return 0
	fi
181
	if [ -z "$md5sums" ]; then
182
		die "Use 'abuild checksum' to generate/update the checksum(s)"
183
	fi
184 185 186
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
187
	fetch || return 1
188
	msg "Checking md5sums..."
Natanael Copa's avatar
Natanael Copa committed
189
	cd "$srcdir" || return 1
190 191 192 193 194 195 196 197
	IFS=$'\n'
	endreturnval=0
	originalparams=$@
	set -- $source
	for src in $md5sums; do
		origin=$1; shift
		echo "$src" | md5sum -c
		if [ $? -ne 0 ]; then
198
			endreturnval=1
199 200 201 202 203 204 205 206 207 208 209
			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
210 211
}

212 213
# verify upstream sources
sourcecheck() {
214 215 216 217 218 219 220 221 222 223
	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
224
	done
225
	return 0
226
}
227

Natanael Copa's avatar
Natanael Copa committed
228 229
uri_fetch() {
	local uri="$1"
230
	local d="${uri##*/}"	# $(basename $uri)
231 232 233 234
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

235 236 237 238 239
	# 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)}";;
240
	esac
241 242 243 244 245

	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
246
	
Natanael Copa's avatar
Natanael Copa committed
247
	mkdir -p "$SRCDEST"
248 249 250
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
251
	fi
Linux User's avatar
Linux User committed
252
	msg "Fetching $uri"
253 254
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
255 256
}

257 258
is_remote() {
	case "$1" in
259
		http://*|ftp://*|https://*|saveas-*://*)
260 261 262 263 264
			return 0;;
	esac
	return 1
}

265 266 267 268 269
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
270 271 272 273 274
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
275 276 277 278
	fi
	uri_fetch "$uri"
}

279
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
280 281
	local s
	mkdir -p "$srcdir"
282 283
	for s in $source; do
		if is_remote "$s"; then
284
			uri_fetch_mirror "$s" || return 1
285 286 287 288
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
289 290 291
	done
}

292 293 294 295
fetch() {
	default_fetch
}

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

342 343 344 345
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
346 347
# cleanup source and package dir
clean() {
348
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
349
	rm -rf "$srcdir"
350
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
351 352 353 354 355 356
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
357 358 359 360
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
361 362 363
	done
}

364 365 366 367 368 369 370 371 372 373
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
374 375
cleanpkg() {
	local i
376
	getpkgver || return 1
377
	msg "Cleaning built packages..."
378
	for i in $(listpkgnames); do
379 380
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
381
			"$abuildrepo"/$p.apk "$abuildrepo"/*/$p.apk
Natanael Copa's avatar
Natanael Copa committed
382
	done
383
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
384 385
}

386 387 388
# clean all packages except current
cleanoldpkg() {
	local i j
389
	getpkgver || return 1
390
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
391
	for i in $(listpkgnames); do
392 393 394 395 396
		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##*/}
397 398 399 400
		done
	done
	return 0
}
401 402 403 404 405

mkusers() {
	local i
	for i in $pkgusers; do
		if ! getent passwd $i >/dev/null; then
406
			local gopt=
407
			msg "Creating user $i"
408 409 410 411
			if getent group $i >/dev/null; then
				gopt="-G $i"
			fi
			$SUDO adduser -D -H $gopt $i || return 1
412 413 414 415 416 417 418 419 420 421 422
		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
423 424
runpart() {
	local part=$1
425
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
426 427 428 429
	$part || die "$part failed"
}

# override those in your build script
430 431 432 433 434 435 436 437
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
438 439 440 441
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
442
build() {
443
	:
Natanael Copa's avatar
Natanael Copa committed
444 445
}

446 447 448
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
Natanael Copa's avatar
Natanael Copa committed
449
	mkdir -p "$PKGDEST"
450 451 452
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

453 454 455 456 457 458 459 460 461
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
}

462
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
463 464
	local i
	cd "$startdir"
465
	for i in $subpackages; do
466 467
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
468
		msg "Running split function $func..."
469
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
470
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
471
	done
472 473 474 475 476 477 478
	# 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
Natanael Copa's avatar
Natanael Copa committed
479 480
}

481
default_lang_subpkg() {
482 483 484 485
	if [ -z "$lang" ]; then
		error "lang is not set"
		return 1
	fi
486
	pkgdesc="$pkgname language pack for $lang"
487 488
	arch="noarch"
	install_if="$pkgname=$pkgver-r$pkgrel lang-$lang"
489 490 491 492 493
	local dir
	for dir in ${langdir:-/usr/share/locale}; do
		mkdir -p "$subpkgdir"/$dir
		mv "$pkgdir"/$dir/$lang* \
		"$subpkgdir"/$dir/ \
494
		|| return 1
495 496 497 498 499
	done
}

lang_subpkg() {
	default_lang_subpkg
500 501 502 503 504 505 506 507 508 509 510
}

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
}

511 512 513 514 515 516 517 518 519 520 521 522
# 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}'
}

523 524 525 526 527 528
get_maintainer() {
	if [ -z "$maintainer" ]; then
		maintainer=$(awk -F': ' '/\# *Maintainer/ {print $2}' "$APKBUILD")
	fi
}

529
prepare_metafiles() {
530
	getpkgver || return 1
531 532
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
533
	local dir=${subpkgdir:-$pkgdir}
534
	local pkg="$name-$pkgver-r$pkgrel.apk"
535
	local pkginfo="$controldir"/.PKGINFO
536
	local sub
Natanael Copa's avatar
Natanael Copa committed
537 538 539
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
540
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
541 542
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
543
	local parch="$CARCH"
544 545 546 547 548

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

550
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
551
	if [ -n "$FAKEROOTKEY" ]; then
552
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
553
	fi
554 555
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
556
pkgname = $name
557
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
558 559 560 561 562
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
Natanael Copa's avatar
Natanael Copa committed
563
arch = $parch
564
origin = $pkgname
Natanael Copa's avatar
Natanael Copa committed
565
EOF
566 567
	local i deps
	deps="$depends"
568
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
569 570
		for i in $install $triggers; do
			local s=${i%=*}
571
			[ "$name" != "${s%.*}" ] && continue
572
			if head -n 1 "$startdir/$s" | grep '^#!/bin/sh' >/dev/null ; then
573 574 575 576 577
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
578 579
	fi
	
580 581 582 583 584 585 586 587 588 589
	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
590
	for i in $license; do
591
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
592 593
	done
	for i in $replaces; do
594
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
595
	done
596
	for i in $deps; do
597
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
598 599
	done
	for i in $conflicts; do
600
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
601 602
	done
	for i in $provides; do
603
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
604
	done
605 606 607 608 609 610
	for i in $triggers; do
		local f=${i%=*}
		local dirs=${i#*=}
		[ "${f%.trigger}" != "$name" ] && continue
		echo "triggers = ${dirs//:/ }" >> "$pkginfo"
	done
611 612 613
	if [ -n "$install_if" ]; then
		echo "install_if = $(echo $install_if)" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
614 615

	local metafiles=".PKGINFO"
616 617 618
	for i in $install $triggers; do
		local f=${i%=*}
		local n=${f%.*}
619
		if [ "$n" != "$name" ]; then
620 621
			continue
		fi
622
		script=${f#$name}
623
		msg "Adding $script"
624
		cp "$startdir/$f" "$controldir/$script" || return 1
625
		chmod +x "$controldir/$script"
626 627
		metafiles="$metafiles $script"
	done
628
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
629
}
630

631 632
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
633
	[ "$arch" = "noarch" ] && return 0
634
	options_has "!tracedeps" && return 0
635
	# lets tell all the .so files this package provides in .provides-so
636 637
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
638 639 640 641 642
	# 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
643
	scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
644 645 646
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
647 648
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
649
	done
Natanael Copa's avatar
Natanael Copa committed
650 651
}

652 653 654 655 656 657 658 659 660 661 662 663
# 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
}

664 665 666 667 668
# returns true if this is the -dev package
is_dev_pkg() {
	test "${subpkgname%-dev}" != "$subpkgname"
}

669 670 671 672 673 674 675
# 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
676 677 678
	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\""
679 680 681 682
	fi
	return 0
}

683
prepare_package() {
684
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
685
	stripbin
686 687
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
688 689 690 691 692 693 694 695
}

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

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
# 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
}

719 720 721
trace_apk_deps() {
	local name="$1"
	local dir="$2"
722
	local i= j= found= autodeps= deppkgs= missing= so_paths= self_provided=
723
	msg "Tracing dependencies for $name..."
724 725 726
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
727
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
728 729 730 731
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
732
	if [ -f "$dir"/.needs-so ] && grep -q -w '^libpthread.so.*' "$dir"/.needs-so \
733 734
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
735
		msg "  added libgcc (due to libpthread)"
736
	fi
737
	[ -f "$dir"/.needs-so ] && for i in $(cat "$dir"/.needs-so); do
738
		found=
739
		# first check if its provide by same apkbuild
740
		for j in "$dir"/../.control.*/.provides-so; do
741
			grep -w "$i" "$j" >/dev/null || continue
742
			found=${j%/.provides-so}
743
			found=${found##*/.control.}
744
			break
745
		done
746 747 748 749 750 751
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
752
		fi
753 754 755
	done

	# find all packages that holds the so files
756 757 758 759 760
	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

761
	for found in $self_provided $deppkgs; do
762
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
763 764 765
			warning "You can remove '$found' from depends"
			continue
		fi
766 767
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
768
			msg "  added $found"
769
		fi
770
	done
771

772 773 774 775 776 777 778
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

779 780
create_apks() {
	local file
781
	getpkgver || return 1
Natanael Copa's avatar
Natanael Copa committed
782
	mkdir -p "$PKGDEST"
783
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
784 785 786
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
787
		local apk=$name-$ver.apk
788
		local datadir="$pkgbasedir"/$name
789 790

		trace_apk_deps "$name" "$dir" || return 1
791
		msg "Creating $apk..."
792
		(
793 794
		cd "$datadir"
		# data.tar.gz
795 796 797 798 799 800
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
801 802 803 804 805 806

		# 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
807
		cd "$dir"
808 809
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
810
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
811

812 813 814
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
815 816 817
	done
}

818
clean_abuildrepo() {
819
	local apk
820 821 822
	cd "$abuildrepo" || return 1
	
	# remove compat symlink
823
	for d in "$abuildrepo/$CARCH"; do
824 825
		[ -L "$d" ] && rm "$d"
	done
826

827 828
	# remove broken links from abuildrepo
	for apk in *.apk */*.apk; do
829 830 831 832
		if [ -L "$apk" ] && [ ! -f "$apk" ]; then
			rm -f "$apk"
		fi
	done
833
}
834

835
mklinks_abuildrepo() {
836 837
	local apk
	mkdir -p "$abuildrepo"/$CARCH
838
	cd "$abuildrepo" || return 1
839
	# create links for this package
840
	for apk in $(listpkg); do
841
		[ -f "$PKGDEST"/$apk ] || continue
842
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$CARCH/$apk
843
	done
844 845 846 847 848 849 850 851 852 853 854 855 856 857
}

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
858

859
	msg "Updating the cached abuild repository index..."
860 861
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
862 863
	if [ -f "$index" ]; then
		oldindex="--index $index"
864
	fi
865
	$APK index --quiet $oldindex --output "$index".unsigned \
866
		--description "$repo $(cd $startdir && git describe)" \
867 868
		--rewrite-arch $CARCH \
		$CARCH/*.apk || exit 1
869
	msg "Signing the index..."
870 871 872
	abuild-sign -q "$index".unsigned || exit 1
	mv "$index".unsigned "$index"
	chmod 644 "$index"
873 874
}

875
# predefined splitfunc doc
876
default_doc() {
877
	depends="$depends_doc"
878
	pkgdesc="$pkgdesc (documentation)"
879
	arch=${arch_doc:-"noarch"}
880

Natanael Copa's avatar
Natanael Copa committed
881
	local i
882
	for i in doc man info html sgml licenses gtk-doc; do
883 884 885
		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
886 887
		fi
	done
888 889 890
	
	rm -f "$subpkgdir/usr/share/info/dir"

891 892 893
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
894 895 896 897

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

Natanael Copa's avatar
Natanael Copa committed
898
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
899 900 901
	return 0
}

902 903 904 905
doc() {
	default_doc
}

906
# predefined splitfunc mod
907
default_mod() {
908
	depends="$kernel $depends_mod"
Natanael Copa's avatar
Natanael Copa committed
909
	for i in firmware modules; do
910 911 912
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
913
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
914 915 916 917
		fi
	done
}

918 919 920 921
mod() {
	default_mod
}

922
# predefined splitfunc dev
923
default_dev() {
924
	local i= j=
925
	depends="$pkgname $depends_dev"
926 927
	pkgdesc="$pkgdesc (development files)"

928 929 930 931
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
932
	cd "$pkgdir" || return 0
933 934
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
935
			usr/share/vala/vapi usr/share/gir-[0-9]*\
936
			$(find -name include -type d) 		\
937
			$(find usr/ -name '*.[acho]' 2>/dev/null); do
938 939
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
940
			mkdir -p "$d"
941
			mv "$pkgdir/$i" "$d"
942
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
943 944
		fi
	done
945 946 947 948 949 950 951
	# 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
952
	return 0
Natanael Copa's avatar
Natanael Copa committed
953 954
}

955 956 957 958
dev() {
	default_dev
}

959 960 961 962
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

963 964 965 966 967 968 969 970
do_fakeroot() {
	if [ -n "$FAKEROOT" ]; then
		$FAKEROOT -- "$@"
	else
		"$@"
	fi
}

Natanael Copa's avatar
Natanael Copa committed
971 972
# build and package in fakeroot
rootpkg() {
973 974 975 976
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
977
		do_build=package
978
	fi
Natanael Copa's avatar
Natanael Copa committed
979
	cd "$startdir"
980
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
981
	do_fakeroot "$abuild_path" $color_opt $do_build \
982
		prepare_subpackages \
983
		prepare_language_packs \
984
		prepare_package \
985
		create_apks
Natanael Copa's avatar
Natanael Copa committed
986 987 988
}

srcpkg() {
989
	getpkgver || return 1
990
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
991
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
992 993 994 995 996
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
997
	msg "Creating source package $p.src.tar.gz..."
998
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
999 1000
}

Natanael Copa's avatar
Natanael Copa committed
1001 1002
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
1003
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
1004 1005
}

1006
# check if package is up to date
1007
apk_up2date() {
1008
	getpkgver || return 1
1009
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
1010
	local i s
Natanael Copa's avatar
Natanael Copa committed
1011
	cd "$startdir"
1012 1013 1014 1015 1016
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

1017
	for i in $source APKBUILD; do
1018 1019 1020 1021 1022 1023
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
1024 1025 1026 1027 1028 1029 1030
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

1031
abuildindex_up2date() {
1032
	local i
1033
	getpkgver || return 1
1034 1035 1036 1037 1038 1039 1040
	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
1041
			
1042 1043 1044 1045 1046
	# 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
1047
		
1048 1049 1050 1051
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
1052
	check_arch || return 0
1053 1054 1055 1056 1057 1058 1059
	apk_up2date && abuildindex_up2date
}

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

1062 1063 1064 1065 1066 1067 1068
# 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
	(
1069 1070
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
1071 1072 1073 1074
		pkgname=
		subpackages=
		depends=
		makedepends=
1075
		. $i
1076
		dir=${i%/APKBUILD}
1077
		deps=
1078
		# filter out conflicts from deps and version info
1079 1080 1081 1082
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
1083
			deps="$deps ${j%%[<>=]*}"
1084
		done
1085
		for j in $pkgname $subpackages; do
1086
			echo "o ${j%%:*} $dir"
1087
			set -- $deps
1088 1089 1090 1091 1092 1093 1094
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
1095 1096
		done
	done
1097
	)
1098 1099
}

1100
deptrace() {
1101 1102 1103
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
1104
		deps="$deps ${i%%[<>=]*}"
1105
	done
1106
	[ -z "$deps" ] && return 0
1107 1108 1109
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
1110
			$APK info -q | sort |  sed 's/^/i /'
1111
		fi
1112
	) | awk -v pkgs="$deps" '
1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137

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