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

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

# defaults
SRCDEST=${SRCDEST:-$startdir}
PKGDEST=${PKGDEST:-$startdir}
25
BUILD_BASE="build-base"
Natanael Copa's avatar
Natanael Copa committed
26

27 28
SUDO=${SUDO:-"sudo"}

29
default_cmds="sanitycheck builddeps clean fetch unpack mkusers rootpkg apkcache"
Natanael Copa's avatar
Natanael Copa committed
30

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

Natanael Copa's avatar
Natanael Copa committed
35
# source functions
36 37
datadir=/usr/share/abuild

Natanael Copa's avatar
Natanael Copa committed
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 67 68 69
#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
}
Natanael Copa's avatar
Natanael Copa committed
70 71

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

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

Natanael Copa's avatar
Natanael Copa committed
85
die() {
Linux User's avatar
Linux User committed
86
	error "$@"
Linux User's avatar
Linux User committed
87
	cleanup
Natanael Copa's avatar
Natanael Copa committed
88 89 90
	exit 1
}

91 92
# check if apkbuild is basicly sane
sanitycheck() {
Natanael Copa's avatar
Natanael Copa committed
93
	local i
94
	msg "Checking sanity of $APKBUILD..."
95 96 97
	[ -z "$pkgname" ] && die "Missing pkgname in APKBUILD"
	[ -z "${pkgname##* *}" ] && die "pkgname contains spaces"
	[ -z "$pkgver" ] && die "Missing pkgver in APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
98 99 100 101
	if [ "$pkgver" != "volatile" ]; then
		apk version --check -q "$pkgver" ||\
			die "$pkgver is not a valid version"
	fi
102 103 104 105 106
	[ -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
107 108 109 110 111 112 113
	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"

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

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

133 134 135
	# 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
136 137

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
140 141 142 143
	return 0
}

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

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

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

182 183
is_remote() {
	case "$1" in
Natanael Copa's avatar
Natanael Copa committed
184
		http://*|ftp://*|https://*)
185 186 187 188 189
			return 0;;
	esac
	return 1
}

Natanael Copa's avatar
Natanael Copa committed
190 191 192
fetch() {
	local s
	mkdir -p "$srcdir"
193 194 195 196 197 198 199
	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
200 201 202 203 204 205
	done
}

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

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

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

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

257 258 259 260 261 262 263 264 265 266 267 268
# 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
}
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286

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

# override those in your build script
Natanael Copa's avatar
Natanael Copa committed
294 295 296 297
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
298
build() {
299
	:
Natanael Copa's avatar
Natanael Copa committed
300 301
}

Natanael Copa's avatar
Natanael Copa committed
302 303 304 305 306 307 308 309 310
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
}

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

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

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

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

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

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

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

433 434 435
trace_apk_deps() {
	local name="$1"
	local dir="$2"
436 437
	local i j found autodeps=
	msg "Tracing dependencies for $name..."
438
	for i in $(cat "$dir"/.needs-so 2>/dev/null); do
439
		found=
440
		# first check if its provide by same apkbuild
441
		for j in "$dir"/../.control.*/.provides-so; do
442
			grep -w "$i" "$j" >/dev/null || continue
443
			found=${j%/.provides-so}
444
			found=${found##*/.control.}
445
			break
446 447 448 449 450 451 452 453 454
		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
455
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
456 457 458
			warning "You can remove '$found' from depends"
			continue
		fi
459 460
		list_has "$found" $autodeps || autodeps="$autodeps $found"
		msg "Added '$found' as dependency as it has $i"
461 462 463 464 465 466
	done
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
467 468 469 470 471 472
	# 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
473 474
}

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

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

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

506 507 508
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
509 510 511
	done
}

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

518
	local apk
519
	mkdir -p "$apkcache" || return 1
520 521 522 523 524 525 526 527 528 529
	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
530
	for apk in $(listpkg); do
531
		ln -sf "$PKGDEST"/$apk "$apkcache"/$apk
532 533
	done

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

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

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

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

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

575 576 577 578
doc() {
	default_doc
}

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

592 593 594 595
mod() {
	default_mod
}

596
# predefined splitfunc dev
597
default_dev() {
598
	local i
599
	depends="$pkgname $depends_dev"
600
	install="$install_dev"
Natanael Copa's avatar
Natanael Copa committed
601
	triggers="$triggers_dev"
602 603 604 605
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
606
	cd "$pkgdir" || return 0
607 608 609 610 611
	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
612 613
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
614
			mkdir -p "$d"
615
			mv "$pkgdir/$i" "$d"
616
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
617 618
		fi
	done
Natanael Copa's avatar
Natanael Copa committed
619
	return 0
Natanael Copa's avatar
Natanael Copa committed
620 621
}

