abuild.in 25.2 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
	$part || die "$part failed"
}

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

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

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

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

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

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

571
572
573
574
doc() {
	default_doc
}

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

588
589
590
591
mod() {
	default_mod
}

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

618
619
620
621
dev() {
	default_dev
}

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

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

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

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

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

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

up2date() {
	apk_up2date && cache_up2date
}

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

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

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

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

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

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

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

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

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

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

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

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

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

887
888
889
890
891
892
893
894
895
896
897
898
899
# 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
}

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

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

Natanael Copa's avatar
Natanael Copa committed
908
makedepends_has() {
909
	deplist_has "$1" $makedepends
Natanael Copa's avatar
Natanael Copa committed
910
911
}

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

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

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

937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
# 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
960
961
962
		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\"/" \
963
964
965
966
967
			-e "s/^source=\"\(.*\)\"/source=\"\1\n\t$pn.initd\n\t$pn.confd\n\t\$install\n\t\"/" \
			APKBUILD
		
	fi
}
968
969

installdeps() {
970
	local deps i
971
	sudo apk add --repository "$apkcache" --virtual .makedepends-$pkgname \
972
		$makedepends
973
974
975
}

uninstalldeps (){
976
	sudo apk del .makedepends-$pkgname
977
}
978
979
980
981
982
983
984
985
986

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

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

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

1055
1056
1057
1058
1059
# If REPODEST is set then it will override the PKGDEST
if [ -n "$REPODEST" ]; then
	PKGDEST="$REPODEST/$repo"
fi

1060
# source the buildfile
1061
1062
1063
1064
if [ -z "$newname" ]; then
	[ -f "$APKBUILD" ] || die "Could not find $APKBUILD (PWD=$PWD)"
	. "$APKBUILD"
fi
1065

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

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

1078
1079
1080
if [ -z "$1" ] && [ -n "$newname" ]; then
	set "newaport"
fi
Natanael Copa's avatar
Natanael Copa committed
1081
1082

if [ -z "$1" ]; then
1083
	set all
Natanael Copa's avatar
Natanael Copa committed
1084
1085
1086
1087
1088
1089
fi

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