abuild.in 24.9 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
294
295
296
297
	$part || die "$part failed"
}

# override those in your build script
build() {
	die "No build() function found in $APKBUILD"
}

Natanael Copa's avatar
Natanael Copa committed
298
299
300
301
302
303
304
305
306
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
}

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

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

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

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

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

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

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

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

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

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

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

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

508
apkcache() {
509
	if ! apk_up2date || [ -n "$force" ]; then
510
511
		sanitycheck && builddeps && clean && fetch && unpack \
			&& mkusers && rootpkg || return 1
512
	fi
513

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

530
	msg "Updating the cached abuild repository index..."
531
532
533
534
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
	apk index $oldindex --output APKINDEX.tar.gz.unsigned *.apk || exit 1
	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
540
	chmod 644 APKINDEX.tar.gz
541
542
}

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

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

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

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

569
570
571
572
doc() {
	default_doc
}

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

586
587
588
589
mod() {
	default_mod
}

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

616
617
618
619
dev() {
	default_dev
}

Natanael Copa's avatar
Natanael Copa committed
620
621
# build and package in fakeroot
rootpkg() {
Natanael Copa's avatar
Natanael Copa committed
622
	cd "$startdir"
623
	msg "Entering fakeroot..."
624
	fakeroot $0 build prepare_subpackages prepare_package create_apks
Natanael Copa's avatar
Natanael Copa committed
625
626
627
}

srcpkg() {
628
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
629
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
630
631
632
633
634
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
635
	msg "Creating source package $p.src.tar.gz..."
636
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
637
638
}

639
# check if package is up to date
640
apk_up2date() {
641
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
642
	local i s
Natanael Copa's avatar
Natanael Copa committed
643
	cd "$startdir"
644
645
646
647
648
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

649
	for i in $source APKBUILD; do
650
651
652
653
654
655
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
656
657
658
659
660
661
662
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

663
664
665
666
cache_up2date() {
	local i apk
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
667
		[ "$apkcache"/APKINDEX.tar.gz -nt "$apkcache"/$apk ] || return 1
668
669
670
671
672
673
674
675
	done
	return 0
}

up2date() {
	apk_up2date && cache_up2date
}

676
677
678
679
680
681
682
683
684
685
686
687
688
# 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=
689
		. $i
690
		dir=${i%/APKBUILD}
691
		deps=
692
		# filter out conflicts from deps and version info
693
694
695
696
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
697
			deps="$deps ${j%[<>=]*}"
698
		done
699
		for j in $pkgname $subpackages; do
700
			echo "o ${j%%:*} $dir"
701
			set -- $deps
702
703
704
705
706
707
708
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
709
710
		done
	done
711
	)
712
713
}

