abuild.in 24 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
apkcache=@apkcache@
Natanael Copa's avatar
Natanael Copa committed
14
datadir=@datadir@
Natanael Copa's avatar
Natanael Copa committed
15

16
program=${0##*/}
Natanael Copa's avatar
Natanael Copa committed
17 18
startdir="$PWD"
srcdir=${srcdir:-"$startdir/src"}
19
pkgbasedir=${pkgbasedir:-"$startdir/pkg"}
Natanael Copa's avatar
Natanael Copa committed
20
pkgrel=0
Natanael Copa's avatar
Natanael Copa committed
21 22
repo=${startdir%/*}
repo=${repo##*/}
Natanael Copa's avatar
Natanael Copa committed
23 24 25 26

# defaults
SRCDEST=${SRCDEST:-$startdir}
PKGDEST=${PKGDEST:-$startdir}
27
BUILD_BASE="build-base"
28 29
SUDO=${SUDO:-"sudo"}

Natanael Copa's avatar
Natanael Copa committed
30
# read config
Natanael Copa's avatar
Natanael Copa committed
31
ABUILD_CONF=${ABUILD_CONF:-"$sysconfdir/abuild.conf"}
Natanael Copa's avatar
Natanael Copa committed
32 33
[ -f "$ABUILD_CONF" ] && . "$ABUILD_CONF"

34

Natanael Copa's avatar
Natanael Copa committed
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
#colors
if [ -n "$USE_COLORS" ]; then
	NORMAL="\033[1;0m"
	STRONG="\033[1;1m"
	RED="\033[1;31m"
	GREEN="\033[1;32m"
	YELLOW="\033[1;33m"
	BLUE="\033[1;34m"
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
}
67 68

	
Linux User's avatar
Linux User committed
69 70
set_xterm_title() {
	if [ "$TERM" = xterm ]; then
71
		 printf "\033]0;$1\007" >&2
Linux User's avatar
Linux User committed
72 73 74 75 76
	fi
} 

cleanup() {
	set_xterm_title ""
77
	if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
78
		$SUDO apk del $uninstall_after
79
	fi
Linux User's avatar
Linux User committed
80 81
}

Natanael Copa's avatar
Natanael Copa committed
82
die() {
Linux User's avatar
Linux User committed
83
	error "$@"
Linux User's avatar
Linux User committed
84
	cleanup
Natanael Copa's avatar
Natanael Copa committed
85 86 87
	exit 1
}

88 89
# check if apkbuild is basicly sane
sanitycheck() {
Natanael Copa's avatar
Natanael Copa committed
90
	local i
91
	msg "Checking sanity of $APKBUILD..."
92 93 94
	[ -z "$pkgname" ] && die "Missing pkgname in APKBUILD"
	[ -z "${pkgname##* *}" ] && die "pkgname contains spaces"
	[ -z "$pkgver" ] && die "Missing pkgver in APKBUILD"
95 96 97 98
	if [ "$pkgver" != "volatile" ]; then
		apk version --check -q "$pkgver" ||\
			die "$pkgver is not a valid version"
	fi
99 100 101 102 103
	[ -z "$pkgrel" ] && warning "Missing pkgrel in APKBUILD. Using pkgrel=0"
	[ -z "$pkgdesc" ] && die "Missing pkgdesc in APKBUILD"
	[ -z "$url" ] && die "Missing url in APKBUILD"
	[ -z "$license" ] && die "Missing license in APKBULID"

Natanael Copa's avatar
Natanael Copa committed
104 105 106 107 108 109 110
	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"

111 112
	if [ -n "$source" ]; then
		for i in $source; do
Natanael Copa's avatar
Natanael Copa committed
113 114 115 116
			if install_has "$i"; then
				warning "You should not have \$install in source"
				continue
			fi
117
			md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
118 119 120
			case "$i" in
				https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
			esac
121 122
		done
	fi
Natanael Copa's avatar
Natanael Copa committed
123

124 125
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
126
			source_has $i || die "$i exists in md5sums but is missing in source"
127 128
		done
	fi
129

130 131 132
	# 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
133 134

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
137 138 139 140
	return 0
}

