Subversion Repositories HelenOS

Rev

Rev 3881 | Rev 3916 | 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]
3881 decky 40
MAKEFILE = 'Makefile.config'
41
MACROS = 'config.h'
42
DEFS = 'config.defs'
1802 decky 43
 
3803 decky 44
def read_defaults(fname, defaults):
45
	"Read saved values from last configuration run"
46
 
3881 decky 47
	inf = file(fname, 'r')
3803 decky 48
 
49
	for line in inf:
50
		res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
51
		if (res):
52
			defaults[res.group(1)] = res.group(2)
53
 
54
	inf.close()
1802 decky 55
 
3803 decky 56
def check_condition(text, defaults, ask_names):
57
	"Check for condition"
58
 
59
	ctype = 'cnf'
60
 
61
	if ((')|' in text) or ('|(' in text)):
62
		ctype = 'dnf'
63
 
64
	if (ctype == 'cnf'):
65
		conds = text.split('&')
66
	else:
67
		conds = text.split('|')
68
 
69
	for cond in conds:
70
		if (cond.startswith('(')) and (cond.endswith(')')):
71
			cond = cond[1:-1]
72
 
73
		inside = check_inside(cond, defaults, ctype)
74
 
75
		if (ctype == 'cnf') and (not inside):
76
			return False
77
 
78
		if (ctype == 'dnf') and (inside):
79
			return True
80
 
81
	if (ctype == 'cnf'):
82
		return True
83
	return False
1802 decky 84
 
3803 decky 85
def check_inside(text, defaults, ctype):
86
	"Check that the condition specified on input line is True (only CNF is supported)"
87
 
88
	if (ctype == 'cnf'):
89
		conds = text.split('|')
90
	else:
91
		conds = text.split('&')
92
 
93
	for cond in conds:
94
		res = re.match(r'^(.*?)(!?=)(.*)$', cond)
95
		if (not res):
96
			raise RuntimeError("Invalid condition: %s" % cond)
97
 
98
		condname = res.group(1)
99
		oper = res.group(2)
100
		condval = res.group(3)
101
 
102
		if (not defaults.has_key(condname)):
103
			varval = ''
104
		else:
105
			varval = defaults[condname]
106
 
107
		if (ctype == 'cnf'):
108
			if (oper == '=') and (condval == varval):
109
				return True
110
 
111
			if (oper == '!=') and (condval != varval):
112
				return True
113
		else:
114
			if (oper == '=') and (condval != varval):
115
				return False
116
 
117
			if (oper == '!=') and (condval == varval):
118
				return False
119
 
120
	if (ctype == 'cnf'):
121
		return False
122
 
123
	return True
1802 decky 124
 
3803 decky 125
def parse_config(fname, ask_names):
126
	"Parse configuration file"
127
 
128
	inf = file(fname, 'r')
129
 
130
	name = ''
131
	choices = []
132
 
133
	for line in inf:
134
 
135
		if (line.startswith('!')):
136
			# Ask a question
137
			res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
138
 
139
			if (not res):
140
				raise RuntimeError("Weird line: %s" % line)
141
 
142
			cond = res.group(1)
143
			varname = res.group(2)
144
			vartype = res.group(3)
145
 
146
			ask_names.append((varname, vartype, name, choices, cond))
147
			name = ''
148
			choices = []
149
			continue
150
 
151
		if (line.startswith('@')):
152
			# Add new line into the 'choices' array
153
			res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
154
 
155
			if not res:
156
				raise RuntimeError("Bad line: %s" % line)
157
 
158
			choices.append((res.group(2), res.group(3)))
159
			continue
160
 
161
		if (line.startswith('%')):
162
			# Name of the option
163
			name = line[1:].strip()
164
			continue
165
 
166
		if ((line.startswith('#')) or (line == '\n')):
167
			# Comment or empty line
168
			continue
169
 
170
 
171
		raise RuntimeError("Unknown syntax: %s" % line)
172
 
173
	inf.close()
1802 decky 174
 
