Subversion Repositories HelenOS-historic

Rev

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

Rev 552 Rev 554
1
#!/usr/bin/env python
1
#!/usr/bin/env python
2
"""
2
"""
3
Kernel configuration script
3
Kernel configuration script
4
"""
4
"""
5
import sys
5
import sys
6
import os
6
import os
7
import re
7
import re
8
 
8
 
9
INPUT = 'kernel.config'
9
INPUT = 'kernel.config'
10
OUTPUT = 'Makefile.config'
10
OUTPUT = 'Makefile.config'
11
TMPOUTPUT = 'Makefile.config.tmp'
11
TMPOUTPUT = 'Makefile.config.tmp'
12
 
12
 
13
class DefaultDialog:
13
class DefaultDialog:
14
    "Wrapper dialog that tries to return default values"
14
    "Wrapper dialog that tries to return default values"
15
    def __init__(self, dlg):
15
    def __init__(self, dlg):
16
        self.dlg = dlg
16
        self.dlg = dlg
17
 
17
 
18
    def set_title(self,text):
18
    def set_title(self,text):
19
        self.dlg.set_title(text)
19
        self.dlg.set_title(text)
20
       
20
       
21
    def yesno(self, text, default=None):
21
    def yesno(self, text, default=None):
22
        if default is not None:
22
        if default is not None:
23
            return default
23
            return default
24
        return self.dlg.yesno(text, default)
24
        return self.dlg.yesno(text, default)
25
    def noyes(self, text, default=None):
25
    def noyes(self, text, default=None):
26
        if default is not None:
26
        if default is not None:
27
            return default
27
            return default
28
        return self.dlg.noyes(text, default)
28
        return self.dlg.noyes(text, default)
29
   
29
   
30
    def choice(self, text, choices, defopt=None):
30
    def choice(self, text, choices, defopt=None):
31
        if defopt is not None:
31
        if defopt is not None:
32
            return choices[defopt][0]
32
            return choices[defopt][0]
33
        return self.dlg.choice(text, choices, defopt)
33
        return self.dlg.choice(text, choices, defopt)
34
 
34
 
35
class NoDialog:
35
class NoDialog:
36
    def __init__(self):
36
    def __init__(self):
37
        self.printed = None
37
        self.printed = None
38
        self.title = 'HelenOS Configuration'
38
        self.title = 'HelenOS Configuration'
39
 
39
 
40
    def print_title(self):
40
    def print_title(self):
41
        if not self.printed:
41
        if not self.printed:
42
            sys.stdout.write("\n*** %s ***\n" % self.title)
42
            sys.stdout.write("\n*** %s ***\n" % self.title)
43
            self.printed = True
43
            self.printed = True
44
 
44
 
45
    def set_title(self, text):
45
    def set_title(self, text):
46
        self.title = text
46
        self.title = text
47
        self.printed = False
47
        self.printed = False
48
   
48
   
49
    def noyes(self, text, default=None):
49
    def noyes(self, text, default=None):
50
        if not default:
50
        if not default:
51
            default = 'n'
51
            default = 'n'
52
        return self.yesno(text, default)
52
        return self.yesno(text, default)
53
   
53
   
54
    def yesno(self, text, default=None):
54
    def yesno(self, text, default=None):
55
        self.print_title()
55
        self.print_title()
56
       
56
       
57
        if default != 'n':
57
        if default != 'n':
58
            default = 'y'
58
            default = 'y'
59
        while 1:
59
        while 1:
60
            sys.stdout.write("%s (y/n)[%s]: " % (text,default))
60
            sys.stdout.write("%s (y/n)[%s]: " % (text,default))
61
            inp = sys.stdin.readline()
61
            inp = sys.stdin.readline()
62
            if not inp:
62
            if not inp:
63
                raise EOFError
63
                raise EOFError
64
            inp = inp.strip().lower()
64
            inp = inp.strip().lower()
65
            if not inp:
65
            if not inp:
66
                return default
66
                return default
67
            if inp == 'y':
67
            if inp == 'y':
68
                return 'y'
68
                return 'y'
69
            elif inp == 'n':