714
deptrace() {
715
	local deps="$@"
716
	[ -z "$deps" ] && return 0
717
718
719
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
Natanael Copa's avatar
abuild:    
Natanael Copa committed
720
			apk info -q | sort |  sed 's/^/i /'
721
		fi
722
	) | awk -v pkgs="$deps" '
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747

	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() {
748
749
	local deps= alldeps= pkg= i= dir= ver= missing= installed_deps=
	local filtered_deps=
750
	[ -n "$nodeps" ] && return 0
751
	msg "Analyzing dependencies..."
752
753
754
755
	deps="$BUILD_BASE $makedepends"

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

759
	installed_deps=$(apk info -e $deps)
760
	# find which deps are missing
761
	for i in $deps; do
762
		if [ "${i#\!}" != "$i" ]; then
763
			list_has ${i#\!} $installed_deps \
764
				&& die "Conflicting package ${i#\!} is installed."
765
		elif ! deplist_has $i $installed_deps; then
766
767
768
769
770
771
772
			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

773
774
	[ -z "$missing" ] && [ -z "$install_deps" ] && [ -z "$recursive" ] \
		&& return 0
775

776
	uninstall_after=".makedepends-$pkgname $uninstall_after"
777
	if [ -n "$install_deps" ] && [ -z "$recursive" ]; then
778
		$SUDO apk add --repository "$apkcache" \
779
780
			--virtual .makedepends-$pkgname $deps \
			&& return 0
781
	fi
782
783
	
	[ -z "$recursive" ] && return 1
784
785
786

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
787
		local m=$(apk search --repository "$apkcache" ${i%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
788
		if [ -z "$m" ]; then
789
790
791
			missing="$missing $i"
		fi
	done
792
793
	
	for i in $(deptrace $missing); do
794
795
796
797
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
		msg "Entering $dir"
798
		cd "$dir" && $0 -k -r apkcache || return 1
799
	done
800
	$SUDO apk add -u --repository "$apkcache" \
801
		--virtual .makedepends-$pkgname $deps
802
803
}

804
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
805
806
checksum() {
	local s files
807
	[ -z "$source" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
808
	fetch
809
	msg "Updating the md5sums in APKBUILD..."
Natanael Copa's avatar
Natanael Copa committed
810
811
812
813
	for s in $source; do
		files="$files ${s##*/}"
	done
	md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
814
815
	sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
	echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
816
817
}

818
stripbin() {
819
	local bin
820
	options_has "!strip" && return 0
821
822
	cd "${subpkgdir:-$pkgdir}" || return 1
	msg "Stripping binaries"
823
	find . -type f 2>/dev/null | while read bin; do
824
825
826
827
828
829
830
831
832
833
834
		local opt=
		case "$(file -biz "$bin")" in
			*/x-sharedlib*|*/x-archive*)
				strip --strip-debug "$bin";;
			*/x-executable*)
				strip "$bin";;
		esac
	done
	return 0
}

835
836
837
838
839
840
841
842
# 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
843
844
845
846
847
848
849
850
source_has() {
	local i
	for i in $source; do
		[ "$1" = "${i##*/}" ] && return 0
	done
	return 1
}

Natanael Copa's avatar
abuild:    
Natanael Copa committed
851
852
853
854
855
856
857
858
subpackages_has() {
	local i
	for i in $subpackages; do
		[ "$1" = "${i%:*}" ] && return 0
	done
	return 1
}

859
860
861
862
863
864
865
866
867
868
869
list_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

870
871
872
873
874
875
876
877
878
879
880
881
882
# 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
}

883
884
885
886
options_has() {
	list_has "$1" $options
}

887
depends_has() {
888
	deplist_has "$1" $depends
889
890
}

Natanael Copa's avatar
Natanael Copa committed
891
makedepends_has() {
892
	deplist_has "$1" $makedepends
Natanael Copa's avatar
Natanael Copa committed
893
894
}

Natanael Copa's avatar
Natanael Copa committed
895
896
897
898
md5sums_has() {
	list_has "$1" $md5sums
}

Natanael Copa's avatar
Natanael Copa committed
899
900
901
902
install_has() {
	list_has "$1" $install
}

Natanael Copa's avatar
abuild:    
Natanael Copa committed
903
904
905
906
907
908
909
910
911
912
913
914
915
916
# 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
917
	$SUDO apk add -u "$pkgf" || die "Failed to install $1"
Natanael Copa's avatar
abuild:    
Natanael Copa committed
918
919
}

920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
# 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
943
944
945
		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\"/" \
946
947
948
949
950
			-e "s/^source=\"\(.*\)\"/source=\"\1\n\t$pn.initd\n\t$pn.confd\n\t\$install\n\t\"/" \
			APKBUILD
		
	fi
}
951
952

installdeps() {
953
	local deps i
954
	sudo apk add --repository "$apkcache" --virtual .makedepends-$pkgname \
955
		$makedepends
956
957
958
}

uninstalldeps (){
959
	sudo apk del .makedepends-$pkgname
960
}
961
962
963
964
965
966
967
968
969

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