md5check() {
Natanael Copa's avatar
Natanael Copa committed
141
	local dummy f
142 143 144
	if [ -z "$source" ]; then
		return 0
	fi
145
	if [ -z "$md5sums" ]; then
146
		die "Use 'abuild checksum' to generate/update the checksum(s)"
147
	fi
148 149 150
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
151
	fetch || return 1
152
	msg "Checking md5sums..."
153
	cd "$srcdir" && echo "$md5sums" | md5sum -c 
Natanael Copa's avatar
Natanael Copa committed
154 155 156 157 158
}

uri_fetch() {
	local uri="$1"
	local d="${s##*/}"	# $(basename $s)
159 160 161 162
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

163 164 165 166 167 168
	
	# we need GNU wget for this
	case "$uri" in
		https://*) opts="--no-check-certificate";;
	esac
	
Natanael Copa's avatar
Natanael Copa committed
169
	mkdir -p "$SRCDEST"
170 171 172
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
173
	fi
Linux User's avatar
Linux User committed
174
	msg "Fetching $uri"
175 176
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
177 178
}

179 180
is_remote() {
	case "$1" in
181
		http://*|ftp://*|https://*)
182 183 184 185 186
			return 0;;
	esac
	return 1
}

Natanael Copa's avatar
Natanael Copa committed
187 188 189
fetch() {
	local s
	mkdir -p "$srcdir"
190 191 192 193 194 195 196
	for s in $source; do
		if is_remote "$s"; then
			uri_fetch "$s" || return 1
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
197 198 199 200 201 202
	done
}

# unpack the sources
unpack() {
	local u
203
	md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
204 205 206 207
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
208
			*.tar.gz|*.tgz)
209
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
210 211
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
212
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
213
				tar -C "$srcdir" -jxf "$s" || return 1;;
214 215 216 217
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
Natanael Copa's avatar
Natanael Copa committed
218 219 220
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
221 222 223 224 225 226
		esac
	done
}

# cleanup source and package dir
clean() {
227
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
228
	rm -rf "$srcdir"
229
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
230 231 232 233 234 235
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
236 237 238 239
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
240 241 242 243 244
	done
}

cleanpkg() {
	local i
245
	msg "Cleaning built packages..."
246
	for i in $pkgname $subpackages; do
247 248
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
249
			"$apkcache"/$p.apk
Natanael Copa's avatar
Natanael Copa committed
250
	done
251
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
252 253
}

254 255 256 257 258 259 260 261 262 263 264 265
# clean all packages except current
cleanoldpkg() {
	local i j
	msg "Cleaning all packages except $pkgver-r$pkgrel..."
	for i in $pkgname $subpackages; do
		for j in "$PKGDEST"/${i%:*}-[0-9]*.apk; do
			[ "$j" != "$PKGDEST/${i%:*}-$pkgver-r$pkgrel.apk" ] \
				&& rm -f "$j"
		done
	done
	return 0
}
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283

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
284 285
runpart() {
	local part=$1
286
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
287 288 289 290
	$part || die "$part failed"
}

# override those in your build script
Natanael Copa's avatar
Natanael Copa committed
291 292 293 294
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
295
build() {
296
	:
Natanael Copa's avatar
Natanael Copa committed
297 298
}

299 300 301 302 303 304 305 306 307
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
}

308
prepare_subpackages() {
309
	if [ -z "$subpackages" ]; then
310 311
		return 0
	fi
Natanael Copa's avatar
Natanael Copa committed
312 313
	local i
	cd "$startdir"
314
	for i in $subpackages; do
315 316
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
317
		msg "Running split function $func..."
318
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
319
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
320 321 322
	done
}

