abuild.in 29.5 KB
Newer Older
Natanael Copa's avatar
Natanael Copa committed
1
2
#!/bin/sh

3
# script to build apk packages (light version of makepkg)
Natanael Copa's avatar
Natanael Copa committed
4
5
6
7
8
9
10
# Copyright (c) 2008 Natanael Copa <natanael.copa@gmail.com>
#
# Distributed under GPL-2
#
# Depends on: busybox utilities, fakeroot, 
#

Natanael Copa's avatar
Natanael Copa committed
11
12
abuild_ver=@VERSION@
sysconfdir=@sysconfdir@
13
abuildrepo=@abuildrepo@
Natanael Copa's avatar
Natanael Copa committed
14
datadir=@datadir@
Natanael Copa's avatar
Natanael Copa committed
15

16
program=${0##*/}
17
abuild_path=$(readlink -f $0)
Natanael Copa's avatar
Natanael Copa committed
18
19

# defaults
20
BUILD_BASE="build-base"
21
SUDO=${SUDO:-"sudo"}
22
FAKEROOT=${FAKEROOT:-"fakeroot"}
23
APK=${APK:-apk}
24

Natanael Copa's avatar
Natanael Copa committed
25
# read config
Natanael Copa's avatar
Natanael Copa committed
26
ABUILD_CONF=${ABUILD_CONF:-"$sysconfdir/abuild.conf"}
Natanael Copa's avatar
Natanael Copa committed
27
28
[ -f "$ABUILD_CONF" ] && . "$ABUILD_CONF"

29
default_colors() {
Natanael Copa's avatar
Natanael Copa committed
30
31
32
33
34
35
	NORMAL="\033[1;0m"
	STRONG="\033[1;1m"
	RED="\033[1;31m"
	GREEN="\033[1;32m"
	YELLOW="\033[1;33m"
	BLUE="\033[1;34m"
36
37
38
39
40
41
42
43
44
45
46
47
48
49
}

monochrome() {
	NORMAL=""
	STRONG=""
	RED=""
	GREEN=""
	YELLOW=""
	BLUE=""
}

#colors
if [ -n "$USE_COLORS" ]; then
    default_colors
Natanael Copa's avatar
Natanael Copa committed
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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
74
75

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

cleanup() {
	set_xterm_title ""
84
	if [ -z "$install_after" ] && [ -n "$uninstall_after" ]; then
85
		$SUDO $APK del $uninstall_after
86
	fi
Linux User's avatar
Linux User committed
87
88
}

Natanael Copa's avatar
Natanael Copa committed
89
die() {
Linux User's avatar
Linux User committed
90
	error "$@"
Linux User's avatar
Linux User committed
91
	cleanup
Natanael Copa's avatar
Natanael Copa committed
92
93
94
	exit 1
}

95
96
# check if apkbuild is basicly sane
sanitycheck() {
97
	local i suggestion
98
	msg "Checking sanity of $APKBUILD..."
99
100
101
	[ -z "$pkgname" ] && die "Missing pkgname in APKBUILD"
	[ -z "${pkgname##* *}" ] && die "pkgname contains spaces"
	[ -z "$pkgver" ] && die "Missing pkgver in APKBUILD"
102
	if [ "$pkgver" != "volatile" ] && [ -z "$nodeps" ]; then
103
		$APK version --check -q "$pkgver" ||\
Natanael Copa's avatar
Natanael Copa committed
104
105
			die "$pkgver is not a valid version"
	fi
106
	[ -z "$pkgrel" ] && die "Missing pkgrel in APKBUILD"
107
108
109
110
	[ -z "$pkgdesc" ] && die "Missing pkgdesc in APKBUILD"
	[ -z "$url" ] && die "Missing url in APKBUILD"
	[ -z "$license" ] && die "Missing license in APKBULID"

111
112
113
114
115
116
117
118
119
120
	# check if CARCH, CBUILD, CHOST and CTARGET is set
	if [ -z "$CARCH" ]; then
		case "$(uname -m)" in
		i[0-9]86) suggestion=" (Suggestion: CARCH=x86)";;
		x86_64) suggestion=" (Suggestion: CARCH=x86_64)";;
		esac
		die "Please set CARCH in /etc/abuild.conf$suggestion"
	fi
	[ -z "$CHOST" ] && die "Please set CHOST in /etc/abuild.conf"

Natanael Copa's avatar
Natanael Copa committed
121
122
123
124
125
126
127
	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"

128
129
	if [ -n "$source" ]; then
		for i in $source; do
Natanael Copa's avatar
Natanael Copa committed
130
131
132
133
			if install_has "$i"; then
				warning "You should not have \$install in source"
				continue
			fi
