Subversion Repositories HelenOS

Rev

Rev 3909 | Rev 3937 | 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]
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
 
3916 decky 303
			# Cancel out all defaults which have to be deduced
304
			for varname, vartype, name, choices, cond in ask_names:
305
				if (vartype == 'y'):
306
					defaults[varname] = None
307
 
3803 decky 308
			options = []
309
			opt2row = {}
310
			position = None
311
			cnt = 0
3804 decky 312
			for varname, vartype, name, choices, cond in ask_names:
3803 decky 313
 
314
				if ((cond) and (not check_condition(cond, defaults, ask_names))):
315
					continue
316
 
317
				if (varname == selname):
318
					position = cnt
319
 
320
				if (not defaults.has_key(varname)):
321
					default = None
322
				else:
323
					default = defaults[varname]
324
 
325
				if (vartype == 'choice'):
326
					# Check if the default is an acceptable value
327
					if ((default) and (not default in [choice[0] for choice in choices])):
328
						default = None
329
						defaults.pop(varname)
330
 
331
					# If there is just one option, use it
332
					if (len(choices) == 1):
3881 decky 333
						defaults[varname] = choices[0][0]
334
						continue
3803 decky 335
 
336
					options.append("     %s [%s] --> " % (name, default))
3881 decky 337
				elif (vartype == 'y'):
338
					defaults[varname] = 'y'
339
					continue
3803 decky 340
				elif (vartype == 'y/n'):
341
					if (default == None):
342
						default = 'y'
343
						defaults[varname] = default
344
					options.append(" <%s> %s " % (yes_no(default), name))
345
				elif (vartype == 'n/y'):
346
					if (default == None):
347
						default = 'n'
348
						defaults[varname] = default
349
					options.append(" <%s> %s " % (yes_no(default), name))
350
				else:
351
					raise RuntimeError("Unknown variable type: %s" % vartype)
352
 
3804 decky 353
				opt2row[cnt] = (varname, vartype, name, choices)
3803 decky 354
 
355
				cnt += 1
356
 
3804 decky 357
			(button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
3803 decky 358
 
3804 decky 359
			if (button == 'cancel'):
3803 decky 360
				return 'Configuration canceled'
361
 
3804 decky 362
			if (not opt2row.has_key(value)):
363
				raise RuntimeError("Error selecting value: %s" % value)
3803 decky 364
 
3804 decky 365
			(selname, seltype, name, choices) = opt2row[value]
3803 decky 366
 
3804 decky 367
			if (not defaults.has_key(selname)):
368
					default = None
369
			else:
370
				default = defaults[selname]
371
 
372
			if (button == 'done'):
3803 decky 373
				if (check_choices(defaults, ask_names)):
374
					break
375
				else:
3804 decky 376
					xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
3803 decky 377
					continue
378
 
379
			if (seltype == 'choice'):
3804 decky 380
				defaults[selname] = subchoice(screen, name, choices, default)
3803 decky 381
			elif ((seltype == 'y/n') or (seltype == 'n/y')):
382
				if (defaults[selname] == 'y'):
383
					defaults[selname] = 'n'
384
				else:
385
					defaults[selname] = 'y'
386
	finally:
3804 decky 387
		xtui.screen_done(screen)
3803 decky 388
 
3881 decky 389
	create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
3803 decky 390
	return 0
1802 decky 391
 
392
if __name__ == '__main__':
3821 decky 393
	sys.exit(main())