69
            elif inp == 'n':
70
                return 'n'
70
                return 'n'
71
 
71
 
72
    def _print_choice(self, text, choices, defopt):
72
    def _print_choice(self, text, choices, defopt):
73
        sys.stdout.write('%s:\n' % text)
73
        sys.stdout.write('%s:\n' % text)
74
        for i,(text,descr) in enumerate(choices):
74
        for i,(text,descr) in enumerate(choices):
75
            sys.stdout.write('\t%2d. %s\n' % (i, descr))
75
            sys.stdout.write('\t%2d. %s\n' % (i, descr))
76
        if defopt is not None:
76
        if defopt is not None:
77
            sys.stdout.write('Enter choice number[%d]: ' % defopt)
77
            sys.stdout.write('Enter choice number[%d]: ' % defopt)
78
        else:
78
        else:
79
            sys.stdout.write('Enter choice number: ')
79
            sys.stdout.write('Enter choice number: ')
-
 
80
 
-
 
81
    def menu(self, text, choices, button, defopt=None):
-
 
82
        return self.choice(text, [button] + choices)
80
       
83
       
81
    def choice(self, text, choices, defopt=None):
84
    def choice(self, text, choices, defopt=None):
82
        self.print_title()
85
        self.print_title()
83
        while 1:
86
        while 1:
84
            self._print_choice(text, choices, defopt)
87
            self._print_choice(text, choices, defopt)
85
            inp = sys.stdin.readline()
88
            inp = sys.stdin.readline()
86
            if not inp:
89
            if not inp:
87
                raise EOFError
90
                raise EOFError
88
            if not inp.strip():
91
            if not inp.strip():
89
                if defopt is not None:
92
                if defopt is not None:
90
                    return choices[defopt][0]
93
                    return choices[defopt][0]
91
                continue
94
                continue
92
            try:
95
            try:
93
                number = int(inp.strip())
96
                number = int(inp.strip())
94
            except ValueError:
97
            except ValueError:
95
                continue
98
                continue
96
            if number < 0 or number >= len(choices):
99
            if number < 0 or number >= len(choices):
97
                continue
100
                continue
98
            return choices[number][0]
101
            return choices[number][0]
99
 
102
 
100
 
103
 
101
class Dialog(NoDialog):
104
class Dialog(NoDialog):
102
    def __init__(self):
105
    def __init__(self):
103
        NoDialog.__init__(self)
106
        NoDialog.__init__(self)
104
        self.dlgcmd = os.environ.get('DIALOG','dialog')
107
        self.dlgcmd = os.environ.get('DIALOG','dialog')
-
 
108
        self.title = ''
105
        self.title = 'HelenOS Configuration'
109
        self.backtitle = 'HelenOS Kernel Configuration'
106
       
110
       
107
        if os.system('%s --print-maxsize >/dev/null 2>&1' % self.dlgcmd) != 0:
111
        if os.system('%s --print-maxsize >/dev/null 2>&1' % self.dlgcmd) != 0:
108
            raise NotImplementedError
112
            raise NotImplementedError
109
 
113
 
110
    def set_title(self,text):
114
    def set_title(self,text):
111
        self.title = text
115
        self.title = text
112
       
116
       
113
    def calldlg(self,*args,**kw):
117
    def calldlg(self,*args,**kw):
114
        "Wrapper for calling 'dialog' program"
118
        "Wrapper for calling 'dialog' program"
115
        indesc, outdesc = os.pipe()
119
        indesc, outdesc = os.pipe()
116
        pid = os.fork()
120
        pid = os.fork()
117
        if not pid:
121
        if not pid:
118
            os.close(2)
122
            os.close(2)
119
            os.dup(outdesc)
123
            os.dup(outdesc)
120
            os.close(indesc)
124
            os.close(indesc)
121
           
125
           
122
            dlgargs = [self.dlgcmd,'--title',self.title]
126
            dlgargs = [self.dlgcmd,'--title',self.title,
-
 
127
                       '--backtitle', self.backtitle]
123
            for key,val in kw.items():
128
            for key,val in kw.items():
124
                dlgargs.append('--'+key)
