Subversion Repositories HelenOS

Rev

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