134
			md5sums_has ${i##*/} || die "${i##*/} is missing in md5sums"
Natanael Copa's avatar
Natanael Copa committed
135
136
137
			case "$i" in
				https://*) makedepends_has wget || die "wget must be in makedepends when source has https://" ;;
			esac
138
139
		done
	fi
Natanael Copa's avatar
Natanael Copa committed
140

141
142
	if [ -n "$md5sums" ]; then
		for i in $(echo "$md5sums" | awk '{ print $2 }'); do
Natanael Copa's avatar
Natanael Copa committed
143
			source_has $i || die "$i exists in md5sums but is missing in source"
144
145
		done
	fi
146

147
148
149
	# 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
150
151

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

	makedepends_has 'g++' && warning "g++ should not be in makedepends"
154
155
156
157
	return 0
}

md5check() {
Natanael Copa's avatar
Natanael Copa committed
158
	local dummy f
159
160
161
	if [ -z "$source" ]; then
		return 0
	fi
162
	if [ -z "$md5sums" ]; then
163
		die "Use 'abuild checksum' to generate/update the checksum(s)"
164
	fi
165
166
167
	if [ "$(echo $source | wc -l)" -ne "$(echo $md5sums | wc -l)" ]; then
		die "Number of md5sums does not correspond to number of sources"
	fi
168
	fetch || return 1
169
	msg "Checking md5sums..."
170
	cd "$srcdir" && echo "$md5sums" | md5sum -c 
Natanael Copa's avatar
Natanael Copa committed
171
172
173
174
}

uri_fetch() {
	local uri="$1"
175
	local d="${uri##*/}"	# $(basename $uri)
176
177
178
179
	local opts
	[ -n "$quiet" ] && opts="-q"
	[ -f "$SRCDEST/$d" ] && return 0

Natanael Copa's avatar
Natanael Copa committed
180
181
	# we need GNU wget for this
	case "$uri" in
182
183
184
185
186
187
188
189
		*https://*) opts="--no-check-certificate";;
	esac

	# fix saveas-*://* URIs
	case "$uri" in
		# remove 'saveas-' from beginning and
		# '/filename' from end of URI
		saveas-*://*) uri="${uri:7:$(expr ${#uri} - 7 - ${#d} - 1)}";;
Natanael Copa's avatar
Natanael Copa committed
190
191
	esac
	
Natanael Copa's avatar
Natanael Copa committed
192
	mkdir -p "$SRCDEST"
193
194
195
	if [ -f "$SRCDEST/$d.part" ]; then
		msg "Partial download found. Trying to resume"
		opts="$opts -c"
Natanael Copa's avatar
Natanael Copa committed
196
	fi
Linux User's avatar
Linux User committed
197
	msg "Fetching $uri"
198
199
	wget $opts -O "$SRCDEST/$d.part" "$uri" \
		&& mv "$SRCDEST/$d.part" "$SRCDEST/$d"
Natanael Copa's avatar
Natanael Copa committed
200
201
}

202
203
is_remote() {
	case "$1" in
204
		http://*|ftp://*|https://*|saveas-*://*)
205
206
207
208
209
			return 0;;
	esac
	return 1
}

210
211
212
213
214
# try download from file from mirror first
uri_fetch_mirror() {
	local uri="$1"
	local d="${uri##*/}"	# $(basename $uri)
	if [ -n "$DISTFILES_MIRROR" ]; then
215
216
217
218
219
		if is_remote "$DISTFILES_MIRROR"; then
			uri_fetch "$DISTFILES_MIRROR"/$d && return 0
		else
			cp "$DISTFILES_MIRROR"/$d "$SRCDEST" && return 0
		fi
220
221
222
223
	fi
	uri_fetch "$uri"
}

224
default_fetch() {
Natanael Copa's avatar
Natanael Copa committed
225
226
	local s
	mkdir -p "$srcdir"
227
228
	for s in $source; do
		if is_remote "$s"; then
229
			uri_fetch_mirror "$s" || return 1
230
231
232
233
			ln -sf "$SRCDEST/${s##*/}" "$srcdir"/
		else		
			ln -sf "$startdir/$s" "$srcdir/"
		fi
Natanael Copa's avatar
Natanael Copa committed
234
235
236
	done
}

237
238
239
240
fetch() {
	default_fetch
}

Natanael Copa's avatar
Natanael Copa committed
241
# unpack the sources
242
default_unpack() {
Natanael Copa's avatar
Natanael Copa committed
243
	local u
244
	md5check || return 1
Natanael Copa's avatar
Natanael Copa committed
245
246
247
248
	mkdir -p "$srcdir"
	for u in $source; do
		local s="$SRCDEST/${u##*/}"	# $(basename $s)
		case "$s" in
Natanael Copa's avatar
Natanael Copa committed
249
			*.tar.gz|*.tgz)
250
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
251
252
				tar -C "$srcdir" -zxf "$s" || return 1;;
			*.tar.bz2)
253
				msg "Unpacking $s..."
Natanael Copa's avatar
Natanael Copa committed
254
				tar -C "$srcdir" -jxf "$s" || return 1;;
