3 HelenOS configuration script
11 OUTPUT
= 'Makefile.config'
12 TMPOUTPUT
= 'Makefile.config.tmp'
15 "Wrapper dialog that tries to return default values"
16 def __init__(self
, dlg
):
19 def set_title(self
,text
):
20 self
.dlg
.set_title(text
)
22 def yesno(self
, text
, default
=None):
23 if default
is not None:
25 return self
.dlg
.yesno(text
, default
)
26 def noyes(self
, text
, default
=None):
27 if default
is not None:
29 return self
.dlg
.noyes(text
, default
)
31 def choice(self
, text
, choices
, defopt
=None):
32 if defopt
is not None:
33 return choices
[defopt
][0]
34 return self
.dlg
.choice(text
, choices
, defopt
)
39 self
.title
= 'HelenOS Configuration'
41 def print_title(self
):
43 sys
.stdout
.write("\n*** %s ***\n" % self
.title
)
46 def set_title(self
, text
):
50 def noyes(self
, text
, default
=None):
53 return self
.yesno(text
, default
)
55 def yesno(self
, text
, default
=None):
61 sys
.stdout
.write("%s (y/n)[%s]: " % (text
,default
))
62 inp
= sys
.stdin
.readline()
65 inp
= inp
.strip().lower()
73 def _print_choice(self
, text
, choices
, defopt
):
74 sys
.stdout
.write('%s:\n' % text
)
75 for i
,(text
,descr
) in enumerate(choices
):
76 sys
.stdout
.write('\t%2d. %s\n' % (i
, descr
))
77 if defopt
is not None:
78 sys
.stdout
.write('Enter choice number[%d]: ' % defopt
)
80 sys
.stdout
.write('Enter choice number: ')
82 def menu(self
, text
, choices
, button
, defopt
=None):
83 self
.title
= 'Main menu'
85 for key
, descr
in choices
:
86 txt
= key
+ (45-len(key
))*' ' + ': ' + descr
87 menu
.append((key
, txt
))
89 return self
.choice(text
, [button
] + menu
)
91 def choice(self
, text
, choices
, defopt
=None):
94 self
._print
_choice
(text
, choices
, defopt
)
95 inp
= sys
.stdin
.readline()
99 if defopt
is not None:
100 return choices
[defopt
][0]
103 number
= int(inp
.strip())
106 if number
< 0 or number
>= len(choices
):
108 return choices
[number
][0]
111 def eof_checker(fnc
):
112 def wrapper(self
, *args
, **kw
):
114 return fnc(self
, *args
, **kw
)
116 return getattr(self
.bckdialog
,fnc
.func_name
)(*args
, **kw
)
119 class Dialog(NoDialog
):
121 NoDialog
.__init
__(self
)
122 self
.dlgcmd
= os
.environ
.get('DIALOG','dialog')
124 self
.backtitle
= 'HelenOS Configuration'
126 if os
.system('%s --print-maxsize >/dev/null 2>&1' % self
.dlgcmd
) != 0:
127 raise NotImplementedError
129 self
.bckdialog
= NoDialog()
131 def set_title(self
,text
):
133 self
.bckdialog
.set_title(text
)
135 def calldlg(self
,*args
,**kw
):
136 "Wrapper for calling 'dialog' program"
137 indesc
, outdesc
= os
.pipe()
144 dlgargs
= [self
.dlgcmd
,'--title',self
.title
,
145 '--backtitle', self
.backtitle
]
146 for key
,val
in kw
.items():
147 dlgargs
.append('--'+key
)
150 os
.execlp(self
.dlgcmd
,*dlgargs
)
155 errout
= os
.fdopen(indesc
,'r')
158 pid
,status
= os
.wait()
160 os
.system('reset') # Reset terminal
163 if not os
.WIFEXITED(status
):
164 os
.system('reset') # Reset terminal
167 status
= os
.WEXITSTATUS(status
)
172 def yesno(self
, text
, default
=None):
173 if text
[-1] not in ('?',':'):
178 text
= ' '*int(((48-len(text
))/2)) + text
183 res
,data
= self
.calldlg('--defaultno','--yesno',text
,height
,width
)
185 res
,data
= self
.calldlg('--yesno',text
,height
,width
)
190 yesno
= eof_checker(yesno
)
192 def menu(self
, text
, choices
, button
, defopt
=None):
193 self
.title
= 'Main menu'
196 height
= str(8 + len(choices
))
198 for key
,val
in choices
:
204 kw
['default-item'] = choices
[defopt
][0]
205 res
,data
= self
.calldlg('--ok-label','Change',
206 '--extra-label',button
[1],
208 '--menu',text
,height
,width
,
209 str(len(choices
)),*args
,**kw
)
212 if res
== 1: # Cancel
218 menu
= eof_checker(menu
)
220 def choice(self
, text
, choices
, defopt
=None):
223 height
= str(8 + len(choices
))
225 for key
,val
in choices
:
231 kw
['default-item'] = choices
[defopt
][0]
232 res
,data
= self
.calldlg('--nocancel','--menu',text
,height
,width
,
233 str(len(choices
)),*args
, **kw
)
238 choice
= eof_checker(choice
)
240 def read_defaults(fname
,defaults
):
241 "Read saved values from last configuration run"
244 res
= re
.match(r
'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line
)
246 defaults
[res
.group(1)] = res
.group(2)
249 def check_condition(text
, defaults
, asked_names
):
250 seen_vars
= [ x
[0] for x
in asked_names
]
252 if ')|' in text
or '|(' in text
:
256 conds
= text
.split('&')
258 conds
= text
.split('|')
261 if cond
.startswith('(') and cond
.endswith(')'):
264 inside
= check_inside(cond
, defaults
, ctype
, seen_vars
)
266 if ctype
== 'cnf' and not inside
:
268 if ctype
== 'dnf' and inside
:
275 def check_inside(text
, defaults
, ctype
, seen_vars
):
277 Check that the condition specified on input line is True
279 only CNF is supported
282 conds
= text
.split('|')
284 conds
= text
.split('&')
286 res
= re
.match(r
'^(.*?)(!?=)(.*)$', cond
)
288 raise RuntimeError("Invalid condition: %s" % cond
)
289 condname
= res
.group(1)
291 condval
= res
.group(3)
292 if condname
not in seen_vars
:
294 ## raise RuntimeError("Variable %s not defined before being asked." %\
296 elif not defaults
.has_key(condname
):
297 raise RuntimeError("Condition var %s does not exist: %s" % \
300 varval
= defaults
[condname
]
302 if oper
== '=' and condval
== varval
:
304 if oper
== '!=' and condval
!= varval
:
307 if oper
== '=' and condval
!= varval
:
309 if oper
== '!=' and condval
== varval
:
315 def parse_config(input, output
, dlg
, defaults
={}, askonly
=None):
316 "Parse configuration file and create Makefile.config on the fly"
317 def ask_the_question(dialog
):
318 "Ask question based on the type of variables to ask"
319 # This is quite a hack, this thingy is written just to
320 # have access to local variables..
322 return dialog
.yesno(comment
, default
)
323 elif vartype
== 'n/y':
324 return dialog
.noyes(comment
, default
)
325 elif vartype
== 'choice':
327 if default
is not None:
328 for i
,(key
,val
) in enumerate(choices
):
332 return dialog
.choice(comment
, choices
, defopt
)
334 raise RuntimeError("Bad method: %s" % vartype
)
338 outf
= file(output
, 'w')
340 outf
.write('#########################################\n')
341 outf
.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
342 outf
.write('#########################################\n\n')
350 if line
.startswith('%'):
351 res
= re
.match(r
'^%\s*(?:\[(.*?)\])?\s*(.*)$', line
)
353 raise RuntimeError('Invalid command: %s' % line
)
355 if not check_condition(res
.group(1), defaults
,
358 args
= res
.group(2).strip().split(' ')
359 cmd
= args
[0].lower()
362 outf
.write('%s = %s\n' % (args
[1],defaults
[args
[0]]))
363 elif cmd
== 'shellcmd':
366 for i
,arg
in enumerate(args
):
367 if arg
.startswith('$'):
368 args
[i
] = defaults
[arg
[1:]]
369 data
,status
= commands
.getstatusoutput(' '.join(args
))
371 raise RuntimeError('Error running: %s' % ' '.join(args
))
372 outf
.write('%s = %s\n' % (varname
,data
.strip()))
375 if line
.startswith('!'):
377 res
= re
.search(r
'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line
)
379 raise RuntimeError("Weird line: %s" % line
)
380 varname
= res
.group(2)
381 vartype
= res
.group(3)
383 default
= defaults
.get(varname
,None)
386 if not check_condition(res
.group(1), defaults
,
388 if default
is not None:
389 outf
.write('#!# %s = %s\n' % (varname
, default
))
390 # Clear cumulated values
396 asked_names
.append((varname
,comment
))
398 if default
is None or not askonly
or askonly
== varname
:
399 default
= ask_the_question(dlg
)
401 default
= ask_the_question(DefaultDialog(dlg
))
403 outf
.write('%s = %s\n' % (varname
, default
))
404 # Remeber the selected value
405 defaults
[varname
] = default
406 # Clear cumulated values
412 if line
.startswith('@'):
413 # Add new line into the 'choice array'
414 res
= re
.match(r
'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line
)
416 raise RuntimeError("Bad line: %s" % line
)
418 if not check_condition(res
.group(1),defaults
,
421 choices
.append((res
.group(2), res
.group(3)))
424 # All other things print to output file
426 if re
.match(r
'^#[^#]', line
):
427 # Last comment before question will be displayed to the user
428 comment
= line
[1:].strip()
429 elif line
.startswith('## '):
430 # Set title of the dialog window
431 dlg
.set_title(line
[2:].strip())
434 outf
.write('REVISION = %s\n' % commands
.getoutput('svnversion . 2> /dev/null'))
435 outf
.write('TIMESTAMP = %s\n' % commands
.getoutput('date "+%Y-%m-%d %H:%M:%S"'))
444 except NotImplementedError:
447 if len(sys
.argv
) >= 3 and sys
.argv
[2]=='default':
452 # Default run will update the configuration file
453 # with newest options
454 if os
.path
.exists(OUTPUT
):
455 read_defaults(OUTPUT
, defaults
)
457 # Get ARCH from command line if specified
458 if len(sys
.argv
) >= 4:
459 defaults
['ARCH'] = sys
.argv
[3]
460 defaults
['PLATFORM'] = sys
.argv
[3]
462 # Get COMPILER from command line if specified
463 if len(sys
.argv
) >= 5:
464 defaults
['COMPILER'] = sys
.argv
[4]
466 # Get CONFIG_DEBUG from command line if specified
467 if len(sys
.argv
) >= 6:
468 defaults
['CONFIG_DEBUG'] = sys
.argv
[5]
470 # Get MACHINE/IMAGE from command line if specified
471 if len(sys
.argv
) >= 7:
472 defaults
['MACHINE'] = sys
.argv
[6]
473 defaults
['IMAGE'] = sys
.argv
[6]
475 # Dry run only with defaults
476 varnames
= parse_config(INPUT
, TMPOUTPUT
, DefaultDialog(dlg
), defaults
)
477 # If not in default mode, present selection of all possibilities
481 # varnames contains variable names that were in the
483 choices
= [ (x
[1],defaults
[x
[0]]) for x
in varnames
]
484 res
= dlg
.menu('Configuration',choices
,('save','Save'),defopt
)
486 parse_config(INPUT
, TMPOUTPUT
, DefaultDialog(dlg
), defaults
)
488 # transfer description back to varname
489 for i
,(vname
,descr
) in enumerate(varnames
):
493 # Ask the user a simple question, produce output
494 # as if the user answered all the other questions
495 # with default answer
496 varnames
= parse_config(INPUT
, TMPOUTPUT
, dlg
, defaults
,
497 askonly
=varnames
[i
][0])
500 if os
.path
.exists(OUTPUT
):
502 os
.rename(TMPOUTPUT
, OUTPUT
)
504 if not defmode
and dlg
.yesno('Rebuild everything?') == 'y':
505 os
.execlp('make','make','clean','build')
507 if __name__
== '__main__':