Natanael Copa's avatar
Natanael Copa committed
970
usage() {
971
	echo "$(basename $0) $abuild_ver"
972
973
	echo "usage: ${0##*/} [options] [-i PKG] [-P REPODEST] [-p PKGDEST]"
	echo "              [-s SRCDEST] [cmd] ..."
974
	echo "       ${0##*/} [-c] -n PKGNAME[-PKGVER]"
Natanael Copa's avatar
Natanael Copa committed
975
	echo "Options:"
976
	echo " -d  Disable dependency checking"
Natanael Copa's avatar
Natanael Copa committed
977
	echo " -f  Force specified cmd, even if they are already done"
978
979
	echo " -h  Show this help"
	echo " -i  Install PKG after successul build"
980
	echo " -k  Keep built packages, even if APKBUILD or sources are newer"
981
	echo " -p  Set package destination directory"
982
	echo " -P  Set PKGDEST to REPODEST/<repo>, where repo is the parents dir name" 
Natanael Copa's avatar
Natanael Copa committed
983
	echo " -q  Quiet"
984
985
	echo " -r  Install missing dependencies from system repository (using sudo)"
	echo " -R  Recursively build and install missing dependencies (using sudo)"
986
	echo " -s  Set source package destination directory"
987
	echo " -u  Recursively build and upgrade all dependencies (using sudo)"
Natanael Copa's avatar
Natanael Copa committed
988
	echo ""
989
990
991
	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
992
	echo "Commands:"
993
	echo "  checksum    Generate checksum to be included in APKBUILD"
994
995
996
997
998
	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"
999
	echo "  listpkg     List target packages"
1000
1001
1002
	echo "  package     Create package in \$PKGDEST"
	echo "  rootpkg     Run '$0 build package' as fakeroot"
	echo "  clean       Remove temp build and install dirs"
1003
	echo "  cleanoldpkg Remove binary packages except current version"
1004
1005
1006
1007
	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"
1008
1009
	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
1010
1011
1012
1013
1014
	echo ""
	exit 0
}

APKBUILD="${APKBUILD:-./APKBUILD}"
1015
1016
unset force
unset recursive
1017
while getopts "cdfhi:kin:p:P:qrRs:u" opt; do
Natanael Copa's avatar
Natanael Copa committed
1018
	case $opt in
1019
		'c') cpinitd=1;;
1020
		'd') nodeps=1;;
Natanael Copa's avatar
Natanael Copa committed
1021
		'f') force=1;;
1022
1023
		'h') usage;;
		'i') install_after="$install_after $OPTARG";;
1024
		'k') keep=1;;
1025
		'n') newname=$OPTARG;;
1026
		'p') PKGDEST=$OPTARG;;
1027
		'P') REPODEST=$OPTARG;;
Natanael Copa's avatar
Natanael Copa committed
1028
		'q') quiet=1;;
1029
1030
		'r') install_deps=1;;
		'R') recursive=1;;
1031
		's') SRCDEST=$OPTARG;;
1032
1033
		'u') upgrade=1
		     recursive=1;;
Natanael Copa's avatar
Natanael Copa committed
1034
1035
1036
1037
	esac
done
shift $(( $OPTIND - 1 ))

1038
1039
1040
1041
1042
# If REPODEST is set then it will override the PKGDEST
if [ -n "$REPODEST" ]; then
	PKGDEST="$REPODEST/$repo"
fi

1043
# source the buildfile
1044
1045
1046
1047
if [ -z "$newname" ]; then
	[ -f "$APKBUILD" ] || die "Could not find $APKBUILD (PWD=$PWD)"
	. "$APKBUILD"
fi
1048

1049
# If we are handling a sub package then reset subpackages and install
1050
if [ -n "$subpkgname" ]; then
1051
	origsubpackages="$subpackages"
1052
	subpackages=
1053
	install=
1054
fi
1055
pkgdir="$pkgbasedir/$pkgname"