Subversion Repositories HelenOS

Rev

Rev 3808 | Rev 3881 | 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
 
3808 decky 260
	# Check mode: only check defaults
261
	if ((len(sys.argv) >= 3) and (sys.argv[2] == 'check')):
262
		if (check_choices(defaults, ask_names)):
263
			return 0
264
		return 1
265
 
3804 decky 266
	screen = xtui.screen_init()
3803 decky 267
	try:
268
		selname = None
269
		while True:
270
 
271
			options = []
272
			opt2row = {}
273
			position = None
274
			cnt = 0
3804 decky 275
			for varname, vartype, name, choices, cond in ask_names:
3803 decky 276
 
277
				if ((cond) and (not check_condition(cond, defaults, ask_names))):
278
					continue
279
 
280
				if (varname == selname):
281
					position = cnt
282
 
283
				if (not defaults.has_key(varname)):
284
					default = None
285
				else:
286
					default = defaults[varname]
287
 
288
				if (vartype == 'choice'):
289
					# Check if the default is an acceptable value
290
					if ((default) and (not default in [choice[0] for choice in choices])):
291
						default = None
292
						defaults.pop(varname)
293
 
294
					# If there is just one option, use it
295
					if (len(choices) == 1):
296
						default = choices[0][0]
297
						defaults[varname] = default
298
 
299
					options.append("     %s [%s] --> " % (name, default))
300
				elif (vartype == 'y/n'):
301
					if (default == None):
302
						default = 'y'
303
						defaults[varname] = default
304
					options.append(" <%s> %s " % (yes_no(default), name))
305
				elif (vartype == 'n/y'):
306
					if (default == None):
307
						default = 'n'
308
						defaults[varname] = default
309
					options.append(" <%s> %s " % (yes_no(default), name))
310
				else:
311
					raise RuntimeError("Unknown variable type: %s" % vartype)
312
 
3804 decky 313
				opt2row[cnt] = (varname, vartype, name, choices)
3803 decky 314
 
315
				cnt += 1
316
 
3804 decky 317
			(button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
3803 decky 318
 
3804 decky 319
			if (button == 'cancel'):
3803 decky 320
				return 'Configuration canceled'
321
 
3804 decky 322
			if (not opt2row.has_key(value)):
323
				raise RuntimeError("Error selecting value: %s" % value)
3803 decky 324
 
3804 decky 325
			(selname, seltype, name, choices) = opt2row[value]
3803 decky 326
 
3804 decky 327
			if (not defaults.has_key(selname)):
328
					default = None
329
			else:
330
				default = defaults[selname]
331
 
332
			if (button == 'done'):
3803 decky 333
				if (check_choices(defaults, ask_names)):
334
					break
335
				else:
3804 decky 336
					xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
3803 decky 337
					continue
338
 
339
			if (seltype == 'choice'):
3804 decky 340
				defaults[selname] = subchoice(screen, name, choices, default)
3803 decky 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:
3804 decky 347
		xtui.screen_done(screen)
3803 decky 348
 
349
	create_output(OUTPUT, defaults, ask_names)
350
	return 0
1802 decky 351
 
352
if __name__ == '__main__':
3821 decky 353
	sys.exit(main())