255
256
257
258
			*.tar.lzma)
				msg "Unpacking $s..."
				unlzma -c "$s" | tar -C "$srcdir" -x  \
					|| return 1;;
259
260
261
			*.tar.xz)
				msg "Unpacking $s..."
				unxz -c "$s" | tar -C "$srcdir" -x || return 1;;
Natanael Copa's avatar
Natanael Copa committed
262
263
264
			*.zip)
				msg "Unpacking $s..."
				unzip "$s" -d "$srcdir" || return 1;;
Natanael Copa's avatar
Natanael Copa committed
265
266
267
268
		esac
	done
}

269
270
271
272
unpack() {
	default_unpack
}

Natanael Copa's avatar
Natanael Copa committed
273
274
# cleanup source and package dir
clean() {
275
	msg "Cleaning temporary build dirs..."
Natanael Copa's avatar
Natanael Copa committed
276
	rm -rf "$srcdir"
277
	rm -rf "$pkgbasedir"
Natanael Copa's avatar
Natanael Copa committed
278
279
280
281
282
283
}

# cleanup fetched sources
cleancache() {
	local s
	for s in $source; do
284
285
286
287
		if is_remote "$s"; then
			msg "Cleaning downloaded ${s##*/}..."
			rm -f "$SRCDEST/${s##*/}"
		fi
Natanael Copa's avatar
Natanael Copa committed
288
289
290
291
292
	done
}

cleanpkg() {
	local i
293
	getpkgver || return 1
294
	msg "Cleaning built packages..."
295
	for i in $pkgname $subpackages; do
296
297
		local p="${i%:*}-$pkgver-r$pkgrel"
		rm -f "$PKGDEST/$p.apk" "$PKGDEST/$p.src.tar.gz" \
298
			"$abuildrepo"/$p.apk
Natanael Copa's avatar
Natanael Copa committed
299
	done
300
	# remove given packages from index
Natanael Copa's avatar
Natanael Copa committed
301
302
}

303
304
305
# clean all packages except current
cleanoldpkg() {
	local i j
306
	getpkgver || return 1
307
308
309
310
311
312
313
314
315
	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
}
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333

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
334
335
runpart() {
	local part=$1
336
	[ -n "$DEBUG" ] && msg "$part"
Natanael Copa's avatar
Natanael Copa committed
337
338
339
340
	$part || die "$part failed"
}

# override those in your build script
341
342
343
344
345
346
347
348
getpkgver() {
	# this func is supposed to be overridden by volatile packages
	if [ "$pkgver" = "volatile" ]; then
		error "Please provide a getpkgver() function in your APKBUILD"
		return 1
	fi
}

Natanael Copa's avatar
Natanael Copa committed
349
350
351
352
prepare() {
	:
}

Natanael Copa's avatar
Natanael Copa committed
353
build() {
354
	:
Natanael Copa's avatar
Natanael Copa committed
355
356
}

357
358
359
360
361
362
# generate a simple tar.gz package of pkgdir
targz() {
	cd "$pkgdir" || return 1
	tar -czf "$PKGDEST"/$pkgname-$pkgver-r$pkgrel.tar.gz *
}

Natanael Copa's avatar
Natanael Copa committed
363
364
365
366
367
368
369
370
371
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
}

372
prepare_subpackages() {
Natanael Copa's avatar
Natanael Copa committed
373
	if [ -z "$subpackages" ]; then
374
375
		return 0
	fi
Natanael Copa's avatar
Natanael Copa committed
376
377
	local i
	cd "$startdir"
378
	for i in $subpackages; do
Natanael Copa's avatar
Natanael Copa committed
379
380
		local func=$(get_split_func $i)
		# call abuild recursively, setting subpkg{dir,name}
381
		msg "Running split function $func..."
382
		subpkgdir="$pkgbasedir/${i%:*}" subpkgname="${i%:*}" \
383
			$0 $func prepare_package || return 1
Natanael Copa's avatar
Natanael Copa committed
384
385
386
	done
}

