Subversion Repositories HelenOS

Rev

Rev 3022 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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