622 623 624 625
dev() {
	default_dev
}

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

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

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

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

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

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

up2date() {
	apk_up2date && cache_up2date
}

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

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

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

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

780
	installed_deps=$(apk info -e $deps)
781
	# find which deps are missing
782
	for i in $deps; do
783
		if [ "${i#\!}" != "$i" ]; then
784
			list_has ${i#\!} $installed_deps \
785
				&& die "Conflicting package ${i#\!} is installed."
786
		elif ! deplist_has $i $installed_deps; then
787 788 789 790 791 792 793
			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

794 795
	[ -z "$missing" ] && [ -z "$install_deps" ] && [ -z "$recursive" ] \
		&& return 0
796

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

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

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

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

856 857 858 859 860 861 862 863
# 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
864 865 866 867 868 869 870 871
source_has() {
	local i
	for i in $source; do
		[ "$1" = "${i##*/}" ] && return 0
	done
	return 1
}

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

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

891 892 893 894 895 896 897 898 899 900 901 902 903
# 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
}

904 905 906 907
options_has() {
	list_has "$1" $options
}

908
depends_has() {
909
	deplist_has "$1" $depends
910 911
}

Natanael Copa's avatar
Natanael Copa committed
912
makedepends_has() {
913
	deplist_has "$1" $makedepends
Natanael Copa's avatar
Natanael Copa committed
914 915
}

Natanael Copa's avatar
Natanael Copa committed
916 917 918 919
md5sums_has() {
	list_has "$1" $md5sums
}

Natanael Copa's avatar
Natanael Copa committed
920 921 922 923
install_has() {
	list_has "$1" $install
}

Natanael Copa's avatar
abuild:  
Natanael Copa committed
924 925 926 927 928 929 930 931 932 933 934 935 936 937
# 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
938
	$SUDO apk add -u "$pkgf" || die "Failed to install $1"
Natanael Copa's avatar
abuild:  
Natanael Copa committed
939 940
}

941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
# create new aport from templates
newaport() {
	local pn=${newname%-[0-9]*}
	local pv
	if [ "$pn" != "$newname" ]; then
		pv=${newname#$pn-}
	fi
	if [ -e "$pn"/APKBUILD ]; then
		error "$pn/APKBUILD already exist"
		return 1
	fi
	mkdir -p "$pn"
	cd "$pn"
	sed 	-e '1,/^\#*$/d' \
		-e "s/^\(# Contributor: \).*/\1$PACKAGER/" \
		-e "s/^\(# Maintainer: \).*/\1$PACKAGER/" \
		-e "s/^pkgname=.*/pkgname=$pn/" \
		-e "s/^pkgver=.*/pkgver=$pv/" \
		"$datadir"/sample.APKBUILD > APKBUILD || return 1
	#-e '1,/^\#$/d' \
	if [ -n "$cpinitd" ]; then
		cp "$datadir"/sample.initd $pn.initd
		cp "$datadir"/sample.confd $pn.confd
964 965 966
		cp "$datadir"/sample.pre-install $pn.pre-install
		cp "$datadir"/sample.post-install $pn.post-install
		sed -i -e "s/^install=.*/install=\"\$pkgname.pre-install \$pkgname.post-install\"/" \
967 968 969 970 971
			-e "s/^source=\"\(.*\)\"/source=\"\1\n\t$pn.initd\n\t$pn.confd\n\t\$install\n\t\"/" \
			APKBUILD
		
	fi
}
972 973

installdeps() {
974
	local deps i
975
	sudo apk add --repository "$apkcache" --virtual .makedepends-$pkgname \
976
		$makedepends
977 978 979
}

uninstalldeps (){
980
	sudo apk del .makedepends-$pkgname
981
}
982 983 984 985 986 987 988 989 990

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

Natanael Copa's avatar
Natanael Copa committed
991
usage() {
992
	echo "$(basename $0) $abuild_ver"
993 994
	echo "usage: ${0##*/} [options] [-i PKG] [-P REPODEST] [-p PKGDEST]"
	echo "              [-s SRCDEST] [cmd] ..."
995
	echo "       ${0##*/} [-c] -n PKGNAME[-PKGVER]"
Natanael Copa's avatar
Natanael Copa committed
996
	echo "Options:"
997
	echo " -d  Disable dependency checking"
Natanael Copa's avatar
Natanael Copa committed
998
	echo " -f  Force specified cmd, even if they are already done"
999 1000
	echo " -h  Show this help"
	echo " -i  Install PKG after successul build"
1001
	echo " -k  Keep built packages, even if APKBUILD or sources are newer"
1002
	echo " -p  Set package destination directory"
1003
	echo " -P  Set PKGDEST to REPODEST/<repo>, where repo is the parents dir name" 
Natanael Copa's avatar
Natanael Copa committed
1004
	echo " -q  Quiet"
1005 1006
	echo " -r  Install missing dependencies from system repository (using sudo)"
	echo " -R  Recursively build and install missing dependencies (using sudo)"
1007
	echo " -s  Set source package destination directory"
1008
	echo " -u  Recursively build and upgrade all dependencies (using sudo)"
Natanael Copa's avatar
Natanael Copa committed
1009
	echo ""
1010 1011 1012
	echo " -n  Create a new APKBUILD in a directory named PKGNAME"
	echo " -c  Copy a sample init.d, conf.d and install script to new directory"
	echo ""
Natanael Copa's avatar
Natanael Copa committed
1013
	echo "Commands:"
1014
	echo "  checksum    Generate checksum to be included in APKBUILD"
1015 1016 1017 1018 1019
	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"
1020
	echo "  listpkg     List target packages"
1021 1022 1023
	echo "  package     Create package in \$PKGDEST"
	echo "  rootpkg     Run '$0 build package' as fakeroot"
	echo "  clean       Remove temp build and install dirs"
1024
	echo "  cleanoldpkg Remove binary packages except current version"
1025 1026 1027 1028
	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"
1029 1030
	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
1031 1032 1033 1034 1035
	echo ""
	exit 0
}

APKBUILD="${APKBUILD:-./APKBUILD}"
1036 1037
unset force
unset recursive
1038
while getopts "cdfhi:kin:p:P:qrRs:u" opt; do
Natanael Copa's avatar
Natanael Copa committed
1039
	case $opt in
1040
		'c') cpinitd=1;;
