Subversion Repositories HelenOS

Rev

Rev 3803 | Rev 3808 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 3803 Rev 3804
1
#!/usr/bin/env python
1
#!/usr/bin/env python
2
#
2
#
3
# Copyright (c) 2006 Ondrej Palkovsky
3
# Copyright (c) 2006 Ondrej Palkovsky
4
# Copyright (c) 2009 Martin Decky
4
# Copyright (c) 2009 Martin Decky
5
# All rights reserved.
5
# All rights reserved.
6
#
6
#
7
# Redistribution and use in source and binary forms, with or without
7
# Redistribution and use in source and binary forms, with or without
8
# modification, are permitted provided that the following conditions
8
# modification, are permitted provided that the following conditions
9
# are met:
9
# are met:
10
#
10
#
11
# - Redistributions of source code must retain the above copyright
11
# - Redistributions of source code must retain the above copyright
12
#   notice, this list of conditions and the following disclaimer.
12
#   notice, this list of conditions and the following disclaimer.
13
# - Redistributions in binary form must reproduce the above copyright
13
# - Redistributions in binary form must reproduce the above copyright
14
#   notice, this list of conditions and the following disclaimer in the
14
#   notice, this list of conditions and the following disclaimer in the
15
#   documentation and/or other materials provided with the distribution.
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
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.
17
#   derived from this software without specific prior written permission.
18
#
18
#
19
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
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
20
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
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
25
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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
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.
28
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
#
29
#
30
"""
30
"""
31
HelenOS configuration system
31
HelenOS configuration system
32
"""
32
"""
33
import sys
33
import sys
34
import os
34
import os
35
import re
35
import re
36
import commands
36
import commands
37
import snack
37
import xtui
38
 
38
 
39
INPUT = sys.argv[1]
39
INPUT = sys.argv[1]
40
OUTPUT = 'Makefile.config'
40
OUTPUT = 'Makefile.config'
41
 
41
 
42
def read_defaults(fname, defaults):
42
def read_defaults(fname, defaults):
43
    "Read saved values from last configuration run"
43
    "Read saved values from last configuration run"
44
   
44
   
45
    inf = file(fname,'r')
45
    inf = file(fname,'r')
46
   
46
   
47
    for line in inf:
47
    for line in inf:
48
        res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
48
        res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
49
        if (res):
49
        if (res):
50
            defaults[res.group(1)] = res.group(2)
50
            defaults[res.group(1)] = res.group(2)
51
   
51
   
52
    inf.close()
52
    inf.close()
53
 
53
 
54
def check_condition(text, defaults, ask_names):
54
def check_condition(text, defaults, ask_names):
55
    "Check for condition"
55
    "Check for condition"
56
   
56
   
57
    ctype = 'cnf'
57
    ctype = 'cnf'
58
   
58
   
59
    if ((')|' in text) or ('|(' in text)):
59
    if ((')|' in text) or ('|(' in text)):
60
        ctype = 'dnf'
60
        ctype = 'dnf'
61
   
61
   
62
    if (ctype == 'cnf'):
62
    if (ctype == 'cnf'):
63
        conds = text.split('&')
63
        conds = text.split('&')
64
    else:
64
    else:
65
        conds = text.split('|')
65
        conds = text.split('|')
66
   
66
   
67
    for cond in conds:
67
    for cond in conds:
68
        if (cond.startswith('(')) and (cond.endswith(')')):
68
        if (cond.startswith('(')) and (cond.endswith(')')):
69
            cond = cond[1:-1]
69
            cond = cond[1:-1]
70
       
70
       
71
        inside = check_inside(cond, defaults, ctype)
71
        inside = check_inside(cond, defaults, ctype)
72
       
72
       
73
        if (ctype == 'cnf') and (not inside):
73
        if (ctype == 'cnf') and (not inside):
74
            return False
74
            return False
75
       
75
       
76
        if (ctype == 'dnf') and (inside):
76
        if (ctype == 'dnf') and (inside):
