Subversion Repositories HelenOS

Rev

Rev 3017 | Rev 3804 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1802 decky 1
#!/usr/bin/env python
2830 jermar 2
#
3017 decky 3
# Copyright (c) 2006 Ondrej Palkovsky
3803 decky 4
# Copyright (c) 2009 Martin Decky
2830 jermar 5
# All rights reserved.
6
#
7
# Redistribution and use in source and binary forms, with or without
8
# modification, are permitted provided that the following conditions
9
# are met:
10
#
11
# - Redistributions of source code must retain the above copyright
12
#   notice, this list of conditions and the following disclaimer.
13
# - Redistributions in binary form must reproduce the above copyright
14
#   notice, this list of conditions and the following disclaimer in the
15
#   documentation and/or other materials provided with the distribution.
16
# - The name of the author may not be used to endorse or promote products
17
#   derived from this software without specific prior written permission.
18
#
19
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
#
1802 decky 30
"""
3803 decky 31
HelenOS configuration system
1802 decky 32
"""
33
import sys
34
import os
35
import re
36
import commands
3803 decky 37
import snack
1802 decky 38
 
39
INPUT = sys.argv[1]
40
OUTPUT = 'Makefile.config'
41
 
3803 decky 42
def read_defaults(fname, defaults):
43
	"Read saved values from last configuration run"
44
 
45
	inf = file(fname,'r')
46
 
47
	for line in inf:
48
		res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
49
		if (res):
50
			defaults[res.group(1)] = res.group(2)
51
 
52
	inf.close()
1802 decky 53
 
3803 decky 54
def check_condition(text, defaults, ask_names):
55
	"Check for condition"
56
 
57
	ctype = 'cnf'
58
 
59
	if ((')|' in text) or ('|(' in text)):
60
		ctype = 'dnf'
61
 
62
	if (ctype == 'cnf'):
63
		conds = text.split('&')
64
	else:
65
		conds = text.split('|')
66
 
67
	for cond in conds:
68
		if (cond.startswith('(')) and (cond.endswith(')')):
69
			cond = cond[1:-1]
70
 
71
		inside = check_inside(cond, defaults, ctype)
72
 
73
		if (ctype == 'cnf') and (not inside):
74
			return False
75
 
76
		if (ctype == 'dnf') and (inside):
77
			return True
78
 
79
	if (ctype == 'cnf'):
80
		return True
81
	return False
1802 decky 82
 
3803 decky 83
def check_inside(text, defaults, ctype):
84
	"Check that the condition specified on input line is True (only CNF is supported)"
85
 
86
	if (ctype == 'cnf'):
87
		conds = text.split('|')
88
	else:
89
		conds = text.split('&')
90
 
91
	for cond in conds:
92
		res = re.match(r'^(.*?)(!?=)(.*)$', cond)
93
		if (not res):
94
			raise RuntimeError("Invalid condition: %s" % cond)
95
 
96
		condname = res.group(1)
97
		oper = res.group(2)
98
		condval = res.group(3)
99
 
100
		if (not defaults.has_key(condname)):
101
			varval = ''
102
		else:
103
			varval = defaults[condname]
104
 
105
		if (ctype == 'cnf'):
106
			if (oper == '=') and (condval == varval):
107
				return True
108
 
109
			if (oper == '!=') and (condval != varval):
110
				return True
111
		else:
112
			if (oper == '=') and (condval != varval):
113
				return False
114
 
115
			if (oper == '!=') and (condval == varval):
116
				return False
117
 
118
	if (ctype == 'cnf'):
119
		return False
120
 
121
	return True
1802 decky 122
 
3803 decky 123
def parse_config(fname, ask_names):
124
	"Parse configuration file"
125
 
126
	inf = file(fname, 'r')
127
 
128
	name = ''
129
	choices = []
130
 
131
	for line in inf:
132
 
133
		if (line.startswith('!')):
134
			# Ask a question
135
			res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
136
 
137
			if (not res):
138
				raise RuntimeError("Weird line: %s" % line)
139
 
140
			cond = res.group(1)
141
			varname = res.group(2)
142
			vartype = res.group(3)
143
 
144
			ask_names.append((varname, vartype, name, choices, cond))
145
			name = ''
146
			choices = []
147
			continue
148
 
149
		if (line.startswith('@')):
150
			# Add new line into the 'choices' array
151
			res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
152
 
153
			if not res:
154
				raise RuntimeError("Bad line: %s" % line)
155
 
156
			choices.append((res.group(2), res.group(3)))
157
			continue
158
 
159
		if (line.startswith('%')):
160
			# Name of the option
161
			name = line[1:].strip()
162
			continue
163
 
164
		if ((line.startswith('#')) or (line == '\n')):
165
			# Comment or empty line
166
			continue
167
 
168
 
169
		raise RuntimeError("Unknown syntax: %s" % line)
170
 
171
	inf.close()
1802 decky 172
 
3803 decky 173
def yes_no(default):
174
	"Return '*' if yes, ' ' if no"
175
 
176
	if (default == 'y'):
177
		return '*'
178
 
179
	return ' '
1802 decky 180
 
3803 decky 181
def subchoice(screen, name, choices):
182
	"Return choice of choices"
183
 
184
	maxlen = 0
185
	for choice in choices:
186
		length = len(choice[0])
187
		if (length > maxlen):
188
			maxlen = length
189
 
190
	options = []