3803 decky 175
def yes_no(default):
176
	"Return '*' if yes, ' ' if no"
177
 
178
	if (default == 'y'):
179
		return '*'
180
 
181
	return ' '
1802 decky 182
 
3804 decky 183
def subchoice(screen, name, choices, default):
3803 decky 184
	"Return choice of choices"
185
 
3804 decky 186
	maxkey = 0
187
	for key, val in choices:
188
		length = len(key)
189
		if (length > maxkey):
190
			maxkey = length
3803 decky 191
 
192
	options = []
3804 decky 193
	position = None
194
	cnt = 0
195
	for key, val in choices:
196
		if ((default) and (key == default)):
197
			position = cnt
198
 
199
		options.append(" %-*s  %s " % (maxkey, key, val))
200
		cnt += 1
3803 decky 201
 
3804 decky 202
	(button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
3803 decky 203
 
3804 decky 204
	if (button == 'cancel'):
3803 decky 205
		return None
206
 
3804 decky 207
	return choices[value][0]
1802 decky 208
 
3803 decky 209
def check_choices(defaults, ask_names):
210
	"Check whether all accessible variables have a default"
211
 
3804 decky 212
	for varname, vartype, name, choices, cond in ask_names:
3803 decky 213
		if ((cond) and (not check_condition(cond, defaults, ask_names))):
214
			continue
215
 
216
		if (not defaults.has_key(varname)):
217
			return False
218
 
219
	return True
1802 decky 220
 
3881 decky 221
def create_output(mkname, mcname, dfname, defaults, ask_names):
3803 decky 222
	"Create output configuration"
223
 
3881 decky 224
	revision = commands.getoutput('svnversion . 2> /dev/null')
225
	timestamp = commands.getoutput('date "+%Y-%m-%d %H:%M:%S"')
3803 decky 226
 
3881 decky 227
	outmk = file(mkname, 'w')
228
	outmc = file(mcname, 'w')
229
	outdf = file(dfname, 'w')
3803 decky 230
 
3881 decky 231
	outmk.write('#########################################\n')
232
	outmk.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
233
	outmk.write('#########################################\n\n')
234
 
235
	outmc.write('/***************************************\n')
236
	outmc.write(' * AUTO-GENERATED FILE, DO NOT EDIT!!! *\n')
237
	outmc.write(' ***************************************/\n\n')
238
 
239
	outdf.write('#########################################\n')
240
	outdf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
241
	outdf.write('#########################################\n\n')
242
	outdf.write('CONFIG_DEFS =')
243
 
3804 decky 244
	for varname, vartype, name, choices, cond in ask_names:
3803 decky 245
		if ((cond) and (not check_condition(cond, defaults, ask_names))):
246
			continue
247
 
248
		if (not defaults.has_key(varname)):
249
			default = ''
250
		else:
251
			default = defaults[varname]
252
 
3881 decky 253
		outmk.write('# %s\n%s = %s\n\n' % (name, varname, default))
254
 
255
		if ((vartype == "y") or (vartype == "y/n") or (vartype == "n/y")):
256
			if (default == "y"):
257
				outmc.write('/* %s */\n#define %s\n\n' % (name, varname))
258
				outdf.write(' -D%s' % varname)
259
		else:
3909 decky 260
			outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, default, varname, default))
261
			outdf.write(' -D%s=%s -D%s_%s' % (varname, default, varname, default))
3803 decky 262
 
3881 decky 263
	outmk.write('REVISION = %s\n' % revision)
264
	outmk.write('TIMESTAMP = %s\n' % timestamp)
265
 
266
	outmc.write('#define REVISION %s\n' % revision)
267
	outmc.write('#define TIMESTAMP %s\n' % timestamp)
268
 
269
	outdf.write(' "-DREVISION=%s" "-DTIMESTAMP=%s"\n' % (revision, timestamp))
270
 
271
	outmk.close()
272
	outmc.close()
273
	outdf.close()
1802 decky 274
 
275
def main():
3803 decky 276
	defaults = {}
277
	ask_names = []