77
            return True
77
            return True
78
   
78
   
79
    if (ctype == 'cnf'):
79
    if (ctype == 'cnf'):
80
        return True
80
        return True
81
    return False
81
    return False
82
 
82
 
83
def check_inside(text, defaults, ctype):
83
def check_inside(text, defaults, ctype):
84
    "Check that the condition specified on input line is True (only CNF is supported)"
84
    "Check that the condition specified on input line is True (only CNF is supported)"
85
   
85
   
86
    if (ctype == 'cnf'):
86
    if (ctype == 'cnf'):
87
        conds = text.split('|')
87
        conds = text.split('|')
88
    else:
88
    else:
89
        conds = text.split('&')
89
        conds = text.split('&')
90
   
90
   
91
    for cond in conds:
91
    for cond in conds:
92
        res = re.match(r'^(.*?)(!?=)(.*)$', cond)
92
        res = re.match(r'^(.*?)(!?=)(.*)$', cond)
93
        if (not res):
93
        if (not res):
94
            raise RuntimeError("Invalid condition: %s" % cond)
94
            raise RuntimeError("Invalid condition: %s" % cond)
95
       
95
       
96
        condname = res.group(1)
96
        condname = res.group(1)
97
        oper = res.group(2)
97
        oper = res.group(2)
98
        condval = res.group(3)
98
        condval = res.group(3)
99
       
99
       
100
        if (not defaults.has_key(condname)):
100
        if (not defaults.has_key(condname)):
101
            varval = ''
101
            varval = ''
102
        else:
102
        else:
103
            varval = defaults[condname]
103
            varval = defaults[condname]
104
       
104
       
105
        if (ctype == 'cnf'):
105
        if (ctype == 'cnf'):
106
            if (oper == '=') and (condval == varval):
106
            if (oper == '=') and (condval == varval):
107
                return True
107
                return True
108
       
108
       
109
            if (oper == '!=') and (condval != varval):
109
            if (oper == '!=') and (condval != varval):
110
                return True
110
                return True
111
        else:
111
        else:
112
            if (oper == '=') and (condval != varval):
112
            if (oper == '=') and (condval != varval):
113
                return False
113
                return False
114
           
114
           
115
            if (oper == '!=') and (condval == varval):
115
            if (oper == '!=') and (condval == varval):
116
                return False
116
                return False
117
   
117
   
118
    if (ctype == 'cnf'):
118
    if (ctype == 'cnf'):
119
        return False
119
        return False
120
   
120
   
121
    return True
121
    return True
122
 
122
 
123
def parse_config(fname, ask_names):
123
def parse_config(fname, ask_names):
124
    "Parse configuration file"
124
    "Parse configuration file"
125
   
125
   
126
    inf = file(fname, 'r')
126
    inf = file(fname, 'r')
127
   
127
   
128
    name = ''
128
    name = ''
129
    choices = []
129
    choices = []
130
   
130
   
131
    for line in inf:
131
    for line in inf:
132
       
132
       
133
        if (line.startswith('!')):
133
        if (line.startswith('!')):
134
            # Ask a question
134
            # Ask a question
135
            res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
135
            res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
136
           
136
           
137
            if (not res):
137
            if (not res):
138
                raise RuntimeError("Weird line: %s" % line)
138
                raise RuntimeError("Weird line: %s" % line)
139
           
139
           
140
            cond = res.group(1)
140
            cond = res.group(1)
141
            varname = res.group(2)
141
            varname = res.group(2)
142
            vartype = res.group(3)
142
            vartype = res.group(3)
143
           
143
           
144
            ask_names.append((varname, vartype, name, choices, cond))
144
            ask_names.append((varname, vartype, name, choices, cond))
145
            name = ''
145
            name = ''
146
            choices = []
146
            choices = []
147
            continue
147
            continue
148
       
148
       
149
        if (line.startswith('@')):
149
        if (line.startswith('@')):
150
            # Add new line into the 'choices' array