323
prepare_metafiles() {
324 325
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
326
	local dir=${subpkgdir:-$pkgdir}
327
	local pkg="$name-$pkgver-r$pkgrel.apk"
328
	local pkginfo="$controldir"/.PKGINFO
329
	local sub
Natanael Copa's avatar
Natanael Copa committed
330 331 332
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
333
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
334 335 336
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')

337
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
338
	if [ -n "$FAKEROOTKEY" ]; then
339
		echo "# using $(fakeroot -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
340
	fi
341 342
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
343
pkgname = $name
344
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
345 346 347 348 349 350
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
EOF
351 352
	local i deps
	deps="$depends"
353
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
Natanael Copa's avatar
Natanael Copa committed
354 355
		for i in $install ${triggers%%:*}; do
			if head -n 1 "$startdir/$i" | grep '^#!/bin/sh' >/dev/null ; then
356 357 358 359 360
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
361 362
	fi
	
Natanael Copa's avatar
Natanael Copa committed
363
	for i in $license; do
364
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
365 366
	done
	for i in $replaces; do
367
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
368
	done
369
	for i in $deps; do
370
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
371 372
	done
	for i in $conflicts; do
373
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
374 375
	done
	for i in $provides; do
376
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
377 378
	done
	for i in $backup; do
379
		echo "backup = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
380
	done
Natanael Copa's avatar
Natanael Copa committed
381 382 383
	if [ -n "$triggers" ]; then
		echo "triggers = ${triggers#*:}" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
384 385

	local metafiles=".PKGINFO"
Natanael Copa's avatar
Natanael Copa committed
386
	for i in $install ${triggers%%:*}; do
387 388
		script=${i#$name}
		case "$script" in
Natanael Copa's avatar
Natanael Copa committed
389
			.pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
390 391
				msg "Adding $script"
				;;
Natanael Copa's avatar
Natanael Copa committed
392
			*) 	error "$script: Invalid install/trigger script"
393 394 395
				return 1
				;;
		esac
Natanael Copa's avatar
Natanael Copa committed
396
		cp "$startdir/$i" "$controldir/$script" || return 1
397
		chmod +x "$controldir/$script"
398 399
		metafiles="$metafiles $script"
	done
400
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
401
}
402

403 404 405
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
	options_has "!tracedeps" && return 0
406 407
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
408
	scanelf -Rn "$dir" | awk -F "\ " '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
409 410 411
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
412 413
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
414
	done
Natanael Copa's avatar
Natanael Copa committed
415 416
}

417
prepare_package() {
418
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
419
	stripbin
420
	prepare_metafiles
421
	prepare_tracedeps
422 423 424 425 426 427 428 429
}

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

