Subversion Repositories HelenOS

Rev

Rev 3803 | Rev 3808 | 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
3804 decky 37
import xtui
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
 
3804 decky 181
def subchoice(screen, name, choices, default):
3803 decky 182
	"Return choice of choices"
183
 
3804 decky 184
	maxkey = 0
185
	for key, val in choices:
186
		length = len(key)
187
		if (length > maxkey):
188
			maxkey = length
3803 decky 189
 
190
	options = []
3804 decky 191
	position = None
192
	cnt = 0
193
	for key, val in choices:
194
		if ((default) and (key == default)):
195
			position = cnt
196
 
197
		options.append(" %-*s  %s " % (maxkey, key, val))
198
		cnt += 1
3803 decky 199
 
3804 decky 200
	(button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
3803 decky 201
 
3804 decky 202
	if (button == 'cancel'):
3803 decky 203
		return None
204
 
3804 decky 205
	return choices[value][0]
1802 decky 206
 
3803 decky 207
def check_choices(defaults, ask_names):
208
	"Check whether all accessible variables have a default"
209
 
3804 decky 210
	for varname, vartype, name, choices, cond in ask_names:
3803 decky 211
		if ((cond) and (not check_condition(cond, defaults, ask_names))):
212
			continue
213
 
214
		if (not defaults.has_key(varname)):
215
			return False
216
 
217
	return True
1802 decky 218
 
3803 decky 219
def create_output(fname, defaults, ask_names):
220
	"Create output configuration"
221
 
222
	outf = file(fname, 'w')
223
 
224
	outf.write('#########################################\n')
225
	outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
226
	outf.write('#########################################\n\n')
227
 
3804 decky 228
	for varname, vartype, name, choices, cond in ask_names:
3803 decky 229
		if ((cond) and (not check_condition(cond, defaults, ask_names))):
230
			continue
231
 
232
		if (not defaults.has_key(varname)):
233
			default = ''
234
		else:
235
			default = defaults[varname]
236
 
237
		outf.write('# %s\n%s = %s\n\n' % (name, varname, default))
238
 
239
	outf.write('REVISION = %s\n' % commands.getoutput('svnversion . 2> /dev/null'))
240
	outf.write('TIMESTAMP = %s\n' % commands.getoutput('date "+%Y-%m-%d %H:%M:%S"'))
241
	outf.close()
1802 decky 242
 
243
def main():
3803 decky 244
	defaults = {}
245
	ask_names = []
1802 decky 246
 
3803 decky 247
	# Parse configuration file
248
	parse_config(INPUT, ask_names)
1802 decky 249
 
3803 decky 250
	# Read defaults from previous run
251
	if os.path.exists(OUTPUT):
252
		read_defaults(OUTPUT, defaults)
1802 decky 253
 
3803 decky 254
	# Default mode: only check defaults and regenerate configuration
255
	if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
256
		if (check_choices(defaults, ask_names)):
257
			create_output(OUTPUT, defaults, ask_names)
258
			return 0
259
 
3804 decky 260
	screen = xtui.screen_init()
3803 decky 261
	try:
262
		selname = None
263
		while True:
264
 
265
			options = []
266
			opt2row = {}
267
			position = None
268
			cnt = 0
3804 decky 269
			for varname, vartype, name, choices, cond in ask_names:
3803 decky 270
 
271
				if ((cond) and (not check_condition(cond, defaults, ask_names))):
272
					continue
273
 
274
				if (varname == selname):
275
					position = cnt
276
 
277
				if (not defaults.has_key(varname)):
278
					default = None
279
				else:
280
					default = defaults[varname]
281
 
282
				if (vartype == 'choice'):
283
					# Check if the default is an acceptable value
284
					if ((default) and (not default in [choice[0] for choice in choices])):
285
						default = None
286
						defaults.pop(varname)
287
 
288
					# If there is just one option, use it
289
					if (len(choices) == 1):
290
						default = choices[0][0]
291
						defaults[varname] = default
292
 
293
					options.append("     %s [%s] --> " % (name, default))
294
				elif (vartype == 'y/n'):
295
					if (default == None):
296
						default = 'y'
297
						defaults[varname] = default
298
					options.append(" <%s> %s " % (yes_no(default), name))
299
				elif (vartype == 'n/y'):
300
					if (default == None):
301
						default = 'n'
302
						defaults[varname] = default
303
					options.append(" <%s> %s " % (yes_no(default), name))
304
				else:
305
					raise RuntimeError("Unknown variable type: %s" % vartype)
306
 
3804 decky 307
				opt2row[cnt] = (varname, vartype, name, choices)
3803 decky 308
 
309
				cnt += 1
310
 
3804 decky 311
			(button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
3803 decky 312
 
3804 decky 313
			if (button == 'cancel'):
3803 decky 314
				return 'Configuration canceled'
315
 
3804 decky 316
			if (not opt2row.has_key(value)):
317
				raise RuntimeError("Error selecting value: %s" % value)
3803 decky 318
 
3804 decky 319
			(selname, seltype, name, choices) = opt2row[value]
3803 decky 320
 
3804 decky 321
			if (not defaults.has_key(selname)):
322
					default = None
323
			else:
324
				default = defaults[selname]
325
 
326
			if (button == 'done'):
3803 decky 327
				if (check_choices(defaults, ask_names)):
328
					break
329
				else:
3804 decky 330
					xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
3803 decky 331
					continue
332
 
333
			if (seltype == 'choice'):
3804 decky 334
				defaults[selname] = subchoice(screen, name, choices, default)
3803 decky 335
			elif ((seltype == 'y/n') or (seltype == 'n/y')):
336
				if (defaults[selname] == 'y'):
337
					defaults[selname] = 'n'
338
				else:
339
					defaults[selname] = 'y'
340
	finally:
3804 decky 341
		xtui.screen_done(screen)
3803 decky 342
 
343
	create_output(OUTPUT, defaults, ask_names)
344
	return 0
1802 decky 345
 
346
if __name__ == '__main__':
3803 decky 347
    exit(main())