Subversion Repositories HelenOS

Rev

Rev 3017 | Rev 3804 | 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
3803 decky 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
"""
3803 decky 31
HelenOS configuration system
1802 decky 32
"""
33
import sys
34
import os
35
import re
36
import commands
3803 decky 37
import snack
1802 decky 38
 
39
INPUT = sys.argv[1]
40
OUTPUT = 'Makefile.config'
41
 
3803 decky 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
 
3803 decky 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
 
3803 decky 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
 
3803 decky 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
 
3803 decky 173
def yes_no(default):
174
    "Return '*' if yes, ' ' if no"
175
 
176
    if (default == 'y'):
177
        return '*'
178
 
179
    return ' '
1802 decky 180
 
3803 decky 181
def subchoice(screen, name, choices):
182
    "Return choice of choices"
183
 
184
    maxlen = 0
185
    for choice in choices:
186
        length = len(choice[0])
187
        if (length > maxlen):
188
            maxlen = length
189
 
190
    options = []
191
    for choice in choices:
192
        options.append(" %-*s  %s " % (maxlen, choice[0], choice[1]))
193
 
194
    retval = snack.ListboxChoiceWindow(screen, name, 'Choose value', options)
195
 
196
    if (retval[0] == 'cancel'):
197
        return None
198
 
199
    return choices[retval[1]][0]
1802 decky 200
 
3803 decky 201
def check_choices(defaults, ask_names):
202
    "Check whether all accessible variables have a default"
203
 
204
    for row in ask_names:
205
        varname = row[0]
206
        cond = row[4]
207
 
208
        if ((cond) and (not check_condition(cond, defaults, ask_names))):
209
            continue
210
 
211
        if (not defaults.has_key(varname)):
212
            return False
213
 
214
    return True
1802 decky 215
 
3803 decky 216
def create_output(fname, defaults, ask_names):
217
    "Create output configuration"
218
 
219
    outf = file(fname, 'w')
220
 
221
    outf.write('#########################################\n')
222
    outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
223
    outf.write('#########################################\n\n')
224
 
225
    for row in ask_names:
226
        varname = row[0]
227
        name = row[2]
228
        cond = row[4]
229
 
230
        if ((cond) and (not check_condition(cond, defaults, ask_names))):
231
            continue
232
 
233
        if (not defaults.has_key(varname)):
234
            default = ''
235
        else:
236
            default = defaults[varname]
237
 
238
        outf.write('# %s\n%s = %s\n\n' % (name, varname, default))
239
 
240
    outf.write('REVISION = %s\n' % commands.getoutput('svnversion . 2> /dev/null'))
241
    outf.write('TIMESTAMP = %s\n' % commands.getoutput('date "+%Y-%m-%d %H:%M:%S"'))
242
    outf.close()
1802 decky 243
 
244
def main():
3803 decky 245
    defaults = {}
246
    ask_names = []
1802 decky 247
 
3803 decky 248
    # Parse configuration file
249
    parse_config(INPUT, ask_names)
1802 decky 250
 
3803 decky 251
    # Read defaults from previous run
252
    if os.path.exists(OUTPUT):
253
        read_defaults(OUTPUT, defaults)
1802 decky 254
 
3803 decky 255
    # Default mode: only check defaults and regenerate configuration
256
    if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
257
        if (check_choices(defaults, ask_names)):
258
            create_output(OUTPUT, defaults, ask_names)
259
            return 0
260
 
261
    screen = snack.SnackScreen()
262
    try:
263
        selname = None
264
        while True:
265
 
266
            options = []
267
            opt2row = {}
268
            position = None
269
            cnt = 0
270
            for row in ask_names:
271
 
272
                varname = row[0]
273
                vartype = row[1]
274
                name = row[2]
275
                choices = row[3]
276
                cond = row[4]
277
 
278
                if ((cond) and (not check_condition(cond, defaults, ask_names))):
279
                    continue
280
 
281
                if (varname == selname):
282
                    position = cnt
283
 
284
                if (not defaults.has_key(varname)):
285
                    default = None
286
                else:
287
                    default = defaults[varname]
288
 
289
                if (vartype == 'choice'):
290
                    # Check if the default is an acceptable value
291
                    if ((default) and (not default in [choice[0] for choice in choices])):
292
                        default = None
293
                        defaults.pop(varname)
294
 
295
                    # If there is just one option, use it
296
                    if (len(choices) == 1):
297
                        default = choices[0][0]
298
                        defaults[varname] = default
299
 
300
                    options.append("     %s [%s] --> " % (name, default))
301
                elif (vartype == 'y/n'):
302
                    if (default == None):
303
                        default = 'y'
304
                        defaults[varname] = default
305
                    options.append(" <%s> %s " % (yes_no(default), name))
306
                elif (vartype == 'n/y'):
307
                    if (default == None):
308
                        default = 'n'
309
                        defaults[varname] = default
310
                    options.append(" <%s> %s " % (yes_no(default), name))
311
                else:
312
                    raise RuntimeError("Unknown variable type: %s" % vartype)
313
 
314
                opt2row[cnt] = row
315
 
316
                cnt += 1
317
 
318
            retval = snack.ListboxChoiceWindow(screen, 'HelenOS configuration', 'Choose configuration option', options, default = position)
319
 
320
            if (retval[0] == 'cancel'):
321
                return 'Configuration canceled'
322
 
323
            row = opt2row[retval[1]]
324
            if (row == None):
325
                raise RuntimeError("Error selecting value: %s" % retval[1])
326
 
327
            selname = row[0]
328
            seltype = row[1]
329
            name = row[2]
330
            choices = row[3]
331
 
332
            if (retval[0] == 'ok'):
333
                if (check_choices(defaults, ask_names)):
334
                    break
335
                else:
336
                    snack.ButtonChoiceWindow(screen, 'Error', 'Some options have still undefined values.', ['Ok']);
337
                    continue
338
 
339
            if (seltype == 'choice'):
340
                defaults[selname] = subchoice(screen, name, choices)
341
            elif ((seltype == 'y/n') or (seltype == 'n/y')):
342
                if (defaults[selname] == 'y'):
343
                    defaults[selname] = 'n'
344
                else:
345
                    defaults[selname] = 'y'
346
    finally:
347
        screen.finish()
348
 
349
    create_output(OUTPUT, defaults, ask_names)
350
    return 0
1802 decky 351
 
352
if __name__ == '__main__':
3803 decky 353
    exit(main())