source: mainline/tools/config.py@ 45ab770

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 45ab770 was 45ab770, checked in by Ondrej Palkovsky <ondrap@…>, 20 years ago

Added cancel button

  • Property mode set to 100755
File size: 13.1 KB
Line 
1#!/usr/bin/env python
2"""
3Kernel configuration script
4"""
5import sys
6import os
7import re
8
9INPUT = 'kernel.config'
10OUTPUT = 'Makefile.config'
11TMPOUTPUT = 'Makefile.config.tmp'
12
13class DefaultDialog:
14 "Wrapper dialog that tries to return default values"
15 def __init__(self, dlg):
16 self.dlg = dlg
17
18 def set_title(self,text):
19 self.dlg.set_title(text)
20
21 def yesno(self, text, default=None):
22 if default is not None:
23 return default
24 return self.dlg.yesno(text, default)
25 def noyes(self, text, default=None):
26 if default is not None:
27 return default
28 return self.dlg.noyes(text, default)
29
30 def choice(self, text, choices, defopt=None):
31 if defopt is not None:
32 return choices[defopt][0]
33 return self.dlg.choice(text, choices, defopt)
34
35class NoDialog:
36 def __init__(self):
37 self.printed = None
38 self.title = 'HelenOS Configuration'
39
40 def print_title(self):
41 if not self.printed:
42 sys.stdout.write("\n*** %s ***\n" % self.title)
43 self.printed = True
44
45 def set_title(self, text):
46 self.title = text
47 self.printed = False
48
49 def noyes(self, text, default=None):
50 if not default:
51 default = 'n'
52 return self.yesno(text, default)
53
54 def yesno(self, text, default=None):
55 self.print_title()
56
57 if default != 'n':
58 default = 'y'
59 while 1:
60 sys.stdout.write("%s (y/n)[%s]: " % (text,default))
61 inp = sys.stdin.readline()
62 if not inp:
63 raise EOFError
64 inp = inp.strip().lower()
65 if not inp:
66 return default
67 if inp == 'y':
68 return 'y'
69 elif inp == 'n':
70 return 'n'
71
72 def _print_choice(self, text, choices, defopt):
73 sys.stdout.write('%s:\n' % text)
74 for i,(text,descr) in enumerate(choices):
75 sys.stdout.write('\t%2d. %s\n' % (i, descr))
76 if defopt is not None:
77 sys.stdout.write('Enter choice number[%d]: ' % defopt)
78 else:
79 sys.stdout.write('Enter choice number: ')
80
81 def menu(self, text, choices, button, defopt=None):
82 menu = []
83 for key, descr in choices:
84 txt = key + (45-len(key))*' ' + ': ' + descr
85 menu.append((key, txt))
86
87 return self.choice(text, [button] + menu)
88
89 def choice(self, text, choices, defopt=None):
90 self.print_title()
91 while 1:
92 self._print_choice(text, choices, defopt)
93 inp = sys.stdin.readline()
94 if not inp:
95 raise EOFError
96 if not inp.strip():
97 if defopt is not None:
98 return choices[defopt][0]
99 continue
100 try:
101 number = int(inp.strip())
102 except ValueError:
103 continue
104 if number < 0 or number >= len(choices):
105 continue
106 return choices[number][0]
107
108
109class Dialog(NoDialog):
110 def __init__(self):
111 NoDialog.__init__(self)
112 self.dlgcmd = os.environ.get('DIALOG','dialog')
113 self.title = ''
114 self.backtitle = 'HelenOS Kernel Configuration'
115
116 if os.system('%s --print-maxsize >/dev/null 2>&1' % self.dlgcmd) != 0:
117 raise NotImplementedError
118
119 def set_title(self,text):
120 self.title = text
121
122 def calldlg(self,*args,**kw):
123 "Wrapper for calling 'dialog' program"
124 indesc, outdesc = os.pipe()
125 pid = os.fork()
126 if not pid:
127 os.close(2)
128 os.dup(outdesc)
129 os.close(indesc)
130
131 dlgargs = [self.dlgcmd,'--title',self.title,
132 '--backtitle', self.backtitle]
133 for key,val in kw.items():
134 dlgargs.append('--'+key)
135 dlgargs.append(val)
136 dlgargs += args
137 os.execlp(self.dlgcmd,*dlgargs)
138
139 os.close(outdesc)
140
141 try:
142 errout = os.fdopen(indesc,'r')
143 data = errout.read()
144 errout.close()
145 pid,status = os.wait()
146 except:
147 os.system('reset') # Reset terminal
148 raise
149
150 if not os.WIFEXITED(status):
151 os.system('reset') # Reset terminal
152 raise EOFError
153
154 status = os.WEXITSTATUS(status)
155 if status == 255:
156 raise EOFError
157 return status,data
158
159 def yesno(self, text, default=None):
160 if text[-1] not in ('?',':'):
161 text = text + ':'
162 width = '50'
163 height = '5'
164 if len(text) < 48:
165 text = ' '*int(((48-len(text))/2)) + text
166 else:
167 width = '0'
168 height = '0'
169 if default == 'n':
170 res,data = self.calldlg('--defaultno','--yesno',text,height,width)
171 else:
172 res,data = self.calldlg('--yesno',text,height,width)
173
174 if res == 0:
175 return 'y'
176 return 'n'
177
178 def menu(self, text, choices, button, defopt=None):
179 text = text + ':'
180 width = '70'
181 height = str(8 + len(choices))
182 args = []
183 for key,val in choices:
184 args.append(key)
185 args.append(val)
186
187 kw = {}
188 if defopt:
189 kw['default-item'] = choices[defopt][0]
190 res,data = self.calldlg('--extra-label',button[1],
191 '--extra-button',
192 '--menu',text,height,width,
193 str(len(choices)),*args,**kw)
194 if res == 3:
195 return button[0]
196 if res == 1: # Cancel
197 sys.exit(1)
198 elif res:
199 print data
200 raise EOFError
201 return data
202
203 def choice(self, text, choices, defopt=None):
204 text = text + ':'
205 width = '50'
206 height = str(8 + len(choices))
207 args = []
208 for key,val in choices:
209 args.append(key)
210 args.append(val)
211
212 kw = {}
213 if defopt:
214 kw['default-item'] = choices[defopt][0]
215 res,data = self.calldlg('--nocancel','--menu',text,height,width,
216 str(len(choices)),*args, **kw)
217 if res:
218 print data
219 raise EOFError
220 return data
221
222def read_defaults(fname,defaults):
223 "Read saved values from last configuration run"
224 f = file(fname,'r')
225 for line in f:
226 res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
227 if res:
228 defaults[res.group(1)] = res.group(2)
229 f.close()
230
231def check_condition(text, defaults):
232 result = True
233 conds = text.split('&')
234 for cond in conds:
235 if cond.startswith('(') and cond.endswith(')'):
236 cond = cond[1:-1]
237 if not check_dnf(cond, defaults):
238 return False
239 return True
240
241def check_dnf(text, defaults):
242 """
243 Check that the condition specified on input line is True
244
245 only CNF is supported
246 """
247 conds = text.split('|')
248 for cond in conds:
249 res = re.match(r'^(.*?)(!?=)(.*)$', cond)
250 if not res:
251 raise RuntimeError("Invalid condition: %s" % cond)
252 condname = res.group(1)
253 oper = res.group(2)
254 condval = res.group(3)
255 if not defaults.has_key(condname):
256 raise RuntimeError("Condition var %s does not exist: %s" % \
257 (condname,text))
258
259 if oper=='=' and condval == defaults[condname]:
260 return True
261 if oper == '!=' and condval != defaults[condname]:
262 return True
263 return False
264
265def parse_config(input, output, dlg, defaults={}, askonly=None):
266 "Parse configuration file and create Makefile.config on the fly"
267 def ask_the_question():
268 "Ask question based on the type of variables to ask"
269 # This is quite a hack, this thingy is written just to
270 # have access to local variables..
271 if vartype == 'y/n':
272 return dlg.yesno(comment, default)
273 elif vartype == 'n/y':
274 return dlg.noyes(comment, default)
275 elif vartype == 'choice':
276 defopt = None
277 if default is not None:
278 for i,(key,val) in enumerate(choices):
279 if key == default:
280 defopt = i
281 break
282 return dlg.choice(comment, choices, defopt)
283 else:
284 raise RuntimeError("Bad method: %s" % vartype)
285
286
287 f = file(input, 'r')
288 outf = file(output, 'w')
289
290 outf.write('#########################################\n')
291 outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
292 outf.write('#########################################\n\n')
293
294 asked_names = []
295
296 comment = ''
297 default = None
298 choices = []
299 for line in f:
300 if line.startswith('%'):
301 res = re.match(r'^%\s*(?:\[(.*?)\])?\s*(.*)$', line)
302 if not res:
303 raise RuntimeError('Invalid command: %s' % line)
304 if res.group(1):
305 if not check_condition(res.group(1), defaults):
306 continue
307 args = res.group(2).strip().split(' ')
308 cmd = args[0].lower()
309 args = args[1:]
310 if cmd == 'saveas':
311 outf.write('%s = %s\n' % (args[1],defaults[args[0]]))
312
313 continue
314
315 if line.startswith('!'):
316 # Ask a question
317 res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
318 if not res:
319 raise RuntimeError("Weird line: %s" % line)
320 varname = res.group(2)
321 vartype = res.group(3)
322
323 default = defaults.get(varname,None)
324
325 if res.group(1):
326 if not check_condition(res.group(1), defaults):
327 if default is not None:
328 outf.write('#!# %s = %s\n' % (varname, default))
329 # Clear cumulated values
330 comment = ''
331 default = None
332 choices = []
333 continue
334
335 asked_names.append((varname,comment))
336
337 if default is None or not askonly or askonly == varname:
338 default = ask_the_question()
339
340 outf.write('%s = %s\n' % (varname, default))
341 # Remeber the selected value
342 defaults[varname] = default
343 # Clear cumulated values
344 comment = ''
345 default = None
346 choices = []
347 continue
348
349 if line.startswith('@'):
350 # Add new line into the 'choice array'
351 res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
352 if not res:
353 raise RuntimeError("Bad line: %s" % line)
354 if res.group(1):
355 if not check_condition(res.group(1),defaults):
356 continue
357 choices.append((res.group(2), res.group(3)))
358 continue
359
360 # All other things print to output file
361 outf.write(line)
362 if re.match(r'^#[^#]', line):
363 # Last comment before question will be displayed to the user
364 comment = line[1:].strip()
365 elif line.startswith('## '):
366 # Set title of the dialog window
367 dlg.set_title(line[2:].strip())
368
369 outf.close()
370 f.close()
371 return asked_names
372
373def main():
374 defaults = {}
375 try:
376 dlg = Dialog()
377 except NotImplementedError:
378 dlg = NoDialog()
379
380 if len(sys.argv) == 2 and sys.argv[1]=='default':
381 defmode = True
382 else:
383 defmode = False
384
385 # Default run will update the configuration file
386 # with newest options
387 if os.path.exists(OUTPUT):
388 read_defaults(OUTPUT, defaults)
389
390 # Dry run only with defaults
391 varnames = parse_config(INPUT, TMPOUTPUT, DefaultDialog(dlg), defaults)
392 # If not in default mode, present selection of all possibilities
393 if not defmode:
394 defopt = 0
395 while 1:
396 # varnames contains variable names that were in the
397 # last question set
398 choices = [ (x[1],defaults[x[0]]) for x in varnames ]
399 res = dlg.menu('Configuration',choices,('save','Save'),defopt)
400 if res == 'save':
401 parse_config(INPUT, TMPOUTPUT, DefaultDialog(dlg), defaults)
402 break
403 # transfer description back to varname
404 for i,(vname,descr) in enumerate(varnames):
405 if res == descr:
406 defopt = i
407 break
408 # Ask the user a simple question, produce output
409 # as if the user answered all the other questions
410 # with default answer
411 varnames = parse_config(INPUT, TMPOUTPUT, dlg, defaults,
412 askonly=varnames[i][0])
413
414
415 if os.path.exists(OUTPUT):
416 os.unlink(OUTPUT)
417 os.rename(TMPOUTPUT, OUTPUT)
418
419 if not defmode and dlg.yesno('Rebuild kernel?') == 'y':
420 os.execlp('make','make','clean','all')
421
422if __name__ == '__main__':
423 main()
Note: See TracBrowser for help on using the repository browser.