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

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

be more robust when the bzr binary cannot be executed

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