387
prepare_metafiles() {
388
	getpkgver || return 1
389
390
	local name=${subpkgname:-$pkgname}
	[ -z "${name##* *}" ] && die "package name contains spaces"
391
	local dir=${subpkgdir:-$pkgdir}
392
	local pkg="$name-$pkgver-r$pkgrel.apk"
393
	local pkginfo="$controldir"/.PKGINFO
394
	local sub
Natanael Copa's avatar
Natanael Copa committed
395
396
397
	
	[ ! -d "$dir" ] && die "Missing $dir"
	cd "$dir"
398
	mkdir -p "$controldir"
Natanael Copa's avatar
Natanael Copa committed
399
400
	local builddate=$(date -u "+%s")
	local size=$(du -sk | awk '{print $1 * 1024}')
Natanael Copa's avatar
Natanael Copa committed
401
402
403
404
	local parch="$CARCH"
	if [ "$arch" = "noarch" ]; then
		parch="noarch"
	fi
Natanael Copa's avatar
Natanael Copa committed
405

406
	echo "# Generated by $(basename $0) $abuild_ver" >"$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
407
	if [ -n "$FAKEROOTKEY" ]; then
408
		echo "# using $($FAKEROOT -v)" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
409
	fi
410
411
	echo "# $(date -u)" >> "$pkginfo"
	cat >> "$pkginfo" <<EOF
412
pkgname = $name
413
pkgver = $pkgver-r$pkgrel
Natanael Copa's avatar
Natanael Copa committed
414
415
416
417
418
pkgdesc = $pkgdesc
url = $url
builddate = $builddate
packager = ${PACKAGER:-"Unknown"}
size = $size
Natanael Copa's avatar
Natanael Copa committed
419
arch = $parch
Natanael Copa's avatar
Natanael Copa committed
420
EOF
421
422
	local i deps
	deps="$depends"
423
	if [ "$pkgname" != "busybox" ] && ! depends_has busbox; then
Natanael Copa's avatar
Natanael Copa committed
424
425
		for i in $install ${triggers%%:*}; do
			if head -n 1 "$startdir/$i" | grep '^#!/bin/sh' >/dev/null ; then
426
427
428
429
430
				msg "Script found. busybox added as a dependency for $pkg"
				deps="$deps busybox"
				break
			fi
		done
431
432
	fi
	
Natanael Copa's avatar
Natanael Copa committed
433
	for i in $license; do
434
		echo "license = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
435
436
	done
	for i in $replaces; do
437
		echo "replaces = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
438
	done
439
	for i in $deps; do
440
		echo "depend = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
441
442
	done
	for i in $conflicts; do
443
		echo "conflict = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
444
445
	done
	for i in $provides; do
446
		echo "provides = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
447
448
	done
	for i in $backup; do
449
		echo "backup = $i" >> "$pkginfo"
Natanael Copa's avatar
Natanael Copa committed
450
	done
Natanael Copa's avatar
Natanael Copa committed
451
452
453
	if [ -n "$triggers" ]; then
		echo "triggers = ${triggers#*:}" >> "$pkginfo"
	fi
Natanael Copa's avatar
Natanael Copa committed
454
455

	local metafiles=".PKGINFO"
Natanael Copa's avatar
Natanael Copa committed
456
	for i in $install ${triggers%%:*}; do
457
458
		script=${i#$name}
		case "$script" in
Natanael Copa's avatar
Natanael Copa committed
459
			.pre-install|.post-install|.pre-upgrade|.post-upgrade|.pre-deinstall|.post-deinstall|.trigger)
460
461
				msg "Adding $script"
				;;
Natanael Copa's avatar
Natanael Copa committed
462
			*) 	error "$script: Invalid install/trigger script"
463
464
465
				return 1
				;;
		esac
Natanael Copa's avatar
Natanael Copa committed
466
		cp "$startdir/$i" "$controldir/$script" || return 1
467
		chmod +x "$controldir/$script"
468
469
		metafiles="$metafiles $script"
	done
470
	echo $metafiles | tr ' ' '\n' > "$controldir"/.metafiles
471
}
472

473
474
475
prepare_tracedeps() {
	local dir=${subpkgdir:-$pkgdir}
	options_has "!tracedeps" && return 0
476
	# lets tell all the .so files this package provides in .provides-so
477
478
	find -name '*.so' -o -name '*.so.[0-9]*' | sed 's:.*/::' \
		>"$controldir"/.provides-so
479
480
481
482
483
	# lets tell all the places we should look for .so files - all rpaths
	scanelf -q -Rr "$dir" | sed -e 's/[[:space:]].*//' -e 's/:/\n/' \
		| sort | uniq \
		>"$controldir"/.rpaths
	# now find the so dependencies
484
	scanelf -Rn "$dir" | tr ' ' ':' | awk -F ":" '$1 == "ET_DYN" || $1 == "ET_EXEC" {print $2}'  \
485
486
487
		| sed 's:,:\n:g' | sort | uniq \
	| while read i; do
		# only add files that are not self provided
488
489
		grep "^$i$" "$controldir"/.provides-so >/dev/null \
			|| echo $i >> "$controldir"/.needs-so
490
	done
Natanael Copa's avatar
Natanael Copa committed
491
492
}

493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
# check if dir has arch specific binaries
dir_has_arch_binaries() {
	local dir="$1"
	# if scanelf returns something, then we have binaries
	[ -n "$(scanelf -R "$dir" | head -n 1)" ] && return 0

	# look for static *.a
	[ -n "$(find "$dir" -type f -name '*.a' | head -n 1)" ] && return 0

	return 1
}

# check that noarch is set if needed
archcheck() {
	options_has "!archcheck" && return 0
	if dir_has_arch_binaries "${subpkgdir:-$pkgdir}"; then
		[ "$arch" != "noarch" ] && return 0
		error "Arch specific binaries found so arch must not be set to \"noarch\""
		return 1
	else
		[ "$arch" = "noarch" ] && return 0
		error "No arch specific binaries found so arch should be set to \"noarch\""
		return 1
	fi
	return 0
}