1802 decky 278
 
3803 decky 279
	# Parse configuration file
280
	parse_config(INPUT, ask_names)
1802 decky 281
 
3803 decky 282
	# Read defaults from previous run
3881 decky 283
	if os.path.exists(MAKEFILE):
284
		read_defaults(MAKEFILE, defaults)
1802 decky 285
 
3803 decky 286
	# Default mode: only check defaults and regenerate configuration
287
	if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
288
		if (check_choices(defaults, ask_names)):
3881 decky 289
			create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
3803 decky 290
			return 0
291
 
3808 decky 292
	# Check mode: only check defaults
293
	if ((len(sys.argv) >= 3) and (sys.argv[2] == 'check')):
294
		if (check_choices(defaults, ask_names)):
295
			return 0
296
		return 1
297
 
3804 decky 298
	screen = xtui.screen_init()
3803 decky 299
	try:
300
		selname = None
301
		while True:
302
 
303
			options = []
304
			opt2row = {}
305
			position = None
306
			cnt = 0
3804 decky 307
			for varname, vartype, name, choices, cond in ask_names:
3803 decky 308
 
309
				if ((cond) and (not check_condition(cond, defaults, ask_names))):
310
					continue
311
 
312
				if (varname == selname):
313
					position = cnt
314
 
315
				if (not defaults.has_key(varname)):
316
					default = None
317
				else:
318
					default = defaults[varname]
319
 
320
				if (vartype == 'choice'):
321
					# Check if the default is an acceptable value
322
					if ((default) and (not default in [choice[0] for choice in choices])):
323
						default = None
324
						defaults.pop(varname)
325
 
326
					# If there is just one option, use it
327
					if (len(choices) == 1):
3881 decky 328
						defaults[varname] = choices[0][0]
329
						continue
3803 decky 330
 
331
					options.append("     %s [%s] --> " % (name, default))
3881 decky 332
				elif (vartype == 'y'):
333
					defaults[varname] = 'y'
334
					continue
3803 decky 335
				elif (vartype == 'y/n'):
336
					if (default == None):
337
						default = 'y'
338
						defaults[varname] = default
339
					options.append(" <%s> %s " % (yes_no(default), name))
340
				elif (vartype == 'n/y'):
341
					if (default == None):
342
						default = 'n'
343
						defaults[varname] = default
344
					options.append(" <%s> %s " % (yes_no(default), name))
345
				else:
346
					raise RuntimeError("Unknown variable type: %s" % vartype)
347
 
3804 decky 348
				opt2row[cnt] = (varname, vartype, name, choices)
3803 decky 349
 
350
				cnt += 1
351
 
3804 decky 352
			(button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
3803 decky 353
 
3804 decky 354
			if (button == 'cancel'):
3803 decky 355
				return 'Configuration canceled'
356
 
3804 decky 357
			if (not opt2row.has_key(value)):
358
				raise RuntimeError("Error selecting value: %s" % value)
3803 decky 359
 
3804 decky 360
			(selname, seltype, name, choices) = opt2row[value]
3803 decky 361
 
3804 decky 362
			if (not defaults.has_key(selname)):
363
					default = None
364
			else:
365
				default = defaults[selname]
366
 
367
			if (button == 'done'):
3803 decky 368
				if (check_choices(defaults, ask_names)):
369
					break
370
				else:
3804 decky 371
					xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
3803 decky 372
					continue
373
 
374
			if (seltype == 'choice'):
3804 decky 375
				defaults[selname] = subchoice(screen, name, choices, default)
3803 decky 376
			elif ((seltype == 'y/n') or (seltype == 'n/y')):
377
				if (defaults[selname] == 'y'):
378
					defaults[selname] = 'n'
379
				else:
380
					defaults[selname] = 'y'
381
	finally:
3804 decky 382
		xtui.screen_done(screen)
3803 decky 383
 
3881 decky 384
	create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
3803 decky 385
	return 0
1802 decky 386
 
387
if __name__ == '__main__':
3821 decky 388
	sys.exit(main())