source: mainline/tools/config.py@ 373acb4

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

more autotooling: run actually the compiler in autotool.py and detect sizes of integers, generate common.h
more build system cleanup: remove redundancy, remove config.defs, merge kernel makefiles to a single file

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