source: mainline/tools/config.py@ 7aef7ee

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

use three-state logic for deduced defaults

  • Property mode set to 100755
File size: 10.4 KB
RevLine 
[98376de]1#!/usr/bin/env python
[44882c8]2#
[5a55ae6]3# Copyright (c) 2006 Ondrej Palkovsky
[9a0367f]4# Copyright (c) 2009 Martin Decky
[44882c8]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#
[98376de]30"""
[9a0367f]31HelenOS configuration system
[98376de]32"""
33import sys
34import os
35import re
36import commands
[27fb3d6]37import xtui
[98376de]38
[41f7564]39INPUT = sys.argv[1]
[84266669]40MAKEFILE = 'Makefile.config'
41MACROS = 'config.h'
42DEFS = 'config.defs'
[98376de]43
[9a0367f]44def read_defaults(fname, defaults):
45 "Read saved values from last configuration run"
46
[84266669]47 inf = file(fname, 'r')
[9a0367f]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()
[98376de]55
[9a0367f]56def check_condition(text, defaults, ask_names):
[81c8d54]57 "Check that the condition specified on input line is True (only CNF and DNF is supported)"
[9a0367f]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
[98376de]84
[9a0367f]85def check_inside(text, defaults, ctype):
[81c8d54]86 "Check for condition"
[9a0367f]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]
[7aef7ee]106 if (varval == '*'):
107 varval = 'y'
[9a0367f]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
[98376de]126
[9a0367f]127def 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()
[98376de]176
[9a0367f]177def yes_no(default):
178 "Return '*' if yes, ' ' if no"
179
180 if (default == 'y'):
181 return '*'
182
183 return ' '
[98376de]184
[27fb3d6]185def subchoice(screen, name, choices, default):
[9a0367f]186 "Return choice of choices"
187
[27fb3d6]188 maxkey = 0
189 for key, val in choices:
190 length = len(key)
191 if (length > maxkey):
192 maxkey = length
[9a0367f]193
194 options = []
[27fb3d6]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
[9a0367f]203
[27fb3d6]204 (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
[9a0367f]205
[27fb3d6]206 if (button == 'cancel'):
[9a0367f]207 return None
208
[27fb3d6]209 return choices[value][0]
[98376de]210
[9a0367f]211def check_choices(defaults, ask_names):
212 "Check whether all accessible variables have a default"
213
[27fb3d6]214 for varname, vartype, name, choices, cond in ask_names:
[9a0367f]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
[98376de]222
[84266669]223def create_output(mkname, mcname, dfname, defaults, ask_names):
[9a0367f]224 "Create output configuration"
225
[84266669]226 revision = commands.getoutput('svnversion . 2> /dev/null')
227 timestamp = commands.getoutput('date "+%Y-%m-%d %H:%M:%S"')
228
229 outmk = file(mkname, 'w')
230 outmc = file(mcname, 'w')
231 outdf = file(dfname, 'w')
232
233 outmk.write('#########################################\n')
234 outmk.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
235 outmk.write('#########################################\n\n')
[9a0367f]236
[84266669]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 =')
[9a0367f]245
[27fb3d6]246 for varname, vartype, name, choices, cond in ask_names:
[9a0367f]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]
[7aef7ee]254 if (default == '*'):
255 default = 'y'
[9a0367f]256
[84266669]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:
[06da55b]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))
[9a0367f]266
[84266669]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()
[98376de]278
279def main():
[9a0367f]280 defaults = {}
281 ask_names = []
282
283 # Parse configuration file
284 parse_config(INPUT, ask_names)
285
286 # Read defaults from previous run
[84266669]287 if os.path.exists(MAKEFILE):
288 read_defaults(MAKEFILE, defaults)
[9a0367f]289
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)):
[84266669]293 create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
[9a0367f]294 return 0
295
[48c3d50]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
[27fb3d6]302 screen = xtui.screen_init()
[9a0367f]303 try:
304 selname = None
305 while True:
306
[81c8d54]307 # Cancel out all defaults which have to be deduced
308 for varname, vartype, name, choices, cond in ask_names:
[7aef7ee]309 if ((vartype == 'y') and (defaults.has_key(varname)) and (defaults[varname] == '*')):
[81c8d54]310 defaults[varname] = None
311
[9a0367f]312 options = []
313 opt2row = {}
314 position = None
315 cnt = 0
[27fb3d6]316 for varname, vartype, name, choices, cond in ask_names:
[9a0367f]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):
[84266669]337 defaults[varname] = choices[0][0]
338 continue
[9a0367f]339
340 options.append(" %s [%s] --> " % (name, default))
[84266669]341 elif (vartype == 'y'):
[7aef7ee]342 defaults[varname] = '*'
[84266669]343 continue
[9a0367f]344 elif (vartype == 'y/n'):
345 if (default == None):
346 default = 'y'
347 defaults[varname] = default
348 options.append(" <%s> %s " % (yes_no(default), name))
349 elif (vartype == 'n/y'):
350 if (default == None):
351 default = 'n'
352 defaults[varname] = default
353 options.append(" <%s> %s " % (yes_no(default), name))
354 else:
355 raise RuntimeError("Unknown variable type: %s" % vartype)
356
[27fb3d6]357 opt2row[cnt] = (varname, vartype, name, choices)
[9a0367f]358
359 cnt += 1
360
[27fb3d6]361 (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
[9a0367f]362
[27fb3d6]363 if (button == 'cancel'):
[9a0367f]364 return 'Configuration canceled'
365
[27fb3d6]366 if (not opt2row.has_key(value)):
367 raise RuntimeError("Error selecting value: %s" % value)
368
369 (selname, seltype, name, choices) = opt2row[value]
[9a0367f]370
[27fb3d6]371 if (not defaults.has_key(selname)):
372 default = None
373 else:
374 default = defaults[selname]
[9a0367f]375
[27fb3d6]376 if (button == 'done'):
[9a0367f]377 if (check_choices(defaults, ask_names)):
378 break
379 else:
[27fb3d6]380 xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
[9a0367f]381 continue
382
383 if (seltype == 'choice'):
[27fb3d6]384 defaults[selname] = subchoice(screen, name, choices, default)
[9a0367f]385 elif ((seltype == 'y/n') or (seltype == 'n/y')):
386 if (defaults[selname] == 'y'):
387 defaults[selname] = 'n'
388 else:
389 defaults[selname] = 'y'
390 finally:
[27fb3d6]391 xtui.screen_done(screen)
[9a0367f]392
[84266669]393 create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
[9a0367f]394 return 0
[98376de]395
396if __name__ == '__main__':
[43a10c4]397 sys.exit(main())
Note: See TracBrowser for help on using the repository browser.