Subversion Repositories HelenOS

Rev

Rev 3808 | Rev 3909 | 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
3804 decky 37
import xtui
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
 
3804 decky 181
def subchoice(screen, name, choices, default):
3803 decky 182
    "Return choice of choices"
183
 
3804 decky 184
    maxkey = 0
185
    for key, val in choices:
186
        length = len(key)
187
        if (length > maxkey):
188
            maxkey = length
3803 decky 189
 
190
    options = []
3804 decky 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
3803 decky 199
 
3804 decky 200
    (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
3803 decky 201
 
3804 decky 202
    if (button == 'cancel'):
3803 decky 203
        return None
204
 
3804 decky 205
    return choices[value][0]
1802 decky 206
 
3803 decky 207
def check_choices(defaults, ask_names):
208
    "Check whether all accessible variables have a default"
209
 
3804 decky 210
    for varname, vartype, name, choices, cond in ask_names:
3803 decky 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
 
3803 decky 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
 
3804 decky 228
    for varname, vartype, name, choices, cond in ask_names:
3803 decky 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():
3803 decky 244
    defaults = {}
245
    ask_names = []
1802 decky 246
 
3803 decky 247
    # Parse configuration file
248
    parse_config(INPUT, ask_names)
1802 decky 249
 
3803 decky 250
    # Read defaults from previous run
251
    if os.path.exists(OUTPUT):
252
        read_defaults(OUTPUT, defaults)
1802 decky 253
 
3803 decky 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
 
3808 decky 260
    # Check mode: only check defaults
261
    if ((len(sys.argv) >= 3) and (sys.argv[2] == 'check')):
262
        if (check_choices(defaults, ask_names)):
263
            return 0
264
        return 1
265
 
3804 decky 266
    screen = xtui.screen_init()
3803 decky 267
    try:
268
        selname = None
269
        while True:
270
 
271
            options = []
272
            opt2row = {}
273
            position = None
274
            cnt = 0
3804 decky 275
            for varname, vartype, name, choices, cond in ask_names:
3803 decky 276
 
277
                if ((cond) and (not check_condition(cond, defaults, ask_names))):
278
                    continue
279
 
280
                if (varname == selname):
281
                    position = cnt
282
 
283
                if (not defaults.has_key(varname)):
284
                    default = None
285
                else:
286
                    default = defaults[varname]
287
 
288
                if (vartype == 'choice'):
289
                    # Check if the default is an acceptable value
290
                    if ((default) and (not default in [choice[0] for choice in choices])):
291
                        default = None
292
                        defaults.pop(varname)
293
 
294
                    # If there is just one option, use it
295
                    if (len(choices) == 1):
296
                        default = choices[0][0]
297
                        defaults[varname] = default
298
 
299
                    options.append("     %s [%s] --> " % (name, default))
300
                elif (vartype == 'y/n'):
301
                    if (default == None):
302
                        default = 'y'
303
                        defaults[varname] = default
304
                    options.append(" <%s> %s " % (yes_no(default), name))
305
                elif (vartype == 'n/y'):
306
                    if (default == None):
307
                        default = 'n'
308
                        defaults[varname] = default
309
                    options.append(" <%s> %s " % (yes_no(default), name))
310
                else:
311
                    raise RuntimeError("Unknown variable type: %s" % vartype)
312
 
3804 decky 313
                opt2row[cnt] = (varname, vartype, name, choices)
3803 decky 314
 
315
                cnt += 1
316
 
3804 decky 317
            (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
3803 decky 318
 
3804 decky 319
            if (button == 'cancel'):
3803 decky 320
                return 'Configuration canceled'
321
 
3804 decky 322
            if (not opt2row.has_key(value)):
323
                raise RuntimeError("Error selecting value: %s" % value)
3803 decky 324
 
3804 decky 325
            (selname, seltype, name, choices) = opt2row[value]
3803 decky 326
 
3804 decky 327
            if (not defaults.has_key(selname)):
328
                    default = None
329
            else:
330
                default = defaults[selname]
331
 
332
            if (button == 'done'):
3803 decky 333
                if (check_choices(defaults, ask_names)):
334
                    break
335
                else:
3804 decky 336
                    xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
3803 decky 337
                    continue
338
 
339
            if (seltype == 'choice'):
3804 decky 340
                defaults[selname] = subchoice(screen, name, choices, default)
3803 decky 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:
3804 decky 347
        xtui.screen_done(screen)
3803 decky 348
 
349
    create_output(OUTPUT, defaults, ask_names)
350
    return 0
1802 decky 351
 
352
if __name__ == '__main__':
3821 decky 353
    sys.exit(main())