source: mainline/tools/config.py@ 81c8d54

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

fix a flaw in deduction algorithm

  • Property mode set to 100755
File size: 10.2 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'
43
44def read_defaults(fname, defaults):
45 "Read saved values from last configuration run"
46
47 inf = file(fname, 'r')
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()
55
56def check_condition(text, defaults, ask_names):
57 "Check that the condition specified on input line is True (only CNF and DNF is supported)"
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
84
85def check_inside(text, defaults, ctype):
86 "Check for condition"
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
124
125def 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()
174
175def yes_no(default):
176 "Return '*' if yes, ' ' if no"
177
178 if (default == 'y'):
179 return '*'
180
181 return ' '
182
183def subchoice(screen, name, choices, default):
184 "Return choice of choices"
185
186 maxkey = 0
187 for key, val in choices:
188 length = len(key)
189 if (length > maxkey):
190 maxkey = length
191
192 options = []
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
201
202 (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
203
204 if (button == 'cancel'):
205 return None
206
207 return choices[value][0]
208
209def check_choices(defaults, ask_names):
210 "Check whether all accessible variables have a default"
211
212 for varname, vartype, name, choices, cond in ask_names:
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
220
221def create_output(mkname, mcname, dfname, defaults, ask_names):
222 "Create output configuration"
223
224 revision = commands.getoutput('svnversion . 2> /dev/null')
225 timestamp = commands.getoutput('date "+%Y-%m-%d %H:%M:%S"')
226
227 outmk = file(mkname, 'w')
228 outmc = file(mcname, 'w')
229 outdf = file(dfname, 'w')
230
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
244 for varname, vartype, name, choices, cond in ask_names:
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
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:
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))
262
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()
274
275def main():
276 defaults = {}
277 ask_names = []
278
279 # Parse configuration file
280 parse_config(INPUT, ask_names)
281
282 # Read defaults from previous run
283 if os.path.exists(MAKEFILE):
284 read_defaults(MAKEFILE, defaults)
285
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)):
289 create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
290 return 0
291
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
298 screen = xtui.screen_init()
299 try:
300 selname = None
301 while True:
302
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
308 options = []
309 opt2row = {}
310 position = None
311 cnt = 0
312 for varname, vartype, name, choices, cond in ask_names:
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):
333 defaults[varname] = choices[0][0]
334 continue
335
336 options.append(" %s [%s] --> " % (name, default))
337 elif (vartype == 'y'):
338 defaults[varname] = 'y'
339 continue
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
353 opt2row[cnt] = (varname, vartype, name, choices)
354
355 cnt += 1
356
357 (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
358
359 if (button == 'cancel'):
360 return 'Configuration canceled'
361
362 if (not opt2row.has_key(value)):
363 raise RuntimeError("Error selecting value: %s" % value)
364
365 (selname, seltype, name, choices) = opt2row[value]
366
367 if (not defaults.has_key(selname)):
368 default = None
369 else:
370 default = defaults[selname]
371
372 if (button == 'done'):
373 if (check_choices(defaults, ask_names)):
374 break
375 else:
376 xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
377 continue
378
379 if (seltype == 'choice'):
380 defaults[selname] = subchoice(screen, name, choices, default)
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:
387 xtui.screen_done(screen)
388
389 create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
390 return 0
391
392if __name__ == '__main__':
393 sys.exit(main())
Note: See TracBrowser for help on using the repository browser.