191
	for choice in choices:
192
		options.append(" %-*s  %s " % (maxlen, choice[0], choice[1]))
193
 
194
	retval = snack.ListboxChoiceWindow(screen, name, 'Choose value', options)
195
 
196
	if (retval[0] == 'cancel'):
197
		return None
198
 
199
	return choices[retval[1]][0]
1802 decky 200
 
3803 decky 201
def check_choices(defaults, ask_names):
202
	"Check whether all accessible variables have a default"
203
 
204
	for row in ask_names:
205
		varname = row[0]
206
		cond = row[4]
207
 
208
		if ((cond) and (not check_condition(cond, defaults, ask_names))):
209
			continue
210
 
211
		if (not defaults.has_key(varname)):
212
			return False
213
 
214
	return True
1802 decky 215
 
3803 decky 216
def create_output(fname, defaults, ask_names):
217
	"Create output configuration"
218
 
219
	outf = file(fname, 'w')
220
 
221
	outf.write('#########################################\n')
222
	outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
223
	outf.write('#########################################\n\n')
224
 
225
	for row in ask_names:
226
		varname = row[0]
227
		name = row[2]
228
		cond = row[4]
229
 
230
		if ((cond) and (not check_condition(cond, defaults, ask_names))):
231
			continue
232
 
233
		if (not defaults.has_key(varname)):
234
			default = ''
235
		else:
236
			default = defaults[varname]
237
 
238
		outf.write('# %s\n%s = %s\n\n' % (name, varname, default))
239
 
240
	outf.write('REVISION = %s\n' % commands.getoutput('svnversion . 2> /dev/null'))
241
	outf.write('TIMESTAMP = %s\n' % commands.getoutput('date "+%Y-%m-%d %H:%M:%S"'))
242
	outf.close()
1802 decky 243
 
244
def main():
3803 decky 245
	defaults = {}
246
	ask_names = []
1802 decky 247
 
3803 decky 248
	# Parse configuration file
249
	parse_config(INPUT, ask_names)
1802 decky 250
 
3803 decky 251
	# Read defaults from previous run
252
	if os.path.exists(OUTPUT):
253
		read_defaults(OUTPUT, defaults)
1802 decky 254
 
3803 decky 255
	# Default mode: only check defaults and regenerate configuration
256
	if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
257
		if (check_choices(defaults, ask_names)):
258
			create_output(OUTPUT, defaults, ask_names)
259
			return 0
260
 
261
	screen = snack.SnackScreen()
262
	try:
263
		selname = None
264
		while True:
265
 
266
			options = []
267
			opt2row = {}
268
			position = None
269
			cnt = 0
270
			for row in ask_names:
271
 
272
				varname = row[0]
273
				vartype = row[1]
274
				name = row[2]
275
				choices = row[3]
276
				cond = row[4]
277
 
278
				if ((cond) and (not check_condition(cond, defaults, ask_names))):
279
					continue
280
 
281
				if (varname == selname):
282
					position = cnt
283
 
284
				if (not defaults.has_key(varname)):
285
					default = None
286
				else:
287
					default = defaults[varname]
288
 
289
				if (vartype == 'choice'):
290
					# Check if the default is an acceptable value
291
					if ((default) and (not default in [choice[0] for choice in choices])):
292
						default = None
293
						defaults.pop(varname)
294
 
295
					# If there is just one option, use it
296
					if (len(choices) == 1):
297
						default = choices[0][0]
298
						defaults[varname] = default
299
 
300
					options.append("     %s [%s] --> " % (name, default))
301
				elif (vartype == 'y/n'):
302
					if (default == None):
303
						default = 'y'
304
						defaults[varname] = default
305
					options.append(" <%s> %s " % (yes_no(default), name))
306
				elif (vartype == 'n/y'):
307
					if (default == None):
308
						default = 'n'
309
						defaults[varname] = default
310
					options.append(" <%s> %s " % (yes_no(default), name))
311
				else:
312
					raise RuntimeError("Unknown variable type: %s" % vartype)
313
 
314
				opt2row[cnt] = row
315
 
316
				cnt += 1
317
 
318
			retval = snack.ListboxChoiceWindow(screen, 'HelenOS configuration', 'Choose configuration option', options, default = position)
319
 
320
			if (retval[0] == 'cancel'):
321
				return 'Configuration canceled'
322
 
323
			row = opt2row[retval[1]]
324
			if (row == None):
325
				raise RuntimeError("Error selecting value: %s" % retval[1])
326
 
327
			selname = row[0]
328
			seltype = row[1]
329
			name = row[2]
330
			choices = row[3]
331
 
332
			if (retval[0] == 'ok'):
333
				if (check_choices(defaults, ask_names)):
334
					break
335
				else:
336
					snack.ButtonChoiceWindow(screen, 'Error', 'Some options have still undefined values.', ['Ok']);
337
					continue
338
 
339
			if (seltype == 'choice'):
340
				defaults[selname] = subchoice(screen, name, choices)
341
			elif ((seltype == 'y/n') or (seltype == 'n/y')):
342
				if (defaults[selname] == 'y'):
343
					defaults[selname] = 'n'
344
				else:
345
					defaults[selname] = 'y'
346
	finally:
347
		screen.finish()
348
 
349
	create_output(OUTPUT, defaults, ask_names)
350
	return 0
1802 decky 351
 
352
if __name__ == '__main__':
3803 decky 353
    exit(main())