1041
		'd') nodeps=1;;
Natanael Copa's avatar
Natanael Copa committed
1042
		'f') force=1;;
1043 1044
		'h') usage;;
		'i') install_after="$install_after $OPTARG";;
1045
		'k') keep=1;;
1046
		'n') newname=$OPTARG;;
1047
		'p') PKGDEST=$OPTARG;;
1048
		'P') REPODEST=$OPTARG;;
Natanael Copa's avatar
Natanael Copa committed
1049
		'q') quiet=1;;
1050 1051
		'r') install_deps=1;;
		'R') recursive=1;;
1052
		's') SRCDEST=$OPTARG;;
1053 1054
		'u') upgrade=1
		     recursive=1;;
Natanael Copa's avatar
Natanael Copa committed
1055 1056 1057 1058
	esac
done
shift $(( $OPTIND - 1 ))

1059 1060 1061 1062 1063
# If REPODEST is set then it will override the PKGDEST
if [ -n "$REPODEST" ]; then
	PKGDEST="$REPODEST/$repo"
fi

1064
# source the buildfile
1065 1066 1067 1068
if [ -z "$newname" ]; then
	[ -f "$APKBUILD" ] || die "Could not find $APKBUILD (PWD=$PWD)"
	. "$APKBUILD"
fi
1069

1070
# If we are handling a sub package then reset subpackages and install
1071
if [ -n "$subpkgname" ]; then
1072
	origsubpackages="$subpackages"
1073
	subpackages=
1074
	install=
1075
fi
1076
pkgdir="$pkgbasedir/$pkgname"
1077
controldir="$pkgbasedir"/.control.${subpkgname:-$pkgname}
Natanael Copa's avatar
Natanael Copa committed
1078 1079

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

1082 1083 1084
if [ -z "$1" ] && [ -n "$newname" ]; then
	set "newaport"
fi
Natanael Copa's avatar
Natanael Copa committed
1085 1086

if [ -z "$1" ]; then
1087
	set all
Natanael Copa's avatar
Natanael Copa committed
1088 1089 1090 1091 1092 1093
fi

while [ $# -gt 0 ]; do
	runpart $1
	shift
done
1094 1095

for i in $install_after; do
Natanael Copa's avatar
abuild:  
Natanael Copa committed
1096
	post_add $i
1097 1098
done

Linux User's avatar
Linux User committed
1099
cleanup
Natanael Copa's avatar
Natanael Copa committed
1100