150
            # Add new line into the 'choices' array
151
            res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
151
            res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
152
           
152
           
153
            if not res:
153
            if not res:
154
                raise RuntimeError("Bad line: %s" % line)
154
                raise RuntimeError("Bad line: %s" % line)
155
           
155
           
156
            choices.append((res.group(2), res.group(3)))
156
            choices.append((res.group(2), res.group(3)))
157
            continue
157
            continue
158
       
158
       
159
        if (line.startswith('%')):
159
        if (line.startswith('%')):
160
            # Name of the option
160
            # Name of the option
161
            name = line[1:].strip()
161
            name = line[1:].strip()
162
            continue
162
            continue
163
       
163
       
164
        if ((line.startswith('#')) or (line == '\n')):
164
        if ((line.startswith('#')) or (line == '\n')):
165
            # Comment or empty line
165
            # Comment or empty line
166
            continue
166
            continue
167
       
167
       
168
       
168
       
169
        raise RuntimeError("Unknown syntax: %s" % line)
169
        raise RuntimeError("Unknown syntax: %s" % line)
170
   
170
   
171
    inf.close()
171
    inf.close()
172
 
172
 
173
def yes_no(default):
173
def yes_no(default):
174
    "Return '*' if yes, ' ' if no"
174
    "Return '*' if yes, ' ' if no"
175
   
175
   
176
    if (default == 'y'):
176
    if (default == 'y'):
177
        return '*'
177
        return '*'
178
   
178
   
179
    return ' '
179
    return ' '
180
 
180
 
181
def subchoice(screen, name, choices):
181
def subchoice(screen, name, choices, default):
182
    "Return choice of choices"
182
    "Return choice of choices"
183
   
183
   
184
    maxlen = 0
184
    maxkey = 0
185
    for choice in choices:
185
    for key, val in choices:
186
        length = len(choice[0])
186
        length = len(key)
187
        if (length > maxlen):
187
        if (length > maxkey):
188
            maxlen = length
188
            maxkey = length
189
   
189
   
190
    options = []
190
    options = []
-
 
191
    position = None
-
 
192
    cnt = 0
191
    for choice in choices:
193
    for key, val in choices:
-
 
194
        if ((default) and (key == default)):
-
 
195
            position = cnt
-
 
196
       
192
        options.append(" %-*s  %s " % (maxlen, choice[0], choice[1]))
197
        options.append(" %-*s  %s " % (maxkey, key, val))
-
 
198
        cnt += 1
193
   
199
   
194
    retval = snack.ListboxChoiceWindow(screen, name, 'Choose value', options)
200
    (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
195
   
201
   
196
    if (retval[0] == 'cancel'):
202
    if (button == 'cancel'):
197
        return None
203
        return None
198
   
204
   
199
    return choices[retval[1]][0]
205
    return choices[value][0]
200
 
206
 
201
def check_choices(defaults, ask_names):
207
def check_choices(defaults, ask_names):
202
    "Check whether all accessible variables have a default"
208
    "Check whether all accessible variables have a default"
203
   
209
   
204
    for row in ask_names:
210
    for varname, vartype, name, choices, cond in ask_names:
205
        varname = row[0]
-
 
206
        cond = row[4]
-
 
207
       
-
 
208
        if ((cond) and (not check_condition(cond, defaults, ask_names))):
211
        if ((cond) and (not check_condition(cond, defaults, ask_names))):
209
            continue
212
            continue
210
       
213
       
211
        if (not defaults.has_key(varname)):
214
        if (not defaults.has_key(varname)):
212
            return False
215
            return False
213
   
216
   
214
    return True
217
    return True
215
 
218
 
216
def create_output(fname, defaults, ask_names):
219
def create_output(fname, defaults, ask_names):
217
    "Create output configuration"
220
    "Create output configuration"
218
   
221
   
219
    outf = file(fname, 'w')
222
    outf = file(fname, 'w')
220
   
223
   
221
    outf.write('#########################################\n')
224
    outf.write('#########################################\n')
222
    outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
225
    outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
223
    outf.write('#########################################\n\n')
226
    outf.write('#########################################\n\n')
224
   
227
   
225
    for row in ask_names:
228
    for varname, vartype, name, choices, cond 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))):
