summaryrefslogtreecommitdiff
path: root/package/pkgmaker
blob: 34bd129e65ae403178e1904eeaff499ad2cda145 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
unset MAKEFLAGS
export MAKEFLAGS=s
cd "$(dirname "$0")"
export TOPDIR=$(realpath ..)
if gmake --help >/dev/null 2>&1; then
	export GMAKE=gmake
else
	export GMAKE=make
fi
GMAKE="$GMAKE --no-print-directory"
(( x_cols = (COLUMNS > 10) ? COLUMNS - 2 : 80 ))
typeset -L$x_cols pbar

# check for trailing whitespace
#grep -H '[[:space:]]$' */Makefile && print "Found trailing whitespace, please fix"

# build a cache of “ipkg package name” → “package conf option” for
# use with dependency resolution
rm -rf pkglist.d
mkdir pkglist.d
for dn in */Makefile; do
	dn=${dn%/*}
	pbar="Pass 1: $dn ..."
	print -nu2 "$pbar\r"
	cd $dn

	# ALL_PKGOPTS: all subpackage conf options
	# PKGNAME_*: subpackage (ipkg) package name, by subpackage option
	eval $($GMAKE dump="ALL_PKGOPTS \
	    \$(foreach x,\${ALL_PKGOPTS},PKGNAME_\${x})")
	cd ..

	if [[ -z $ALL_PKGOPTS ]]; then
		#print -u2 "Warning: $dn/Makefile contains no packages, skipped"
		continue
	fi

	for spcu in $ALL_PKGOPTS; do		# spcu: package option, ucase
		eval sppn=\$PKGNAME_$spcu	# sppn: subpackage (ipkg) name
		# once mksh R40 is out, use its new associative arrays here!
		print -r -- "$spcu" >pkglist.d/"$sppn"
	done
done

# build Config.in files and resolve dependencies
rm -f package_sections
rm -rf pkgconfigs.d
mkdir pkgconfigs.d
for dn in */Makefile; do
	dn=${dn%/*}
	pbar="Pass 2: $dn ..."
	print -nu2 "$pbar\r"
	mkdir pkgconfigs.d/$dn
	cd $dn

	# PKG_NAME: package name (directory, free-format)
	# PKG_FLAVOURS: all package flavours (boolean options), uppercase
	# PKG_CHOICES: all package choices (boolean options), uppercase
	# PKG_DESCR: package description (directory)
	# PKG_URL: package homepage
	# PKG_CXX: uppercase varname part to use for CFrustFrust checks
	# ALL_PKGOPTS: all subpackage conf options
	# PKGNAME_*: subpackage (ipkg) package name, by subpackage option
	# PKGDESC_*: subpackage description, by subpackage option
	# PKGDEPS_*: subpackage depends on ipkg packages, by subpkg option
	# PKGDFLT_*: subpackage 'default {:-n}', by subpackage option
	# CFLINE_*: one free-format Config.in line per subpackage option
	# PKGFD_*: flavour description, per package flavour option
	# PKG_{HOST,TARGET}_DEPENDS: add host or target dependencies
	eval $($GMAKE dump="PKG_NAME PKG_FLAVOURS PKG_CHOICES PKG_DESCR PKG_SECTION PKG_URL PKG_MULTI PKG_CXX \
	    ALL_PKGOPTS \$(foreach x,\${ALL_PKGOPTS},PKGNAME_\${x} \
	    PKGDESC_\${x} PKGDEPS_\${x} PKGDFLT_\${x} PKGSECT_\${x} CFLINE_\${x}) \
	    \$(foreach x,\${PKG_FLAVOURS},PKGFD_\${x}) \
	    \$(foreach x,\${PKG_FLAVOURS},PKGFS_\${x}) \
	    \$(foreach x,\${PKG_CHOICES},PKGCD_\${x}) \
	    \$(foreach x,\${PKG_CHOICES},PKGCS_\${x}) \
	    PKG_HOST_DEPENDS PKG_TARGET_DEPENDS")

	# dnu: directory name, uppercase, y/-+/_X/
	typeset -u dnu=${dn//-/_}
	dnu=${dnu//+/X}

	echo "pkgconfigs.d/$dn/Config.in ${PKG_SECTION:=none}" >>../package_sections
	echo "$dn/Config.in.manual ${PKG_SECTION:=none}" >>../package_sections

	# skip if we take care of this one manually
	[[ -s Config.in.manual ]] && { cd ..; continue; }

	exec 4>../pkgconfigs.d/$dn/Config.in

	# Handle master package (directory)
	print -u4 "config ADK_COMPILE_$dnu"
	if [[ -z $ALL_PKGOPTS ]]; then
		# pseudo package, does not produce an ipkg package
		ppnf=$PKG_NAME			# ppnf: pseudopkg name, filled
		if [[ -n $PKG_DESCR ]]; then
			while (( ${#ppnf} < 23 )); do
				ppnf=$ppnf.
			done
			ppnf="$ppnf $PKG_DESCR"
		fi
		print -u4 "\tprompt \"$ppnf\""
	fi
	print -u4 \\ttristate
	if [[ -n $ALL_PKGOPTS ]]; then
		# real (master) package, contains 1+ ipkg (sub)packages
		print -nu4 \\tdepends on
		sp=' '				# local sp: space (or ' || ')
		for spcu in $ALL_PKGOPTS; do	# spcu: package option, ucase
			if [[ -n $PKG_MULTI ]]; then
				if [[ $dnu != $spcu ]]; then
					print -nu4 "${sp}ADK_PACKAGE_$spcu"
					sp=' || '
				else
					print -nu4 "${sp}ADK_HAVE_DOT_CONFIG"
					sp=' || '
				fi
			else
				print -nu4 "${sp}ADK_PACKAGE_$spcu"
				sp=' || '
			fi
		done
		print -u4
	fi
	print -u4 \\tdefault n

	# Handle NOT/ONLY_FOR_PLATFORM alikes
	phd=					# phd: PKG_HOST_DEPENDS expand.
	if [[ -n $PKG_HOST_DEPENDS ]]; then
		phd='\tdepends on'
		if [[ $PKG_HOST_DEPENDS = *\!* ]]; then
			sp=' !'
		else
			sp=' '
		fi
		for x in $PKG_HOST_DEPENDS; do
			typeset -u x=${x#!}
			phd="$phd${sp}ADK_HOST_$x"
			if [[ $PKG_HOST_DEPENDS = *\!* ]]; then
				sp=' && !'
			else
				sp=' || '
			fi
		done
	fi
	ptd=					# ptd: PKG_TARGET_DEPENDS exp.
	if [[ -n $PKG_TARGET_DEPENDS ]]; then
		ptd='\tdepends on'
		sp=' '				# local sp: space (or ' || ')
		if [[ $PKG_TARGET_DEPENDS = *\!* ]]; then
			sp=' !'
		else
			sp=' '
		fi
		for x in $PKG_TARGET_DEPENDS; do
			typeset -l x=${x#!}
			#XXX cache this with mksh R40+
			found=0
			while read friendlyname sym; do
				[[ $friendlyname = $x ]] || continue
				found=1
				break
			done <../../target/target.lst
			if (( !found )); then
				print -u2 "$dn: Target '$x' not found!"
				exit 1
			fi
			ptd="$ptd${sp}$sym"
			if [[ $PKG_TARGET_DEPENDS = *\!* ]]; then
				sp=' && !'
			else
				sp=' || '
			fi
		done
	fi

	# Handle subpackages / multipackages
	for spcu in $ALL_PKGOPTS; do		# spcu: package option, ucase
		eval sppn=\$PKGNAME_$spcu	# sppn: subpackage (ipkg) name
		eval desc=\$PKGDESC_$spcu	# desc: subpackage description
		: ${desc:=$PKG_DESCR}		# take from main pkg if empty
		eval sect=\$PKGSECT_$spcu	# sect: subpackage section
		: ${sect:=$PKG_SECTION}		# take from main pkg if empty
		eval deps=\$PKGDEPS_$spcu	# deps: subpackage dependencies
		eval dflt=\$PKGDFLT_$spcu	# dflt: config 'default' opt.
		eval xline=\$CFLINE_$spcu	# xline: one free-format line

		echo "pkgconfigs.d/$dn/Config.in.$sppn $sect" >>../package_sections
		exec 4>../pkgconfigs.d/$dn/Config.in.$sppn
		h=4

		print -u$h config ADK_PACKAGE_$spcu
		spnf=$sppn			# spnf: subpackage name, filled
		if [[ -n ${desc:-$PKG_NAME} ]]; then
			while (( ${#spnf} < 23 )); do
				spnf=$spnf.
			done
			spnf="$spnf ${desc:-$PKG_NAME}"
		fi
		print -u$h "\tprompt \"$spnf\""
		print -u$h \\ttristate
		if [[ -n $PKG_MULTI ]]; then
			if [[ $spcu != $dnu ]]; then
				print -u$h "\tdepends on ADK_PACKAGE_$dnu"
			fi
		fi
		[[ -n $phd ]] && print -u$h "$phd"
		[[ -n $ptd ]] && print -u$h "$ptd"
		print -u$h "\tdefault ${dflt:-n}"
		for dep in $deps; do		# dep: ipkg name of one rundep.
			# skip dependencies on uclibc++ and libstdcxx iff
			# we produce these automatically
			[[ -n $PKG_CXX && $dep = @(uclibc++|libstdcxx) ]] && \
			    continue
			case $dep {
			(kmod-*)
				# produce dependency on kernel package
				# which have special name→sym mangling
				typeset -u udep=${dep//-/_}
				print -u$h "\tselect ADK_KPACKAGE_$udep"
				;;
			(*)
				# produce dependency on regular package
				# where the symbol is cached (see above)
				if [[ ! -f ../pkglist.d/"$dep" ]]; then
					print -u2 "Warning: $PKG_NAME: unreachable dependency '$dep'"
					continue
				fi
				print -u$h '\tselect' \
				    ADK_PACKAGE_$(<../pkglist.d/"$dep")
				;;
			}
		done
		print -u$h \\tselect ADK_COMPILE_$dnu
		[[ -n $xline ]] && print -u$h "\t$xline"
		if [[ -n $desc$PKG_URL ]]; then
			# produce (optional) help text
			print -u$h \\thelp
			[[ -n $desc ]] && print -u$h "\t  $desc"
			[[ -n $desc && -n $PKG_URL ]] && print -u$h '\t  '
			[[ -n $PKG_URL ]] && print -u$h "\t  WWW: $PKG_URL"
		fi
	done

	# Handle CFrustFrust library selection, if necessary
	[[ -n $PKG_CXX ]] && cat >&4 <<EOF

choice
prompt "C++ library to use"
depends on ADK_COMPILE_$dnu
default ADK_COMPILE_${PKG_CXX}_WITH_STDCXX if ADK_TARGET_LIB_GLIBC || ADK_TARGET_LIB_EGLIBC
default ADK_COMPILE_${PKG_CXX}_WITH_UCLIBCXX if ADK_TARGET_LIB_UCLIBC

config ADK_COMPILE_${PKG_CXX}_WITH_STDCXX
	bool "GNU C++ library"
	select ADK_PACKAGE_LIBSTDCXX

config ADK_COMPILE_${PKG_CXX}_WITH_UCLIBCXX
	bool "uClibc++ library"
	select ADK_PACKAGE_UCLIBCXX

endchoice
EOF

	[[ -n $PKG_CHOICES ]] && cat >&4 <<EOF

choice
prompt "Package flavour choice"
depends on ADK_COMPILE_$dnu
EOF
	# Handle choices
	for pfco in $PKG_CHOICES; do
		eval pfcd=\$PKGCD_$pfco
		eval pfcs=\$PKGCS_$pfco
		typeset -u pfcs=${pfcs#!}
		print
		print config ADK_PACKAGE_${dnu}_$pfco
		print "\tbool \"$pfcd\""
		print "\\tselect ADK_PACKAGE_${pfcs}"
	done >&4
	[[ -n $PKG_CHOICES ]] && cat >&4 <<EOF

endchoice
EOF

	# Handle flavours (per directory)
	for pfcu in $PKG_FLAVOURS; do		# pfcu: pkg flavour conf opt.
		eval pfd=\$PKGFD_$pfcu		# pfd: pkg flavour description
		eval pfs=\$PKGFS_$pfcu		# pfs: pkg flavour dependencies
		print
		print config ADK_PACKAGE_${dnu}_$pfcu
		print "\tbool \"${pfd:-$PKG_NAME -> flavour $pfcu}\""
		print \\tdefault n
		print \\tdepends on ADK_COMPILE_$dnu
		for pfso in $pfs; do
			typeset -u pfso=${pfso#!}
			print \\tselect ADK_PACKAGE_${pfso}
		done
		print \\thelp
		print "\t  flavour ADK_PACKAGE_${dnu}_$pfcu for $PKG_NAME"
	done >&4
	cd ..
done

# return good if given file exists and is non-empty
non_empty_file() {
	[[ -f "$1" ]] || return 1
	[[ -n "$(cat "$1")" ]] || return 1
	return 0
}

# print the verbose section name for a given section tag
lookup_section_string() {
	str="$(grep ^$1\  sections.lst | cut -d ' ' -f '2-')"
	[[ -n $str ]] && { echo $str; return; }
	echo $1
}

# print the first prompt's first word's value in a given Config.in file
get_first_prompt() {
	prompt="$(grep "prompt " $1 |head -1| sed -n 's/.*"\([^ \.]*\)[ \.].*"/\1/p')"
	[[ -n $prompt ]] && echo $prompt
}

# prepare Config.in list for sorting
while read config_in section; do
	pbar="Pass 3: $config_in ..."
	print -nu2 "$pbar\r"

	non_empty_file $config_in || continue
	prompt="$(get_first_prompt $config_in)"
	[[ -n $prompt ]] || continue
	echo "$prompt $config_in $(lookup_section_string $section)"
done <package_sections >package_section_list

# create the Config.in.auto from the sorted list from above
cursec=""
sort -k 3 -k 1 -f package_section_list | while read name file section; do
	if [[ $cursec != $section ]]; then
		[[ -n $cursec ]] && print "endmenu\n"

		print "menu \"$section\""
		cursec="$section"
	fi
	print "source \"package/$file\""
	print -u3 "source \"package/${file%.*}\""
done >Config.in.auto 3>Config.in.auto.pre
print "endmenu\n" >>Config.in.auto
grep pkgconfigs.d Config.in.auto.pre | sort | uniq > Config.in.auto.global
rm -f package_sections package_section_list Config.in.auto.pre