source: mainline/tools/config.py@ 9a0367f

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

configuration system overhaul (requires newt)

  • Property mode set to 100755
File size: 8.6 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
[9a0367f]37import snack
[98376de]38
[41f7564]39INPUT = sys.argv[1]
[98376de]40OUTPUT = 'Makefile.config'
41
[9a0367f]42def read_defaults(fname, defaults):
43 "Read saved values from last configuration run"
44
45 inf = file(fname,'r')
46
47 for line in inf:
48 res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
49 if (res):
50 defaults[res.group(1)] = res.group(2)
51
52 inf.close()
[98376de]53
[9a0367f]54def check_condition(text, defaults, ask_names):
55 "Check for condition"
56
57 ctype = 'cnf'
58
59 if ((')|' in text) or ('|(' in text)):
60 ctype = 'dnf'
61
62 if (ctype == 'cnf'):
63 conds = text.split('&')
64 else:
65 conds = text.split('|')
66
67 for cond in conds:
68 if (cond.startswith('(')) and (cond.endswith(')')):
69 cond = cond[1:-1]
70
71 inside = check_inside(cond, defaults, ctype)
72
73 if (ctype == 'cnf') and (not inside):
74 return False
75
76 if (ctype == 'dnf') and (inside):
77 return True
78
79 if (ctype == 'cnf'):
80 return True
81 return False
[98376de]82
[9a0367f]83def check_inside(text, defaults, ctype):
84 "Check that the condition specified on input line is True (only CNF is supported)"
85
86 if (ctype == 'cnf'):
87 conds = text.split('|')
88 else:
89 conds = text.split('&')
90
91 for cond in conds:
92 res = re.match(r'^(.*?)(!?=)(.*)$', cond)
93 if (not res):
94 raise RuntimeError("Invalid condition: %s" % cond)
95
96 condname = res.group(1)
97 oper = res.group(2)
98 condval = res.group(3)
99
100 if (not defaults.has_key(condname)):
101 varval = ''
102 else:
103 varval = defaults[condname]
104
105 if (ctype == 'cnf'):
106 if (oper == '=') and (condval == varval):
107 return True
108
109 if (oper == '!=') and (condval != varval):
110 return True
111 else:
112 if (oper == '=') and (condval != varval):
113 return False
114
115 if (oper == '!=') and (condval == varval):
116 return False
117
118 if (ctype == 'cnf'):
119 return False
120
121 return True
[98376de]122
[9a0367f]123def parse_config(fname, ask_names):
124 "Parse configuration file"
125
126 inf = file(fname, 'r')
127
128 name = ''
129 choices = []
130
131 for line in inf:
132
133 if (line.startswith('!')):
134 # Ask a question
135 res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
136
137 if (not res):
138 raise RuntimeError("Weird line: %s" % line)
139
140 cond = res.group(1)
141 varname = res.group(2)
142 vartype = res.group(3)
143
144 ask_names.append((varname, vartype, name, choices, cond))
145 name = ''
146 choices = []
147 continue
148
149 if (line.startswith('@')):
150 # Add new line into the 'choices' array
151 res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
152
153 if not res:
154 raise RuntimeError("Bad line: %s" % line)
155
156 choices.append((res.group(2), res.group(3)))
157 continue
158
159 if (line.startswith('%')):
160 # Name of the option
161 name = line[1:].strip()
162 continue
163
164 if ((line.startswith('#')) or (line == '\n')):
165 # Comment or empty line
166 continue
167
168
169 raise RuntimeError("Unknown syntax: %s" % line)
170
171 inf.close()
[98376de]172
[9a0367f]173def yes_no(default):
174 "Return '*' if yes, ' ' if no"
175
176 if (default == 'y'):
177 return '*'
178
179 return ' '
[98376de]180
[9a0367f]181def subchoice(screen, name, choices):
182 "Return choice of choices"
183
184 maxlen = 0
185 for choice in choices:
186 length = len(choice[0])
187 if (length > maxlen):
188 maxlen = length
189
190 options = []
191 for choice in choices:
192 options.append(" %-*s %s " % (maxlen, choice[0], choice[1]))
193
194 retval = snack.ListboxChoiceWindow(screen, name, 'Choose value', options)
195
196 if (retval[0] == 'cancel'):
197 return None
198
199 return choices[retval[1]][0]
[98376de]200
[9a0367f]201def check_choices(defaults, ask_names):
202 "Check whether all accessible variables have a default"
203
204 for row in ask_names:
205 varname = row[0]
206 cond = row[4]
207
208 if ((cond) and (not check_condition(cond, defaults, ask_names))):
209 continue
210
211 if (not defaults.has_key(varname)):
212 return False
213
214 return True
[98376de]215
[9a0367f]216def create_output(fname, defaults, ask_names):
217 "Create output configuration"
218
219 outf = file(fname, 'w')
220
221 outf.write('#########################################\n')
222 outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
223 outf.write('#########################################\n\n')
224
225 for row in ask_names:
226 varname = row[0]
227 name = row[2]
228 cond = row[4]
229
230 if ((cond) and (not check_condition(cond, defaults, ask_names))):
231 continue
232
233 if (not defaults.has_key(varname)):
234 default = ''
235 else:
236 default = defaults[varname]
237
238 outf.write('# %s\n%s = %s\n\n' % (name, varname, default))
239
240 outf.write('REVISION = %s\n' % commands.getoutput('svnversion . 2> /dev/null'))
241 outf.write('TIMESTAMP = %s\n' % commands.getoutput('date "+%Y-%m-%d %H:%M:%S"'))
242 outf.close()
[98376de]243
244def main():
[9a0367f]245 defaults = {}
246 ask_names = []
247
248 # Parse configuration file
249 parse_config(INPUT, ask_names)
250
251 # Read defaults from previous run
252 if os.path.exists(OUTPUT):
253 read_defaults(OUTPUT, defaults)
254
255 # Default mode: only check defaults and regenerate configuration
256 if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
257 if (check_choices(defaults, ask_names)):
258 create_output(OUTPUT, defaults, ask_names)
259 return 0
260
261 screen = snack.SnackScreen()
262 try:
263 selname = None
264 while True:
265
266 options = []
267 opt2row = {}
268 position = None
269 cnt = 0
270 for row in ask_names:
271
272 varname = row[0]
273 vartype = row[1]
274 name = row[2]
275 choices = row[3]
276 cond = row[4]
277
278 if ((cond) and (not check_condition(cond, defaults, ask_names))):
279 continue
280
281 if (varname == selname):
282 position = cnt
283
284 if (not defaults.has_key(varname)):
285 default = None
286 else:
287 default = defaults[varname]
288
289 if (vartype == 'choice'):
290 # Check if the default is an acceptable value
291 if ((default) and (not default in [choice[0] for choice in choices])):
292 default = None
293 defaults.pop(varname)
294
295 # If there is just one option, use it
296 if (len(choices) == 1):
297 default = choices[0][0]
298 defaults[varname] = default
299
300 options.append(" %s [%s] --> " % (name, default))
301 elif (vartype == 'y/n'):
302 if (default == None):
303 default = 'y'
304 defaults[varname] = default
305 options.append(" <%s> %s " % (yes_no(default), name))
306 elif (vartype == 'n/y'):
307 if (default == None):
308 default = 'n'
309 defaults[varname] = default
310 options.append(" <%s> %s " % (yes_no(default), name))
311 else:
312 raise RuntimeError("Unknown variable type: %s" % vartype)
313
314 opt2row[cnt] = row
315
316 cnt += 1
317
318 retval = snack.ListboxChoiceWindow(screen, 'HelenOS configuration', 'Choose configuration option', options, default = position)
319
320 if (retval[0] == 'cancel'):
321 return 'Configuration canceled'
322
323 row = opt2row[retval[1]]
324 if (row == None):
325 raise RuntimeError("Error selecting value: %s" % retval[1])
326
327 selname = row[0]
328 seltype = row[1]
329 name = row[2]
330 choices = row[3]
331
332 if (retval[0] == 'ok'):
333 if (check_choices(defaults, ask_names)):
334 break
335 else:
336 snack.ButtonChoiceWindow(screen, 'Error', 'Some options have still undefined values.', ['Ok']);
337 continue
338
339 if (seltype == 'choice'):
340 defaults[selname] = subchoice(screen, name, choices)
341 elif ((seltype == 'y/n') or (seltype == 'n/y')):
342 if (defaults[selname] == 'y'):
343 defaults[selname] = 'n'
344 else:
345 defaults[selname] = 'y'
346 finally:
347 screen.finish()
348
349 create_output(OUTPUT, defaults, ask_names)
350 return 0
[98376de]351
352if __name__ == '__main__':
[9a0367f]353 exit(main())
Note: See TracBrowser for help on using the repository browser.