520
prepare_package() {
521
	msg "Preparing ${subpkgname:+sub}package ${subpkgname:-$pkgname}..."
522
	stripbin
523
524
	prepare_metafiles && prepare_tracedeps || return 1
	archcheck	
525
526
527
528
529
530
531
532
}

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

533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
# find real path to so files
real_so_path() {
	local so="$1"
	shift
	while [ $# -gt 0 ]; do
		[ -e "$1"/$so ] && realpath "$1/$so" && return 0
		shift
	done
	error "$so: path not found"
	return 1
}

# search rpaths and /usr/lib /lib for given so files
find_so_files() {
	local rpaths=$(cat "$1")
	shift
	while [ $# -gt 0 ]; do
		real_so_path "$1" /usr/lib /lib $rpaths || return 1
		shift
	done
	return 0
}

556
557
558
trace_apk_deps() {
	local name="$1"
	local dir="$2"
559
	local i= j= found= autodeps= deppkgs= missing= so_paths=
560
	msg "Tracing dependencies for $name..."
561
562
563
	# add pkgconfig if usr/lib/pkgconfig is found
	if [ -d "$pkgbasedir"/$name/usr/lib/pkgconfig ] \
			&& ! grep -q '^depend = pkgconfig' "$dir"/.PKGINFO; then
564
		msg "  added pkgconfig (found /usr/lib/pkgconfig)"
565
566
567
568
569
570
571
		autodeps="$autodeps pkgconfig"
	fi

	# special case for libpthread: we need depend on libgcc
	if grep -q -w '^libpthread.so.*' "$dir"/.needs-so 2>/dev/null \
			&& ! grep -q -w "^depend = libgcc" "$dir"/.PKGINFO; then
		autodeps="$autodeps libgcc"
572
		msg "  added libgcc (due to libpthread)"
573
	fi
574
	for i in $(cat "$dir"/.needs-so 2>/dev/null); do
575
		found=
576
		# first check if its provide by same apkbuild
577
		for j in "$dir"/../.control.*/.provides-so; do
578
			grep -w "$i" "$j" >/dev/null || continue
579
			found=${j%/.provides-so}
580
			found=${found##*/.control.}
581
			break
582
		done
583
584
585
586
587
588
		if [ -n "$found" ]; then
			if ! list_has "$found" $self_provided; then
				self_provided="$self_provided $found"
			fi
		else
			missing="$missing $i"
589
		fi
590
591
592
593
594
595
596
	done

	# find all packages that holds the so files
	so_files=$(find_so_files "$dir"/.rpaths $missing) || return 1
	deppkgs=$($APK info -q -W $so_files) || return 1
	
	for found in $self_provided $deppkgs; do
597
		if grep -w "^depend = ${found}$" "$dir"/.PKGINFO >/dev/null ; then
598
599
600
			warning "You can remove '$found' from depends"
			continue
		fi
601
602
		if [ "$found" != "$name" ] && ! list_has "$found" $autodeps; then
			autodeps="$autodeps $found"
603
			msg "  added $found"
604
		fi
605
	done
606

607
608
609
610
611
612
613
	[ -z "$autodeps" ] && return 0
	echo "# automatically detected:" >> "$dir"/.PKGINFO
	for i in $autodeps; do
		echo "depend = $i" >> "$dir"/.PKGINFO
	done
}

614
615
create_apks() {
	local file
616
	getpkgver || return 1
617
	for file in "$pkgbasedir"/.control.*/.PKGINFO; do
618
619
620
		local dir="${file%/.PKGINFO}"
		local name=$(pkginfo_val pkgname $file)
		local ver=$(pkginfo_val pkgver $file)
621
		local apk=$name-$ver.apk
622
		local datadir="$pkgbasedir"/$name
623
624

		trace_apk_deps "$name" "$dir" || return 1
625
		msg "Creating $apk..."
626
		(
627
628
		cd "$datadir"
		# data.tar.gz
629
630
631
632
633
634
		set -- *
		if [ "$1" = '*' ]; then
			touch .dummy
			set -- .dummy
		fi
		tar -c "$@" | abuild-tar --hash | gzip -9 >"$dir"/data.tar.gz
635
636
637
638
639
640

		# 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
641
		cd "$dir"
642
643
		tar -c $(cat "$dir"/.metafiles) | abuild-tar --cut \
			| gzip -9 > control.tar.gz
644
		abuild-sign -q control.tar.gz || exit 1
Natanael Copa's avatar
Natanael Copa committed
645

646
647
648
		# create the final apk
		cat control.tar.gz data.tar.gz > "$PKGDEST"/$apk
	)
649
650
651
	done
}

652
update_abuildrepo() {
653
	if ! apk_up2date || [ -n "$force" ]; then
654
		sanitycheck && builddeps && clean && fetch && unpack \
Natanael Copa's avatar
Natanael Copa committed
655
			&& prepare && mkusers && rootpkg || return 1
656
	fi
657

658
	local apk
659
660
	mkdir -p "$abuildrepo" || return 1
	cd "$abuildrepo"
661
662
663
664
665
666
667
668
669

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

	# create links for this package
670
	for apk in $(listpkg); do
671
		ln -sf "$PKGDEST"/$apk "$abuildrepo"/$apk
672
673
	done

674
	msg "Updating the cached abuild repository index..."
675
676
677
678
679
	local sign=".SIGN.RSA.${SIGN_PUBLIC_KEY##*/}"
	local oldindex=
	if [ -f APKINDEX.tar.gz ]; then
		oldindex="--index APKINDEX.tar.gz"
	fi
680
	$APK index $oldindex --output APKINDEX.tar.gz.unsigned \
681
682
		--description "$repo $(cd $startdir && git describe)" \
		*.apk || exit 1
683
684
685
	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
686
	chmod 644 APKINDEX.tar.gz
687
688
}

689
# predefined splitfunc doc
690
default_doc() {
691
	depends="$depends_doc"
692
	install="$install_doc"
Natanael Copa's avatar
Natanael Copa committed
693
	triggers="$triggers_doc"
694
	arch=${arch_doc:-"noarch"}
Natanael Copa's avatar
Natanael Copa committed
695
	local i
696
	for i in doc man info html sgml licenses gtk-doc; do
697
698
699
		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
700
701
		fi
	done
702
703
704
	
	rm -f "$subpkgdir/usr/share/info/dir"

705
706
707
#	# compress info and man pages
#	find "$subpkgdir/usr/share" \( -name '*.info' -o -name '*.info-[1-9]' \
#		-o -name '*.[1-9]' \) -exec gzip {} \;
708
709
710
711

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

Natanael Copa's avatar
Natanael Copa committed
712
#	[ -d "$subpkgdir/usr/share/man" ] && depends="man"
Natanael Copa's avatar
Natanael Copa committed
713
714
715
	return 0
}

716
717
718
719
doc() {
	default_doc
}

720
# predefined splitfunc mod
721
default_mod() {
722
	depends="$kernel $depends_mod"
723
	install="$install_mod"
Natanael Copa's avatar
Natanael Copa committed
724
	for i in firmware modules; do
725
726
727
		if [ -d "$pkgdir/lib/$i" ]; then
			rm -rf "$subpkgdir/lib"
			mkdir -p "$subpkgdir/lib"
728
			mv "$pkgdir/lib/$i" "$subpkgdir/lib"
Natanael Copa's avatar
Natanael Copa committed
729
730
731
732
		fi
	done
}

733
734
735
736
mod() {
	default_mod
}

737
# predefined splitfunc dev
738
default_dev() {
739
	local i= j=
740
	depends="$pkgname $depends_dev"
741
	install="$install_dev"
Natanael Copa's avatar
Natanael Copa committed
742
	triggers="$triggers_dev"
743
744
745
746
	for i in $origsubpackages; do
		[ "${i%:*}" = "$subpkgname" ] || depends="$depends ${i%:*}"
	done
		
747
	cd "$pkgdir" || return 0
748
749
	for i in usr/include usr/lib/pkgconfig usr/share/aclocal\
			usr/share/gettext usr/bin/*-config	\
750
			usr/share/vala/vapi usr/share/gir-[0-9]*\
751
752
753
			$(find -name include -type d) 		\
			$(find usr/ -name '*.[acho]' -o -name '*.la' \
			2>/dev/null); do
754
755
		if [ -e "$pkgdir/$i" ] || [ -L "$pkgdir/$i" ]; then
			d="$subpkgdir/${i%/*}"	# dirname $i
Natanael Copa's avatar
Natanael Copa committed
756
			mkdir -p "$d"
757
			mv "$pkgdir/$i" "$d"
758
			rmdir "$pkgdir/${i%/*}" 2>/dev/null
Natanael Copa's avatar
Natanael Copa committed
759
760
		fi
	done
761
762
763
764
765
766
767
	# move *.so links needed when linking the apps to -dev packages
	for i in lib/*.so usr/lib/*.so; do
		if [ -L "$i" ]; then
			mkdir -p "$subpkgdir"/"${i%/*}"
			mv "$i" "$subpkgdir/$i" || return 1
		fi
	done
Natanael Copa's avatar
Natanael Copa committed
768
	return 0
Natanael Copa's avatar
Natanael Copa committed
769
770
}

771
772
773
774
dev() {
	default_dev
}

775
776
777
778
is_function() {
	type "$1" 2>&1 | head -n 1 | egrep -q "is a (shell )?function"
}

Natanael Copa's avatar
Natanael Copa committed
779
780
# build and package in fakeroot
rootpkg() {
781
782
783
784
	local do_build=build
	cd "$startdir"
	if is_function package; then
		build || return 1
785
		do_build=package
786
	fi
Natanael Copa's avatar
Natanael Copa committed
787
	cd "$startdir"
788
	[ -n "$FAKEROOT" ] && msg "Entering fakeroot..."
789
	$FAKEROOT -- "$abuild_path" $color_opt $do_build \
790
791
		prepare_subpackages \
		prepare_package \
792
		create_apks
Natanael Copa's avatar
Natanael Copa committed
793
794
795
}

srcpkg() {
796
	getpkgver || return 1
797
	local p="$pkgname-$pkgver-$pkgrel"
Natanael Copa's avatar
Natanael Copa committed
798
	local prefix="${startdir##*/}"
Natanael Copa's avatar
Natanael Copa committed
799
800
801
802
803
	local i files="$prefix/APKBUILD"
	for i in $source; do
		files="$files $prefix/${i##*/}"
	done
	mkdir -p "$PKGDEST"
804
	msg "Creating source package $p.src.tar.gz..."
805
	(cd .. && tar -zcf "$PKGDEST/$p.src.tar.gz" $files) 
Natanael Copa's avatar
Natanael Copa committed
806
807
}

Natanael Copa's avatar
Natanael Copa committed
808
809
# return true if arch is supported or noarch
check_arch() {
Natanael Copa's avatar
Natanael Copa committed
810
	list_has $CARCH $arch || [ "$arch" = "noarch" ] || [ "$arch" = "all" ]
Natanael Copa's avatar
Natanael Copa committed
811
812
}

813
# check if package is up to date
814
apk_up2date() {
815
	getpkgver || return 1
816
	local pkg="$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk"
817
	local i s
Natanael Copa's avatar
Natanael Copa committed
818
	cd "$startdir"
819
820
821
822
823
	for i in $pkgname $subpackages; do
		[ -f "$PKGDEST/$pkgname-$pkgver-r$pkgrel.apk" ] || return 1
	done
	[ -n "$keep" ] && return 0

824
	for i in $source APKBUILD; do
825
826
827
828
829
830
		local s
		if is_remote "$i"; then
			s="$SRCDEST/${i##*/}"	# $(basename $i)
		else
			s="$startdir/${i##*/}"
		fi
831
832
833
834
835
836
837
		if [ "$s" -nt "$pkg" ]; then
			return 1
		fi
	done
	return 0
}

838
abuildindex_up2date() {
839
	local i apk
840
	getpkgver || return 1
841
842
	for i in $pkgname $subpackages; do
		apk="${i%:*}-$pkgver-r$pkgrel.apk"
843
		[ "$abuildrepo"/APKINDEX.tar.gz -nt "$abuildrepo"/$apk ] || return 1
844
845
846
847
848
	done
	return 0
}

up2date() {
Natanael Copa's avatar
Natanael Copa committed
849
	check_arch || return 0
850
851
852
853
854
855
856
	apk_up2date && abuildindex_up2date
}

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

859
860
861
862
863
864
865
# 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
	(
866
867
	aportsdir=$(realpath ${APKBUILD%/APKBUILD}/..)
	for i in $aportsdir/*/APKBUILD; do
868
869
870
871
		pkgname=
		subpackages=
		depends=
		makedepends=
872
		. $i
873
		dir=${i%/APKBUILD}
874
		deps=
875
		# filter out conflicts from deps and version info
876
877
878
879
		for j in $depends $makedepends; do
			case "$j" in
				!*) continue;;
			esac
880
			deps="$deps ${j%%[<>=]*}"
881
		done
882
		for j in $pkgname $subpackages; do
883
			echo "o ${j%%:*} $dir"
884
			set -- $deps
885
886
887
888
889
890
891
			echo -n "d ${j%%:*} $1"
			shift
			while [ $# -gt 0 ]; do
				echo -n ",$1"
				shift
			done
			echo
892
893
		done
	done
894
	)
895
896
}

897
deptrace() {
898
899
900
	local deps= i=
	# strip versions from deps
	for i in "$@"; do
901
		deps="$deps ${i%%[<>=]*}"
902
	done
903
	[ -z "$deps" ] && return 0
904
905
906
	( 	depparse_aports 
		if [ -z "$upgrade" ]; then
			# list installed pkgs and prefix with 'i '
907
			$APK info -q | sort |  sed 's/^/i /'
908
		fi
909
	) | awk -v pkgs="$deps" '
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934

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

940
941
942
	# add depends unless it is a subpackage or package itself
	for i in $BUILD_BASE $depends $makedepends; do
		[ "$pkgname" = "${i%%[<>=]*}" ] && continue
943
		subpackages_has ${i%%[<>=]*} || deps="$deps $i"
944
945
	done

946
	installed_deps=$($APK info -e $deps)
947
	# find which deps are missing
948
	for i in $deps; do
949
		if [ "${i#\!}" != "$i" ]; then
950
			$APK info -q -e "${i#\!}" \
951
				&& conflicts="$conflicts ${i#\!}"
952
		elif ! deplist_has $i $installed_deps || [ -n "$upgrade" ]; then
953
954
955
			missing="$missing $i"
		fi
	done
956
	
957
	if [ -n "$conflicts" ]; then
958
959
		error "Conflicting package(s) installed:$conflicts"
		return 1
960
961
962
963
964
965
966
967
	fi
	
	if [ -z "$install_deps" ] && [ -z "$recursive" ]; then
		# if we dont have any missing deps we are done now
		[ -z "$missing" ] && return 0
		error "Missing dependencies: $missing Use -r to autoinstall or -R to build"
		return 1
	fi
968

969
	uninstall_after=".makedepends-$pkgname $uninstall_after"
970
	if [ -n "$install_deps" ] && [ -z "$recursive" ]; then
971
972
		# make a --simluate run first to detect missing deps
		# apk-tools --virtual is no goot at reporting those.
973
		$SUDO $APK add --repository "$abuildrepo" \
974
975
			--wait 30 \
			--simulate --quiet $deps || return 1
976
		$SUDO $APK add --repository "$abuildrepo" \
977
			--wait 30 \
978
979
			--virtual .makedepends-$pkgname $deps \
			&& return 0
980
	fi
981
982
	
	[ -z "$recursive" ] && return 1
983
984
985

	# find dependencies that are installed but missing in repo.
	for i in $deps; do
986
		local m=$($APK search --repository "$abuildrepo" ${i%%[<>=]*})
Natanael Copa's avatar
Natanael Copa committed
987
		if [ -z "$m" ]; then
988
989
990
			missing="$missing $i"
		fi
	done
991
992
	
	for i in $(deptrace $missing); do
993
994
995
		# i = pkg:dir
		local dir=${i#*:}
		local pkg=${i%:*}
996
997
998
999
1000
1001
1002
1003
1004
1005

		# ignore if dependency is in other repo
		[ -d "$dir" ] || continue
		
		# break cricular deps
		list_has $pkg $ABUILD_VISITED && continue
		export ABUILD_VISITED="$ABUILD_VISITED $pkg"

		msg "Entering $dir"
		cd "$dir" && $0 $forceroot $keep $quiet $install_deps \
1006
			$recursive $upgrade $color_opt abuildindex || return 1
1007
	done
1008
	$SUDO $APK add -u --repository "$abuildrepo" \
1009
		--wait 30 \
1010
		--virtual .makedepends-$pkgname $deps
1011
1012
}

1013
# replace the md5sums in the APKBUILD
Natanael Copa's avatar
Natanael Copa committed
1014
1015
checksum() {
	local s files
1016
	[ -z "$source" ] && return 0
Natanael Copa's avatar
Natanael Copa committed
1017
	fetch
1018
	msg "Updating the md5sums in APKBUILD..."
Natanael Copa's avatar
Natanael Copa committed
1019
1020
1021
1022
	for s in $source; do
		files="$files ${s##*/}"
	done
	md5sums="$(cd "$srcdir" && md5sum $files)" || die "md5sum failed"
1023
1024
	sed -i -e '/^md5sums="/,/"\$/d; /^md5sums=''/,/''\$/d' "$APKBUILD"
	echo "md5sums=\"$md5sums\"" >>"$APKBUILD"
Natanael Copa's avatar
Natanael Copa committed
1025
1026
}

1027
stripbin() {
1028
	local bin
1029
1030
1031
	if options_has "!strip" || [ "$arch" = "noarch" ]; then
		return 0
	fi
1032
1033
	cd "${subpkgdir:-$pkgdir}" || return 1
	msg "Stripping binaries"
1034
1035
1036
	scanelf --recursive --nobanner --etype "ET_DYN,ET_EXEC" . \
		| sed -e 's:^ET_DYN ::' -e 's:^ET_EXEC ::' \
		| xargs -r strip
1037
1038
}

1039
1040
1041
# simply list target apks
listpkg() {
	local i 
1042
	getpkgver || return 1
1043
1044
1045
1046
1047
	for i in $pkgname $subpackages; do
		echo "${i%:*}-$pkgver-r$pkgrel.apk"
	done
}

Natanael Copa's avatar
Natanael Copa committed
1048
1049
1050
1051
1052
1053
1054
1055
source_has() {
	local i
	for i in $source; do
		[ "$1" = "${i##*/}" ] && return 0
	done
	return 1
}

Natanael Copa's avatar
abuild:    
Natanael Copa committed
1056
1057
1058
1059
1060
1061
1062
1063
subpackages_has() {
	local i
	for i in $subpackages; do
		[ "$1" = "${i%:*}" ] && return 0
	done
	return 1
}

1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
list_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		[ "$needle" = "$i" ] && return 0
		[ "$needle" = "!$i" ] && return 1
	done
	return 1
}

1075
1076
1077
1078
1079
1080
# same as list_has but we filter version info