source: mainline/tools/config.py@ 3c80f2b

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

Python coding style cleanup (no change in functionality)

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