Subversion Repositories HelenOS

Rev

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

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