129
                dlgargs.append('--'+key)
125
                dlgargs.append(val)
130
                dlgargs.append(val)
126
            dlgargs += args            
131
            dlgargs += args            
127
            os.execlp(self.dlgcmd,*dlgargs)
132
            os.execlp(self.dlgcmd,*dlgargs)
128
 
133
 
129
        os.close(outdesc)
134
        os.close(outdesc)
130
        errout = os.fdopen(indesc,'r')
135
        errout = os.fdopen(indesc,'r')
131
        data = errout.read()
136
        data = errout.read()
132
        errout.close()
137
        errout.close()
133
           
138
           
134
        pid,status = os.wait()
139
        pid,status = os.wait()
135
        if not os.WIFEXITED(status):
140
        if not os.WIFEXITED(status):
136
            raise EOFError
141
            raise EOFError
137
        status = os.WEXITSTATUS(status)
142
        status = os.WEXITSTATUS(status)
138
        if status == 255:
143
        if status == 255:
139
            raise EOFError
144
            raise EOFError
140
        return status,data
145
        return status,data
141
       
146
       
142
    def yesno(self, text, default=None):
147
    def yesno(self, text, default=None):
143
        text = text + ':'
148
        text = text + ':'
144
        width = '50'
149
        width = '50'
145
        height = '5'
150
        height = '5'
146
        if len(text) < 48:
151
        if len(text) < 48:
147
            text = ' '*int(((48-len(text))/2)) + text
152
            text = ' '*int(((48-len(text))/2)) + text
148
        else:
153
        else:
149
            width = '0'
154
            width = '0'
150
            height = '0'
155
            height = '0'
151
        if default == 'n':
156
        if default == 'n':
152
            res,data = self.calldlg('--defaultno','--yesno',text,height,width)
157
            res,data = self.calldlg('--defaultno','--yesno',text,height,width)
153
        else:
158
        else:
154
            res,data = self.calldlg('--yesno',text,height,width)
159
            res,data = self.calldlg('--yesno',text,height,width)
155
 
160
 
156
        if res == 0:
161
        if res == 0:
157
            return 'y'
162
            return 'y'
158
        return 'n'
163
        return 'n'
-
 
164
 
-
 
165
    def menu(self, text, choices, button, defopt=None):
-
 
166
        text = text + ':'
-
 
167
        width = '70'
-
 
168
        height = str(8 + len(choices))
-
 
169
        args = []
-
 
170
        for key,val in choices:
-
 
171
            args.append(key)
-
 
172
            args.append(val)
-
 
173
 
-
 
174
        kw = {}
-
 
175
        if defopt:
-
 
176
            kw['default-item'] = choices[defopt][0]
-
 
177
        res,data = self.calldlg('--cancel-label',button[1],
-
 
178
                                '--menu',text,height,width,
-
 
179
                                str(len(choices)),*args,**kw)
-
 
180
        if res == 1:
-
 
181
            return button[0]
-
 
182
        elif res:
-
 
183
            print data
-
 
184
            raise EOFError
-
 
185
        return data
159
   
186
   
160
    def choice(self, text, choices, defopt=None):
187
    def choice(self, text, choices, defopt=None):
161
        text = text + ':'
188
        text = text + ':'
162
        width = '50'
189
        width = '50'
163
        height = str(8 + len(choices))
190
        height = str(8 + len(choices))
164
        args = []
191
        args = []
165
        for key,val in choices:
192
        for key,val in choices:
166
            args.append(key)
193
            args.append(key)
167
            args.append(val)
194
            args.append(val)
168
 
195
 
169
        kw = {}
196
        kw = {}
170
        if defopt:
197
        if defopt:
171
            kw['default-item'] = choices[defopt][0]
198
            kw['default-item'] = choices[defopt][0]
172
        res,data = self.calldlg('--nocancel','--menu',text,height,width,
199
        res,data = self.calldlg('--nocancel','--menu',text,height,width,
173
                                str(len(choices)),*args, **kw)
200
                                str(len(choices)),*args, **kw)
174
        if res:
201
        if res:
175
            print data