430 431 432
trace_apk_deps() {
	local name="$1"
	local dir="$2"
433 434
	local i j found autodeps=
	msg "Tracing dependencies for $name..."
435
	for i in $(cat "$dir"/.needs-so 2>/dev/null); do
436
		found=
437
		# first check if its provide by same apkbuild
438
		for j in "$dir"/../.control.*/.provides-so; do
439
			grep -w "$i" "$j" >/dev/null || continue
440
			found=${j%/.provides-so}
441
			found=${found##*/.control.}
442
			break
443 444 445 446 447 448 449 450 451
		done
		# check apk db if not provided by a subpackage
		if [ -z "$found" ]; then
			found=$(apk info -q -W /lib/$i /usr/lib/$i)
		fi
		if [ -z "$found" ]; then
			error "Could not find dependency for $i"
			return 1
		fi
452
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
453 454 455
			warning "You can remove '$found' from depends"
			continue
		fi
456 457
		list_has "$found" $autodeps || autodeps="$autodeps $found"
		msg "Added '$found' as dependency as it has $i"
458 459 460 461 462 463
	done
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
464 465 466 467 468 469
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasdir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
		msg "Added pkgconfig as dependency"
		echo "depend = pkgconfig" >> "$dir"/.PKGINFO
	fi
470 471
}

472 473
create_apks() {
	local file
474
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
475 476 477
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
478
		local apk=$name-$ver.apk
479
		local datadir="$pkgbasedir"/$name
480 481

		trace_apk_deps "$name" "$dir" || return 1
482
		msg "Creating $apk..."
483
		(
484 485
		cd "$datadir"
		# data.tar.gz
486 487 488 489 490 491
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
492 493 494 495 496 497

		# 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
498
		cd "$dir"
499 500
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
501
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
502

503 504 505
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
506 507 508
	done
}

509
apkcache() {
510
	if ! apk_up2date || [ -n "$force" ]; then
511
		sanitycheck && builddeps && clean && fetch && unpack \
Natanael Copa's avatar
Natanael Copa committed
512
			&& prepare && mkusers && rootpkg || return 1
513
	fi
514

515
	local apk
516
	mkdir -p "$apkcache" || return 1
517 518 519 520 521 522 523 524 525 526
	cd "$apkcache"

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

	# create links for this package
527
	for apk in $(listpkg); do
528
		ln -sf "$PKGDEST"/$apk "$apkcache"/$apk
529 530
	done

531
	msg "Updating the cached abuild repository index..."
532 533 534 535 536
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
	if [ -f APKINDEX.tar.gz ]; then
		oldindex="--index APKINDEX.tar.gz"
	fi
537 538 539
	apk index $oldindex --output APKINDEX.tar.gz.unsigned \
		--description "$repo $(cd $startdir && git describe)" \
		*.apk || exit 1
540 541 542
	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
543
	chmod 644 APKINDEX.tar.gz
544 545
}

546
# predefined splitfunc doc
547
default_doc() {
548
	depends="$depends_doc"
549
	install="$install_doc"
Natanael Copa's avatar
Natanael Copa committed
550
	triggers="$triggers_doc"
Natanael Copa's avatar
Natanael Copa committed
551
	local i
552
	for i in doc man info html sgml licenses gtk-doc; do
553 554 555
		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
556 557
		fi
	done
558 559 560
	
	rm -f "$subpkgdir/usr/share/info/dir"

561 562 563
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
564 565 566 567

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

Natanael Copa's avatar
Natanael Copa committed
568
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
569 570 571
	return 0
}

572 573 574 575
doc() {
	default_doc
}

576
# predefined splitfunc mod
577
default_mod() {
578
	depends="$kernel $depends_mod"
579
	install="$install_mod"
Natanael Copa's avatar
Natanael Copa committed
580
	for i in firmware modules; do
581 582 583
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
584
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
585 586 587 588
		fi
	done
}

589 590 591 592
mod() {
	default_mod
}

593
# predefined splitfunc dev
594
default_dev() {
595
	local i
596
	depends="$pkgname $depends_dev"
597
	install="$install_dev"
Natanael Copa's avatar
Natanael Copa committed
598
	triggers="$triggers_dev"
599 600 601 602
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
603
	cd "$pkgdir" || return 0
604 605 606 607 608
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
			$(find -name include -type d) 		\
			$(find usr/ -name '*.[acho]' -o -name '*.la' \
			2>/dev/null); do
609 610
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
611
			mkdir -p "$d"
612
			mv "$pkgdir/$i" "$d"
613
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
614 615
		fi
	done
Natanael Copa's avatar
Natanael Copa committed
616
	return 0
Natanael Copa's avatar
Natanael Copa committed
617 618
}

619 620 621 622
dev() {
	default_dev
}

623 624 625 626
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

Natanael Copa's avatar
Natanael Copa committed
627 628
# build and package in fakeroot
rootpkg() {
629 630 631 632 633
	local do_build=build
	cd "$startdir"
	if is_function package; then
		msg "Building without fakeroot"
		build || return 1
634
		do_build=package
635
	fi
Natanael Copa's avatar
Natanael Copa committed
636
	cd "$startdir"
637
	msg "Entering fakeroot..."
638
	fakeroot $0 $do_build prepare_subpackages prepare_package create_apks
Natanael Copa's avatar
Natanael Copa committed
639 640 641
}

srcpkg() {
642
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
643
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
644 645 646 647 648
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
649
	msg "Creating source package $p.src.tar.gz..."
650
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
651 652
}

653
# check if package is up to date
654
apk_up2date() {
655
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
656
	local i s
Natanael Copa's avatar
Natanael Copa committed
657
	cd "$startdir"
658 659 660 661 662
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

663
	for i in $source APKBUILD; do
664 665 666 667 668 669
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
670 671 672 673 674 675 676
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

677 678 679 680
cache_up2date() {
	local i apk
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
681
		[ "$apkcache"/APKINDEX.tar.gz -nt "$apkcache"/$apk ] || return 1
682 683 684 685 686 687 688 689
	done
	return 0
}

up2date() {
	apk_up2date && cache_up2date
}

690 691 692 693 694 695 696 697 698 699 700 701 702
# 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
	(
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/../..)
	for i in $aportsdir/*/*/APKBUILD; do
		pkgname=
		subpackages=
		depends=
		makedepends=
703
		. $i
704
		dir=${i%/APKBUILD}
705
		deps=
706
		# filter out conflicts from deps and version info
707 708 709 710
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
711
			deps="$deps ${j%[<>=]*}"
712
		done
713
		for j in $pkgname $subpackages; do
714
			echo "o ${j%%:*} $dir"
715
			set -- $deps
716 717 718 719 720 721 722
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
723 724
		done
	done
725
	)
726 727
}

728
deptrace() {
729 730 731 732 733
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
		deps="$deps ${i%[<>=]*}"
	done
734
	[ -z "$deps" ] && return 0
735 736 737
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
Natanael Copa's avatar
Natanael Copa committed
738
			apk info -q | sort |  sed 's/^/i /'
739
		fi
740
	) | awk -v pkgs="$deps" '
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765

	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() {
766 767
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
	local filtered_deps=
768
	[ -n "$nodeps" ] && return 0
769
	msg "Analyzing dependencies..."
770 771 772 773
	deps="$BUILD_BASE $makedepends"

	# add depends unless it is a subpackage
	for i in $depends; do
774
		subpackages_has ${i%[<>=]*} || deps="$deps $i"
775 776
	done

777
	installed_deps=$(apk info -e $deps)
778
	# find which deps are missing
779
	for i in $deps; do
780
		if [ "${i#\!}" != "$i" ]; then
781
			list_has ${i#\!} $installed_deps \
782
				&& die "Conflicting package ${i#\!} is installed."
783
		elif ! deplist_has $i $installed_deps; then
784 785 786 787 788 789 790
			if [ -z "$install_deps" ] && [ -z "$recursive" ]; then
				die "Missing dependency $i. Use -r to autoinstall or -R to build"
			fi
			missing="$missing $i"
		fi
	done

791 792
	[ -z "$missing" ] && [ -z "$install_deps" ] && [ -z "$recursive" ] \
		&& return 0
793

794
	uninstall_after=".makedepends-$pkgname $uninstall_after"
795
	if [ -n "$install_deps" ] && [ -z "$recursive" ]; then
796
		$SUDO apk add --repository "$apkcache" \
797 798
			--virtual .makedepends-$pkgname $deps \
			&& return 0
799
	fi
800 801
	
	[ -z "$recursive" ] && return 1
802 803 804

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
805
		local m=$(apk search --repository "$apkcache" ${i%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
806
		if [ -z "$m" ]; then
807 808 809
			missing="$missing $i"
		fi
	done
810 811
	
	for i in $(deptrace $missing); do
812 813 814 815
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
		msg "Entering $dir"
816
		cd "$dir" && $0 -k -r apkcache || return 1
817
	done
818
	$SUDO apk add -u --repository "$apkcache" \
819
		--virtual .makedepends-$pkgname $deps
820 821
}

822
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
823 824
checksum() {
	local s files
825
	[ -z "$source" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
826
	fetch
827
	msg "Updating the md5sums in APKBUILD..."
Natanael Copa's avatar
Natanael Copa committed
828 829 830 831
	for s in $source; do
		files="$files ${s##*/}"
	done
	md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
832 833
	sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
	echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
834 835
}

836
stripbin() {
837
	local bin
838
	options_has "!strip" && return 0
839 840
	cd "${subpkgdir:-$pkgdir}" || return 1
	msg "Stripping binaries"
841
	find . -type f 2>/dev/null | while read bin; do
842 843 844 845 846 847 848 849 850 851 852
		local opt=
		case "$(file -biz "$bin")" in
			*/x-sharedlib*|*/x-archive*)
				strip --strip-debug "$bin";;
			*/x-executable*)
				strip "$bin";;
		esac
	done
	return 0
}

853 854 855 856 857 858 859 860
# simply list target apks
listpkg() {
	local i 
	for i in $pkgname $subpackages; do
		echo "${i%:*}-$pkgver-r$pkgrel.apk"
	done
}

Natanael Copa's avatar
Natanael Copa committed
861 862 863 864 865 866 867 868
source_has() {
	local i
	for i in $source; do
		[ "$1" = "${i##*/}" ] && return 0
	done
	return 1
}

Natanael Copa's avatar
Natanael Copa committed
869 870 871 872 873 874 875 876
subpackages_has() {
	local i
	for i in $subpackages; do
		[ "$1" = "${i%:*}" ] && return 0
	done
	return 1
}

877 878 879 880 881 882 883 884 885 886 887
list_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

888 889 890 891 892 893 894 895 896 897 898 899 900
# same as list_has but we filter version info
deplist_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		i=${i%[<>=]*}
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

901 902 903 904
options_has() {
	list_has "$1" $options
}

905
depends_has() {
906
	deplist_has "$1" $depends
907 908
}

909
makedepends_has() {
910
	deplist_has "$1" $makedepends
911 912
}

Natanael Copa's avatar
Natanael Copa committed
913 914 915 916
md5sums_has() {
	list_has "$1" $md5sums
}

Natanael Copa's avatar
Natanael Copa committed
917 918 919 920
install_has() {
	list_has "$1" $install
}

Natanael Copa's avatar
Natanael Copa committed
921 922 923 924 925 926 927 928 929 930 931 932 933 934
# install package after build
post_add() {
	local pkgf="$PKGDEST/$1-$pkgver-r$pkgrel.apk"
	local deps i
	if ! subpackages_has $1 && [ "$1" != "$pkgname" ]; then
		die "$1 is not built by this APKBUILD"
	fi
	# recursively install dependencies that are provided by this APKBUILD
	deps=$(apk index "$pkgf" 2>/dev/null | awk -F: '$1=="D" { print $2 }')
	for i in $deps; do
		if subpackages_has $i || [ "$i" = "$pkgname" ]; then
			post_add $i || return 1
		fi
	done
935
	$SUDO apk add -u "$pkgf" || die "Failed to install $1"
Natanael Copa's avatar
Natanael Copa committed
936 937
}

938
installdeps() {
939
	local deps i
940
	sudo apk add --repository "$apkcache" --virtual .makedepends-$pkgname \
941
		$makedepends
942 943 944
}

uninstalldeps (){
945
	sudo apk del .makedepends-$pkgname
946
}
947 948 949 950 951 952 953 954 955

all() {
	if up2date && [ -z "$force" ]; then
		msg "Package is up to date"
	else
		apkcache
	fi
}

Natanael Copa's avatar
Natanael Copa committed
956
usage() {
957 958
	echo "$program $abuild_ver"
	echo "usage: $program [options] [-i PKG] [-P REPODEST] [-p PKGDEST]"
959
	echo "              [-s SRCDEST] [cmd] ..."
960
	echo "       $program [-c] -n PKGNAME[-PKGVER]"
Natanael Copa's avatar
Natanael Copa committed
961
	echo "Options:"
962
	echo " -d  Disable dependency checking"
Natanael Copa's avatar
Natanael Copa committed
963
	echo " -f  Force specified cmd, even if they are already done"
964 965
	echo " -h  Show this help"
	echo " -i  Install PKG after successul build"
966
	echo " -k  Keep built packages, even if APKBUILD or sources are newer"
967
	echo " -p  Set package destination directory"
968
	echo " -P  Set PKGDEST to REPODEST/<repo>, where repo is the parents dir name" 
Natanael Copa's avatar
Natanael Copa committed
969
	echo " -q  Quiet"
970 971
	echo " -r  Install missing dependencies from system repository (using sudo)"
	echo " -R  Recursively build and install missing dependencies (using sudo)"
972
	echo " -s  Set source package destination directory"
973
	echo " -u  Recursively build and upgrade all dependencies (using sudo)"
Natanael Copa's avatar
Natanael Copa committed
974 975
	echo ""
	echo "Commands:"
976
	echo "  checksum    Generate checksum to be included in APKBUILD"
977 978 979 980 981
	echo "  fetch       Fetch sources to \$SRCDEST and verify checksums"
	echo "  sanitycheck Basic sanity check of APKBUILD"
	echo "  md5check    Check md5sums"
	echo "  unpack      Unpack sources to \$srcdir"
	echo "  build       Compile and install package into \$pkgdir"
982
	echo "  listpkg     List target packages"
983 984 985
	echo "  package     Create package in \$PKGDEST"
	echo "  rootpkg     Run '$0 build package' as fakeroot"
	echo "  clean       Remove temp build and install dirs"
986
	echo "  cleanoldpkg Remove binary packages except current version"
987 988 989 990
	echo "  cleanpkg    Remove already built binary and source package"
	echo "  cleancache  Remove downloaded files from \$SRCDEST"
	echo "  srcpkg      Make a source package"
	echo "  up2date     Compare target and sources dates"
991 992
	echo "  installdeps Install packages listed in makedepends and depends"
	echo "  uninstalldeps Uninstall packages listed in makedepends and depends"
Natanael Copa's avatar
Natanael Copa committed
993 994 995 996 997
	echo ""
	exit 0
}

APKBUILD="${APKBUILD:-./APKBUILD}"
998 999
unset force
unset recursive
1000
while getopts "dfhi:kinp:P:qrRs:u" opt; do
Natanael Copa's avatar
Natanael Copa committed
1001
	case $opt in
1002
		'd') nodeps=1;;
Natanael Copa's avatar
Natanael Copa committed
1003
		'f') force=1;;
1004 1005
		'h') usage;;
		'i') install_after="$install_after $OPTARG";;