229
        if ((cond) and (not check_condition(cond, defaults, ask_names))):
231
            continue
230
            continue
232
       
231
       
233
        if (not defaults.has_key(varname)):
232
        if (not defaults.has_key(varname)):
234
            default = ''
233
            default = ''
235
        else:
234
        else:
236
            default = defaults[varname]
235
            default = defaults[varname]
237
       
236
       
238
        outf.write('# %s\n%s = %s\n\n' % (name, varname, default))
237
        outf.write('# %s\n%s = %s\n\n' % (name, varname, default))
239
   
238
   
240
    outf.write('REVISION = %s\n' % commands.getoutput('svnversion . 2> /dev/null'))
239
    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"'))
240
    outf.write('TIMESTAMP = %s\n' % commands.getoutput('date "+%Y-%m-%d %H:%M:%S"'))
242
    outf.close()
241
    outf.close()
243
 
242
 
244
def main():
243
def main():
245
    defaults = {}
244
    defaults = {}
246
    ask_names = []
245
    ask_names = []
247
   
246
   
248
    # Parse configuration file
247
    # Parse configuration file
249
    parse_config(INPUT, ask_names)
248
    parse_config(INPUT, ask_names)
250
   
249
   
251
    # Read defaults from previous run
250
    # Read defaults from previous run
252
    if os.path.exists(OUTPUT):
251
    if os.path.exists(OUTPUT):
253
        read_defaults(OUTPUT, defaults)
252
        read_defaults(OUTPUT, defaults)
254
   
253
   
255
    # Default mode: only check defaults and regenerate configuration
254
    # Default mode: only check defaults and regenerate configuration
256
    if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
255
    if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
257
        if (check_choices(defaults, ask_names)):
256
        if (check_choices(defaults, ask_names)):
258
            create_output(OUTPUT, defaults, ask_names)
257
            create_output(OUTPUT, defaults, ask_names)
259
            return 0
258
            return 0
260
   
259
   
261
    screen = snack.SnackScreen()
260
    screen = xtui.screen_init()
262
    try:
261
    try:
263
        selname = None
262
        selname = None
264
        while True:
263
        while True:
265
           
264
           
266
            options = []
265
            options = []
267
            opt2row = {}
266
            opt2row = {}
268
            position = None
267
            position = None
269
            cnt = 0
268
            cnt = 0
270
            for row in ask_names:
269
            for varname, vartype, name, choices, cond 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
               
270
               
278
                if ((cond) and (not check_condition(cond, defaults, ask_names))):
271
                if ((cond) and (not check_condition(cond, defaults, ask_names))):
279
                    continue
272
                    continue
280
               
273
               
281
                if (varname == selname):
274
                if (varname == selname):
282
                    position = cnt
275
                    position = cnt
283
               
276
               
284
                if (not defaults.has_key(varname)):
277
                if (not defaults.has_key(varname)):
285
                    default = None
278
                    default = None
286
                else:
279
                else:
287
                    default = defaults[varname]
280
                    default = defaults[varname]
288
               
281
               
289
                if (vartype == 'choice'):
282
                if (vartype == 'choice'):
290
                    # Check if the default is an acceptable value
283
                    # Check if the default is an acceptable value
291
                    if ((default) and (not default in [choice[0] for choice in choices])):
284
                    if ((default) and (not default in [choice[0] for choice in choices])):
292
                        default = None
285
                        default = None
293
                        defaults.pop(varname)
286
                        defaults.pop(varname)
294
                   
287
                   
295
                    # If there is just one option, use it
288
                    # If there is just one option, use it
296
                    if (len(choices) == 1):
289
                    if (len(choices) == 1):
297
                        default = choices[0][0]
290
                        default = choices[0][0]
