Subversion Repositories HelenOS

Rev

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