summaryrefslogtreecommitdiff
path: root/package/pkgmaker
blob: bb91dbe37614b6c02002fc708fc9013a82cc7284 (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
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

# 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
for dn in */Makefile; do
	dn=${dn%/*}
	# skip if we take care of this one manually
	[[ $dn != "base-files" ]] && [[ -s $dn/Config.in.manual ]] && continue
	pbar="Pass 2: $dn ..."
	print -nu2 "$pbar\r"
	cd $dn

	# PKG_NAME: package name (directory, free-format)
	# PKG_FLAVOURS: all package flavours (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_DESCR PKG_URL PKG_MULTI PKG_CXX \
	    ALL_PKGOPTS \$(foreach x,\${ALL_PKGOPTS},PKGNAME_\${x} \
	    PKGDESC_\${x} PKGDEPS_\${x} PKGDFLT_\${x} CFLINE_\${x}) \
	    \$(foreach x,\${PKG_FLAVOURS},PKGFD_\${x}) \
	    PKG_HOST_DEPENDS PKG_TARGET_DEPENDS")

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

	(	# fd 4 = Config.in; fd 5 = Config.in.lib; fd 6 = Config.in.kmod
	g5=0

	# 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} < 34 )); 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 deps=\$PKGDEPS_$spcu	# deps: subpackage dependencies
		eval dflt=\$PKGDFLT_$spcu	# dflt: config 'default' opt.
		eval xline=\$CFLINE_$spcu	# xline: one free-format line

		if [[ $spcu = LIB* ]]; then
			h=5			# divert to Config.in.lib
			(( g5++ )) && print -u5	# been here before
		elif [[ $spcu = KMOD* ]]; then
			h=6
			(( g6++ )) && print -u6
		else
			h=4			# divert to Config.in
			print -u4
		fi

		print -u$h config ADK_PACKAGE_$spcu
		spnf=$sppn			# spnf: subpackage name, filled
		if [[ -n ${desc:-$PKG_NAME} ]]; then
			while (( ${#spnf} < 34 )); 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)
				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

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

	) 4>Config.in 5>Config.in.lib 6>Config.in.kmod
	cd ..
done

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

# print the verbose section name for a given section tag
function lookup_section_string() {
	str="$(grep ^$1\  SECTIONS.list | 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
function get_first_prompt() {
	prompt="$(grep -m 1 "prompt " $1 | sed -n 's/.*"\([^ \.]*\)[ \.].*"/\1/p')"
	[[ -n $prompt ]] && echo $prompt
}

# collect packages along with their section and
# create a list of '<name> <path to config.in> <section string>' for later sorting
rm -f package_section_list
for dn in */Makefile; do
	dn=${dn%/*}
	pbar="Pass 3: $dn ..."
	print -nu2 "$pbar\r"

	cd $dn
	eval $($GMAKE dump="PKG_NAME PKG_SECTION")
	cd ..

	# ignore section kernel, these are included inside target/config
	[[ $PKG_SECTION = kernel ]] && continue

	PKG_SECTION=${PKG_SECTION:-none}

	has_config_in=false
	if non_empty_file $dn/Config.in; then
		prompt="$(get_first_prompt $dn/Config.in)"
		prompt="${prompt:-$PKG_NAME}"
		echo "$prompt $dn/Config.in $(lookup_section_string $PKG_SECTION)"
		has_config_in=true
	fi
	if non_empty_file $dn/Config.in.lib; then
		prompt="$(get_first_prompt $dn/Config.in.lib)"
		prompt="${prompt:-$PKG_NAME}"
		echo "$prompt $dn/Config.in.lib $(lookup_section_string libs)"
		has_config_in=true
	fi
	if non_empty_file $dn/Config.in.manual; then
		prompt="$(get_first_prompt $dn/Config.in.manual)"
		prompt="${prompt:-$PKG_NAME}"
		echo "$prompt $dn/Config.in.manual $(lookup_section_string $PKG_SECTION)"
		has_config_in=true
	fi
	$has_config_in || print -u2 "$dn: No Config.in file found?!"
done >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
	pbar="Pass 4: $name ..."
	print -nu2 "$pbar\r"

	if [[ $cursec != $section ]]; then
		[[ -n $cursec ]] && print "endmenu\n"

		print "menu \"$section\""
		cursec="$section"
	fi
	print "source \"package/$file\""
done >Config.in.auto
print "endmenu\n" >>Config.in.auto
rm -f package_section_list