202
            print data
176
            raise EOFError
203
            raise EOFError
177
        return data
204
        return data
178
   
205
   
179
def read_defaults(fname,defaults):
206
def read_defaults(fname,defaults):
180
    "Read saved values from last configuration run"
207
    "Read saved values from last configuration run"
181
    f = file(fname,'r')
208
    f = file(fname,'r')
182
    for line in f:
209
    for line in f:
183
        res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
210
        res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
184
        if res:
211
        if res:
185
            defaults[res.group(1)] = res.group(2)
212
            defaults[res.group(1)] = res.group(2)
186
    f.close()
213
    f.close()
187
 
214
 
188
def check_condition(text, defaults):
215
def check_condition(text, defaults):
189
    result = True
216
    result = True
190
    conds = text.split('&')
217
    conds = text.split('&')
191
    for cond in conds:
218
    for cond in conds:
192
        if cond.startswith('(') and cond.endswith(')'):
219
        if cond.startswith('(') and cond.endswith(')'):
193
            cond = cond[1:-1]
220
            cond = cond[1:-1]
194
        if not check_dnf(cond, defaults):
221
        if not check_dnf(cond, defaults):
195
            return False
222
            return False
196
    return True
223
    return True
197
 
224
 
198
def check_dnf(text, defaults):
225
def check_dnf(text, defaults):
199
    """
226
    """
200
    Check that the condition specified on input line is True
227
    Check that the condition specified on input line is True
201
 
228
 
202
    only CNF is supported
229
    only CNF is supported
203
    """
230
    """
204
    conds = text.split('|')
231
    conds = text.split('|')
205
    for cond in conds:
232
    for cond in conds:
206
        res = re.match(r'^(.*?)(!?=)(.*)$', cond)
233
        res = re.match(r'^(.*?)(!?=)(.*)$', cond)
207
        if not res:
234
        if not res:
208
            raise RuntimeError("Invalid condition: %s" % cond)
235
            raise RuntimeError("Invalid condition: %s" % cond)
209
        condname = res.group(1)
236
        condname = res.group(1)
210
        oper = res.group(2)
237
        oper = res.group(2)
211
        condval = res.group(3)
238
        condval = res.group(3)
212
        if not defaults.has_key(condname):
239
        if not defaults.has_key(condname):
213
            raise RuntimeError("Condition var %s does not exist: %s" % \
240
            raise RuntimeError("Condition var %s does not exist: %s" % \
214
                               (condname,text))
241
                               (condname,text))
215
 
242
 
216
        if oper=='=' and  condval == defaults[condname]:
243
        if oper=='=' and  condval == defaults[condname]:
217
            return True
244
            return True
218
        if oper == '!=' and condval != defaults[condname]:
245
        if oper == '!=' and condval != defaults[condname]:
219
            return True
246
            return True
220
    return False
247
    return False
221
 
248
 
222
def parse_config(input, output, dlg, defaults={}):
249
def parse_config(input, output, dlg, defaults={}, askonly=None):
223
    "Parse configuration file and create Makefile.config on the fly"
250
    "Parse configuration file and create Makefile.config on the fly"
224
    f = file(input, 'r')
251
    f = file(input, 'r')
225
    outf = file(output, 'w')
252
    outf = file(output, 'w')
226
 
253
 
227
    outf.write('#########################################\n')
254
    outf.write('#########################################\n')
228
    outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
255
    outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
229
    outf.write('#########################################\n\n')
256
    outf.write('#########################################\n\n')
230
 
257
 
-
 
258
    asked_names = []
-
 
259
 
231
    comment = ''
260
    comment = ''
232
    default = None
261
    default = None
233
    choices = []
262
    choices = []
234
    for line in f:
263
    for line in f:
235
        if line.startswith('%'):
264
        if line.startswith('%'):
236
            res = re.match(r'^%\s*(?:\[(.*?)\])?\s*(.*)$', line)
265
            res = re.match(r'^%\s*(?:\[(.*?)\])?\s*(.*)$', line)
237
            if not res:
266
            if not res:
238
                raise RuntimeError('Invalid command: %s' % line)