298
                        defaults[varname] = default
291
                        defaults[varname] = default
299
                   
292
                   
300
                    options.append("     %s [%s] --> " % (name, default))
293
                    options.append("     %s [%s] --> " % (name, default))
301
                elif (vartype == 'y/n'):
294
                elif (vartype == 'y/n'):
302
                    if (default == None):
295
                    if (default == None):
303
                        default = 'y'
296
                        default = 'y'
304
                        defaults[varname] = default
297
                        defaults[varname] = default
305
                    options.append(" <%s> %s " % (yes_no(default), name))
298
                    options.append(" <%s> %s " % (yes_no(default), name))
306
                elif (vartype == 'n/y'):
299
                elif (vartype == 'n/y'):
307
                    if (default == None):
300
                    if (default == None):
308
                        default = 'n'
301
                        default = 'n'
309
                        defaults[varname] = default
302
                        defaults[varname] = default
310
                    options.append(" <%s> %s " % (yes_no(default), name))
303
                    options.append(" <%s> %s " % (yes_no(default), name))
311
                else:
304
                else:
312
                    raise RuntimeError("Unknown variable type: %s" % vartype)
305
                    raise RuntimeError("Unknown variable type: %s" % vartype)
313
               
306
               
314
                opt2row[cnt] = row
307
                opt2row[cnt] = (varname, vartype, name, choices)
315
               
308
               
316
                cnt += 1
309
                cnt += 1
317
           
310
           
318
            retval = snack.ListboxChoiceWindow(screen, 'HelenOS configuration', 'Choose configuration option', options, default = position)
311
            (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
319
           
312
           
320
            if (retval[0] == 'cancel'):
313
            if (button == 'cancel'):
321
                return 'Configuration canceled'
314
                return 'Configuration canceled'
322
           
315
           
323
            row = opt2row[retval[1]]
316
            if (not opt2row.has_key(value)):
324
            if (row == None):
-
 
325
                raise RuntimeError("Error selecting value: %s" % retval[1])
317
                raise RuntimeError("Error selecting value: %s" % value)
326
           
318
           
327
            selname = row[0]
319
            (selname, seltype, name, choices) = opt2row[value]
-
 
320
           
-
 
321
            if (not defaults.has_key(selname)):
328
            seltype = row[1]
322
                    default = None
329
            name = row[2]
323
            else:
330
            choices = row[3]
324
                default = defaults[selname]
331
           
325
           
332
            if (retval[0] == 'ok'):
326
            if (button == 'done'):
333
                if (check_choices(defaults, ask_names)):
327
                if (check_choices(defaults, ask_names)):
334
                    break
328
                    break
335
                else:
329
                else:
336
                    snack.ButtonChoiceWindow(screen, 'Error', 'Some options have still undefined values.', ['Ok']);
330
                    xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
337
                    continue
331
                    continue
338
           
332
           
339
            if (seltype == 'choice'):
333
            if (seltype == 'choice'):
340
                defaults[selname] = subchoice(screen, name, choices)
334
                defaults[selname] = subchoice(screen, name, choices, default)
341
            elif ((seltype == 'y/n') or (seltype == 'n/y')):
335
            elif ((seltype == 'y/n') or (seltype == 'n/y')):
342
                if (defaults[selname] == 'y'):
336
                if (defaults[selname] == 'y'):
343
                    defaults[selname] = 'n'
337
                    defaults[selname] = 'n'
344
                else:
338
                else:
345
                    defaults[selname] = 'y'
339
                    defaults[selname] = 'y'
346
    finally:
340
    finally:
347
        screen.finish()
341
        xtui.screen_done(screen)
348
   
342
   
349
    create_output(OUTPUT, defaults, ask_names)
343
    create_output(OUTPUT, defaults, ask_names)
350
    return 0
344
    return 0
351
 
345
 
352
if __name__ == '__main__':
346
if __name__ == '__main__':
353
    exit(main())
347
    exit(main())
354
 
348