source: mainline/tools/config.py@ a248234

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a248234 was 04b29ca, checked in by Martin Decky <martin@…>, 16 years ago

interpret negative inference properly

  • Property mode set to 100755
File size: 12.0 KB
Line 
1#!/usr/bin/env python
2#
3# Copyright (c) 2006 Ondrej Palkovsky
4# Copyright (c) 2009 Martin Decky
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#
30"""
31HelenOS configuration system
32"""
33import sys
34import os
35import re
36import commands
37import xtui
38
39INPUT = sys.argv[1]
40MAKEFILE = 'Makefile.config'
41MACROS = 'config.h'
42DEFS = 'config.defs'
43PRECONF = 'defaults'
44
45def read_defaults(fname, defaults):
46 "Read saved values from last configuration run"
47
48 inf = file(fname, 'r')
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()
56
57def check_condition(text, defaults, ask_names):
58 "Check that the condition specified on input line is True (only CNF and DNF is supported)"
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
85
86def check_inside(text, defaults, ctype):
87 "Check for condition"
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]
107 if (varval == '*'):
108 varval = 'y'
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
127
128def 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()
177
178def yes_no(default):
179 "Return '*' if yes, ' ' if no"
180
181 if (default == 'y'):
182 return '*'
183
184 return ' '
185
186def subchoice(screen, name, choices, default):
187 "Return choice of choices"
188
189 maxkey = 0
190 for key, val in choices:
191 length = len(key)
192 if (length > maxkey):
193 maxkey = length
194
195 options = []
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
204
205 (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
206
207 if (button == 'cancel'):
208 return None
209
210 return choices[value][0]
211
212def check_choices(defaults, ask_names):
213 "Check whether all accessible variables have a default"
214
215 for varname, vartype, name, choices, cond in ask_names:
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
223
224def create_output(mkname, mcname, dfname, defaults, ask_names):
225 "Create output configuration"
226
227 revision = commands.getoutput('svnversion . 2> /dev/null')
228 timestamp = commands.getoutput('date "+%Y-%m-%d %H:%M:%S"')
229
230 outmk = file(mkname, 'w')
231 outmc = file(mcname, 'w')
232 outdf = file(dfname, 'w')
233
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
247 for varname, vartype, name, choices, cond in ask_names:
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]
255 if (default == '*'):
256 default = 'y'
257
258 outmk.write('# %s\n%s = %s\n\n' % (name, varname, default))
259
260 if ((vartype == "y") or (vartype == "n") 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:
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))
267
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()
279
280def sorted_dir(root):
281 list = os.listdir(root)
282 list.sort()
283 return list
284
285def 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
323def main():
324 defaults = {}
325 ask_names = []
326
327 # Parse configuration file
328 parse_config(INPUT, ask_names)
329
330 # Read defaults from previous run
331 if os.path.exists(MAKEFILE):
332 read_defaults(MAKEFILE, defaults)
333
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)):
337 create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
338 return 0
339
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
346 screen = xtui.screen_init()
347 try:
348 selname = None
349 position = None
350 while True:
351
352 # Cancel out all defaults which have to be deduced
353 for varname, vartype, name, choices, cond in ask_names:
354 if ((vartype == 'y') and (defaults.has_key(varname)) and (defaults[varname] == '*')):
355 defaults[varname] = None
356
357 options = []
358 opt2row = {}
359 cnt = 1
360
361 options.append(" --- Load preconfigured defaults ... ")
362
363 for varname, vartype, name, choices, cond in ask_names:
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):
384 defaults[varname] = choices[0][0]
385 continue
386
387 if (default == None):
388 options.append("? %s --> " % name)
389 else:
390 options.append(" %s [%s] --> " % (name, default))
391 elif (vartype == 'y'):
392 defaults[varname] = '*'
393 continue
394 elif (vartype == 'n'):
395 defaults[varname] = 'n'
396 continue
397 elif (vartype == 'y/n'):
398 if (default == None):
399 default = 'y'
400 defaults[varname] = default
401 options.append(" <%s> %s " % (yes_no(default), name))
402 elif (vartype == 'n/y'):
403 if (default == None):
404 default = 'n'
405 defaults[varname] = default
406 options.append(" <%s> %s " % (yes_no(default), name))
407 else:
408 raise RuntimeError("Unknown variable type: %s" % vartype)
409
410 opt2row[cnt] = (varname, vartype, name, choices)
411
412 cnt += 1
413
414 if (position >= options):
415 position = None
416
417 (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
418
419 if (button == 'cancel'):
420 return 'Configuration canceled'
421
422 if (button == 'done'):
423 if (check_choices(defaults, ask_names)):
424 break
425 else:
426 xtui.error_dialog(screen, 'Error', 'Some options have still undefined values. These options are marked with the "?" sign.')
427 continue
428
429 if (value == 0):
430 read_preconfigured(PRECONF, MAKEFILE, screen, defaults)
431 position = 1
432 continue
433
434 position = None
435 if (not opt2row.has_key(value)):
436 raise RuntimeError("Error selecting value: %s" % value)
437
438 (selname, seltype, name, choices) = opt2row[value]
439
440 if (not defaults.has_key(selname)):
441 default = None
442 else:
443 default = defaults[selname]
444
445 if (seltype == 'choice'):
446 defaults[selname] = subchoice(screen, name, choices, default)
447 elif ((seltype == 'y/n') or (seltype == 'n/y')):
448 if (defaults[selname] == 'y'):
449 defaults[selname] = 'n'
450 else:
451 defaults[selname] = 'y'
452 finally:
453 xtui.screen_done(screen)
454
455 create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
456 return 0
457
458if __name__ == '__main__':
459 sys.exit(main())
Note: See TracBrowser for help on using the repository browser.