Subversion Repositories HelenOS

Rev

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