267
                raise RuntimeError('Invalid command: %s' % line)
239
            if res.group(1):
268
            if res.group(1):
240
                if not check_condition(res.group(1), defaults):
269
                if not check_condition(res.group(1), defaults):
241
                    continue
270
                    continue
242
            args = res.group(2).strip().split(' ')
271
            args = res.group(2).strip().split(' ')
243
            cmd = args[0].lower()
272
            cmd = args[0].lower()
244
            args = args[1:]
273
            args = args[1:]
245
            if cmd == 'askdefault':
-
 
246
                if isinstance(dlg, DefaultDialog):
-
 
247
                    continue
-
 
248
                res = dlg.noyes('Change kernel configuration')
-
 
249
                if res == 'n':
-
 
250
                    dlg = DefaultDialog(dlg)
-
 
251
            elif cmd == 'saveas':
274
            if cmd == 'saveas':
252
                outf.write('%s = %s\n' % (args[1],defaults[args[0]]))
275
                outf.write('%s = %s\n' % (args[1],defaults[args[0]]))
253
               
276
               
254
            continue
277
            continue
255
           
278
           
256
        if line.startswith('!'):
279
        if line.startswith('!'):
257
            # Ask a question
280
            # Ask a question
258
            res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
281
            res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
259
            if not res:
282
            if not res:
260
                raise RuntimeError("Weird line: %s" % line)
283
                raise RuntimeError("Weird line: %s" % line)
261
            varname = res.group(2)
284
            varname = res.group(2)
262
            vartype = res.group(3)
285
            vartype = res.group(3)
263
 
286
 
264
            default = defaults.get(varname,None)
287
            default = defaults.get(varname,None)
265
 
288
           
266
            if res.group(1):
289
            if res.group(1):
267
                if not check_condition(res.group(1), defaults):
290
                if not check_condition(res.group(1), defaults):
268
                    if default is not None:
291
                    if default is not None:
269
                        outf.write('#!# %s = %s\n' % (varname, default))
292
                        outf.write('#!# %s = %s\n' % (varname, default))
270
                    # Clear cumulated values
293
                    # Clear cumulated values
271
                    comment = ''
294
                    comment = ''
272
                    default = None
295
                    default = None
273
                    choices = []
296
                    choices = []
274
                    continue
297
                    continue
-
 
298
               
-
 
299
            asked_names.append((varname,comment))
-
 
300
 
-
 
301
            if default is not None and askonly and askonly != varname:
-
 
302
                outf.write('%s = %s\n' % (varname, default))
-
 
303
                continue
275
 
304
 
276
            if vartype == 'y/n':
305
            if vartype == 'y/n':
277
                result = dlg.yesno(comment, default)
306
                result = dlg.yesno(comment, default)
278
            elif vartype == 'n/y':
307
            elif vartype == 'n/y':
279
                result = dlg.noyes(comment, default)
308
                result = dlg.noyes(comment, default)
280
            elif vartype == 'choice':
309
            elif vartype == 'choice':
281
                defopt = None
310
                defopt = None
282
                if default is not None:
311
                if default is not None:
283
                    for i,(key,val) in enumerate(choices):
312
                    for i,(key,val) in enumerate(choices):
284
                        if key == default:
313
                        if key == default:
285
                            defopt = i
314
                            defopt = i
286
                            break
315
                            break
287
                result = dlg.choice(comment, choices, defopt)
316
                result = dlg.choice(comment, choices, defopt)
288
            else:
317
            else:
289
                raise RuntimeError("Bad method: %s" % vartype)
318
                raise RuntimeError("Bad method: %s" % vartype)
290
            outf.write('%s = %s\n' % (varname, result))
319
            outf.write('%s = %s\n' % (varname, result))
291
            # Remeber the selected value
320
            # Remeber the selected value
292
            defaults[varname] = result
321
            defaults[varname] = result
293
            # Clear cumulated values
322
            # Clear cumulated values
294
            comment = ''
323
            comment = ''
295
            default = None
324
            default = None
296
            choices = []
325
            choices = []
297
            continue
326
            continue
298
       
327
       