1006
		'k') keep=1;;
1007
		'n') die "Use newapkbuild to create new aports";;
1008
		'p') PKGDEST=$OPTARG;;
1009
		'P') REPODEST=$OPTARG;;
Natanael Copa's avatar
Natanael Copa committed
1010
		'q') quiet=1;;
1011 1012
		'r') install_deps=1;;
		'R') recursive=1;;
1013
		's') SRCDEST=$OPTARG;;
1014 1015
		'u') upgrade=1
		     recursive=1;;
Natanael Copa's avatar
Natanael Copa committed
1016 1017 1018 1019
	esac
done
shift $(( $OPTIND - 1 ))

1020 1021 1022 1023 1024
# If REPODEST is set then it will override the PKGDEST
if [ -n "$REPODEST" ]; then
	PKGDEST="$REPODEST/$repo"
fi

1025
# source the buildfile
1026 1027
[ -f "$APKBUILD" ] || die "Could not find $APKBUILD (PWD=$PWD)"
. "$APKBUILD"
1028

1029
# If we are handling a sub package then reset subpackages and install
1030
if [ -n "$subpkgname" ]; then
1031
	origsubpackages="$subpackages"
1032
	subpackages=
1033
	install=
1034
fi
1035
pkgdir="$pkgbasedir/$pkgname"
1036
controldir="$pkgbasedir"/.control.${subpkgname:-$pkgname}
Natanael Copa's avatar
Natanael Copa committed
1037 1038

trap 'die "Aborted by user"' INT
Linux User's avatar
Linux User committed
1039
set_xterm_title "abuild: $pkgname"
Natanael Copa's avatar
Natanael Copa committed
1040 1041

if [ -z "$1" ]; then
1042
	set all
Natanael Copa's avatar
Natanael Copa committed
1043 1044 1045 1046 1047 1048
fi

while [ $# -gt 0 ]; do
	runpart $1
	shift
done
1049 1050

for i in $install_after; do
Natanael Copa's avatar
Natanael Copa committed
1051
	post_add $i
1052 1053
done

Linux User's avatar
Linux User committed
1054
cleanup
Natanael Copa's avatar
Natanael Copa committed
1055