299
        if line.startswith('@'):
328
        if line.startswith('@'):
300
            # Add new line into the 'choice array' 
329
            # Add new line into the 'choice array' 
301
            res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
330
            res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
302
            if not res:
331
            if not res:
303
                raise RuntimeError("Bad line: %s" % line)
332
                raise RuntimeError("Bad line: %s" % line)
304
            if res.group(1):
333
            if res.group(1):
305
                if not check_condition(res.group(1),defaults):
334
                if not check_condition(res.group(1),defaults):
306
                    continue
335
                    continue
307
            choices.append((res.group(2), res.group(3)))
336
            choices.append((res.group(2), res.group(3)))
308
            continue
337
            continue
309
 
338
 
310
        # All other things print to output file
339
        # All other things print to output file
311
        outf.write(line)
340
        outf.write(line)
312
        if re.match(r'^#[^#]', line):
341
        if re.match(r'^#[^#]', line):
313
            # Last comment before question will be displayed to the user
342
            # Last comment before question will be displayed to the user
314
            comment = line[1:].strip()
343
            comment = line[1:].strip()
315
        elif line.startswith('## '):
344
        elif line.startswith('## '):
316
            # Set title of the dialog window
345
            # Set title of the dialog window
317
            dlg.set_title(line[2:].strip())
346
            dlg.set_title(line[2:].strip())
318
       
347
       
319
    outf.close()
348
    outf.close()
320
    f.close()
349
    f.close()
-
 
350
    return asked_names
321
 
351
 
322
def main():
352
def main():
323
    defaults = {}
353
    defaults = {}
324
    try:
354
    try:
325
        dlg = Dialog()
355
        dlg = Dialog()
326
    except NotImplementedError:
356
    except NotImplementedError:
327
        dlg = NoDialog()
357
        dlg = NoDialog()
328
 
358
 
329
    # Default run will update the configuration file
-
 
330
    # with newest options
-
 
331
    if len(sys.argv) == 2 and sys.argv[1]=='default':
359
    if len(sys.argv) == 2 and sys.argv[1]=='default':
-
 
360
        defmode = True
-
 
361
    else:
332
        dlg = DefaultDialog(dlg)
362
        defmode = False
333
 
363
 
-
 
364
    # Default run will update the configuration file
-
 
365
    # with newest options
334
    if os.path.exists(OUTPUT):
366
    if os.path.exists(OUTPUT):
335
        read_defaults(OUTPUT, defaults)
367
        read_defaults(OUTPUT, defaults)
336
   
368
   
-
 
369
    varnames = parse_config(INPUT, TMPOUTPUT, DefaultDialog(dlg), defaults)
-
 
370
    # If not in default mode, present selection of all possibilities
-
 
371
    if not defmode:
-
 
372
        defopt = 0
-
 
373
        while 1:
-
 
374
            choices = [ (x[1],defaults[x[0]]) for x in varnames ]
-
 
375
            res = dlg.menu('Configuration',choices,('save','Save'),defopt)
-
 
376
            if res == 'save':
-
 
377
                parse_config(INPUT, TMPOUTPUT, DefaultDialog(dlg), defaults)
-
 
378
                break
-
 
379
            # transfer description back to varname
-
 
380
            for i,(vname,descr) in enumerate(varnames):
-
 
381
                if res == descr:
-
 
382
                    defopt = i
-
 
383
                    break
337
    parse_config(INPUT, TMPOUTPUT, dlg, defaults)
384
            varnames = parse_config(INPUT, TMPOUTPUT, dlg, defaults,
-
 
385
                                    askonly=varnames[i][0])
-
 
386
       
-
 
387
   
338
    if os.path.exists(OUTPUT):
388
    if os.path.exists(OUTPUT):
339
        os.unlink(OUTPUT)
389
        os.unlink(OUTPUT)
340
    os.rename(TMPOUTPUT, OUTPUT)
390
    os.rename(TMPOUTPUT, OUTPUT)
341
       
391
       
342
 
392
 
343
if __name__ == '__main__':
393
if __name__ == '__main__':
344
    main()
394
    main()
345
 
395