Subversion Repositories HelenOS-historic

Rev

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

Rev 550 Rev 552
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("*** %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
       
80
       
81
    def choice(self, text, choices, defopt=None):
81
    def choice(self, text, choices, defopt=None):
82
        self.print_title()
82
        self.print_title()
83
        while 1:
83
        while 1:
84
            self._print_choice(text, choices, defopt)
84
            self._print_choice(text, choices, defopt)
85
            inp = sys.stdin.readline()
85
            inp = sys.stdin.readline()
86
            if not inp:
86
            if not inp:
87
                raise EOFError
87
                raise EOFError
88
            if not inp.strip():
88
            if not inp.strip():
89
                if defopt is not None:
89
                if defopt is not None:
90
                    return choices[defopt][0]
90
                    return choices[defopt][0]
91
                continue
91
                continue
92
            try:
92
            try:
93
                number = int(inp.strip())
93
                number = int(inp.strip())
94
            except ValueError:
94
            except ValueError:
95
                continue
95
                continue
96
            if number < 0 or number >= len(choices):
96
            if number < 0 or number >= len(choices):
97
                continue
97
                continue
98
            return choices[number][0]
98
            return choices[number][0]
99
 
99
 
100
 
100
 
101
class Dialog(NoDialog):
101
class Dialog(NoDialog):
102
    def __init__(self):
102
    def __init__(self):
103
        NoDialog.__init__(self)
103
        NoDialog.__init__(self)
104
        self.dlgcmd = os.environ.get('DIALOG','dialog')
104
        self.dlgcmd = os.environ.get('DIALOG','dialog')
105
        self.title = 'HelenOS Configuration'
105
        self.title = 'HelenOS Configuration'
106
       
106
       
107
        if os.system('%s --print-maxsize >/dev/null 2>&1' % self.dlgcmd) != 0:
107
        if os.system('%s --print-maxsize >/dev/null 2>&1' % self.dlgcmd) != 0:
108
            raise NotImplementedError
108
            raise NotImplementedError
109
 
109
 
110
    def set_title(self,text):
110
    def set_title(self,text):
111
        self.title = text
111
        self.title = text
112
       
112
       
113
    def calldlg(self,*args,**kw):
113
    def calldlg(self,*args,**kw):
114
        "Wrapper for calling 'dialog' program"
114
        "Wrapper for calling 'dialog' program"
115
        indesc, outdesc = os.pipe()
115
        indesc, outdesc = os.pipe()
116
        pid = os.fork()
116
        pid = os.fork()
117
        if not pid:
117
        if not pid:
118
            os.close(2)
118
            os.close(2)
119
            os.dup(outdesc)
119
            os.dup(outdesc)
120
            os.close(indesc)
120
            os.close(indesc)
121
           
121
           
122
            dlgargs = [self.dlgcmd,'--title',self.title]
122
            dlgargs = [self.dlgcmd,'--title',self.title]
123
            for key,val in kw.items():
123
            for key,val in kw.items():
124
                dlgargs.append('--'+key)
124
                dlgargs.append('--'+key)
125
                dlgargs.append(val)
125
                dlgargs.append(val)
126
            dlgargs += args            
126
            dlgargs += args            
127
            os.execlp(self.dlgcmd,*dlgargs)
127
            os.execlp(self.dlgcmd,*dlgargs)
128
 
128
 
129
        os.close(outdesc)
129
        os.close(outdesc)
130
        errout = os.fdopen(indesc,'r')
130
        errout = os.fdopen(indesc,'r')
131
        data = errout.read()
131
        data = errout.read()
132
        errout.close()
132
        errout.close()
133
           
133
           
134
        pid,status = os.wait()
134
        pid,status = os.wait()
135
        if not os.WIFEXITED(status):
135
        if not os.WIFEXITED(status):
136
            raise EOFError
136
            raise EOFError
137
        status = os.WEXITSTATUS(status)
137
        status = os.WEXITSTATUS(status)
138
        if status == 255:
138
        if status == 255:
139
            raise EOFError
139
            raise EOFError
140
        return status,data
140
        return status,data
141
       
141
       
142
    def yesno(self, text, default=None):
142
    def yesno(self, text, default=None):
143
        text = text + ':'
143
        text = text + ':'
144
        width = '50'
144
        width = '50'
145
        height = '5'
145
        height = '5'
146
        if len(text) < 48:
146
        if len(text) < 48:
147
            text = ' '*int(((48-len(text))/2)) + text
147
            text = ' '*int(((48-len(text))/2)) + text
148
        else:
148
        else:
149
            width = '0'
149
            width = '0'
150
            height = '0'
150
            height = '0'
151
        if default == 'n':
151
        if default == 'n':
152
            res,data = self.calldlg('--defaultno','--yesno',text,height,width)
152
            res,data = self.calldlg('--defaultno','--yesno',text,height,width)
153
        else:
153
        else:
154
            res,data = self.calldlg('--yesno',text,height,width)
154
            res,data = self.calldlg('--yesno',text,height,width)
155
 
155
 
156
        if res == 0:
156
        if res == 0:
157
            return 'y'
157
            return 'y'
158
        return 'n'
158
        return 'n'
159
   
159
   
160
    def choice(self, text, choices, defopt=None):
160
    def choice(self, text, choices, defopt=None):
161
        text = text + ':'
161
        text = text + ':'
162
        width = '50'
162
        width = '50'
163
        height = str(8 + len(choices))
163
        height = str(8 + len(choices))
164
        args = []
164
        args = []
165
        for key,val in choices:
165
        for key,val in choices:
166
            args.append(key)
166
            args.append(key)
167
            args.append(val)
167
            args.append(val)
168
 
168
 
169
        kw = {}
169
        kw = {}
170
        if defopt:
170
        if defopt:
171
            kw['default-item'] = choices[defopt][0]
171
            kw['default-item'] = choices[defopt][0]
172
        res,data = self.calldlg('--nocancel','--menu',text,height,width,
172
        res,data = self.calldlg('--nocancel','--menu',text,height,width,
173
                                str(len(choices)),*args, **kw)
173
                                str(len(choices)),*args, **kw)
174
        if res:
174
        if res:
175
            print data
175
            print data
176
            raise EOFError
176
            raise EOFError
177
        return data
177
        return data
178
   
178
   
179
def read_defaults(fname,defaults):
179
def read_defaults(fname,defaults):
180
    "Read saved values from last configuration run"
180
    "Read saved values from last configuration run"
181
    f = file(fname,'r')
181
    f = file(fname,'r')
182
    for line in f:
182
    for line in f:
183
        res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
183
        res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
184
        if res:
184
        if res:
185
            defaults[res.group(1)] = res.group(2)
185
            defaults[res.group(1)] = res.group(2)
186
    f.close()
186
    f.close()
187
 
187
 
188
def check_condition(text, defaults):
188
def check_condition(text, defaults):
189
    result = True
189
    result = True
190
    conds = text.split('&')
190
    conds = text.split('&')
191
    for cond in conds:
191
    for cond in conds:
192
        if cond.startswith('(') and cond.endswith(')'):
192
        if cond.startswith('(') and cond.endswith(')'):
193
            cond = cond[1:-1]
193
            cond = cond[1:-1]
194
        if not check_dnf(cond, defaults):
194
        if not check_dnf(cond, defaults):
195
            return False
195
            return False
196
    return True
196
    return True
197
 
197
 
198
def check_dnf(text, defaults):
198
def check_dnf(text, defaults):
199
    """
199
    """
200
    Check that the condition specified on input line is True
200
    Check that the condition specified on input line is True
201
 
201
 
202
    only CNF is supported
202
    only CNF is supported
203
    """
203
    """
204
    conds = text.split('|')
204
    conds = text.split('|')
205
    for cond in conds:
205
    for cond in conds:
206
        res = re.match(r'^(.*?)(!?=)(.*)$', cond)
206
        res = re.match(r'^(.*?)(!?=)(.*)$', cond)
207
        if not res:
207
        if not res:
208
            raise RuntimeError("Invalid condition: %s" % cond)
208
            raise RuntimeError("Invalid condition: %s" % cond)
209
        condname = res.group(1)
209
        condname = res.group(1)
210
        oper = res.group(2)
210
        oper = res.group(2)
211
        condval = res.group(3)
211
        condval = res.group(3)
212
        if not defaults.has_key(condname):
212
        if not defaults.has_key(condname):
213
            raise RuntimeError("Condition var %s does not exist: %s" % \
213
            raise RuntimeError("Condition var %s does not exist: %s" % \
214
                               (condname,text))
214
                               (condname,text))
215
 
215
 
216
        if oper=='=' and  condval == defaults[condname]:
216
        if oper=='=' and  condval == defaults[condname]:
217
            return True
217
            return True
218
        if oper == '!=' and condval != defaults[condname]:
218
        if oper == '!=' and condval != defaults[condname]:
219
            return True
219
            return True
220
    return False
220
    return False
221
 
221
 
222
def parse_config(input, output, dlg, defaults={}):
222
def parse_config(input, output, dlg, defaults={}):
223
    "Parse configuration file and create Makefile.config on the fly"
223
    "Parse configuration file and create Makefile.config on the fly"
224
    f = file(input, 'r')
224
    f = file(input, 'r')
225
    outf = file(output, 'w')
225
    outf = file(output, 'w')
226
 
226
 
227
    outf.write('#########################################\n')
227
    outf.write('#########################################\n')
228
    outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
228
    outf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
229
    outf.write('#########################################\n\n')
229
    outf.write('#########################################\n\n')
230
 
230
 
231
    comment = ''
231
    comment = ''
232
    default = None
232
    default = None
233
    choices = []
233
    choices = []
234
    for line in f:
234
    for line in f:
235
        if line.startswith('%'):
235
        if line.startswith('%'):
236
            res = re.match(r'^%\s*(?:\[(.*?)\])?\s*(.*)$', line)
236
            res = re.match(r'^%\s*(?:\[(.*?)\])?\s*(.*)$', line)
237
            if not res:
237
            if not res:
238
                raise RuntimeError('Invalid command: %s' % line)
238
                raise RuntimeError('Invalid command: %s' % line)
239
            if res.group(1):
239
            if res.group(1):
240
                if not check_condition(res.group(1), defaults):
240
                if not check_condition(res.group(1), defaults):
241
                    continue
241
                    continue
242
            args = res.group(2).strip().split(' ')
242
            args = res.group(2).strip().split(' ')
243
            cmd = args[0].lower()
243
            cmd = args[0].lower()
244
            args = args[1:]
244
            args = args[1:]
245
            if cmd == 'askdefault':
245
            if cmd == 'askdefault':
246
                if isinstance(dlg, DefaultDialog):
246
                if isinstance(dlg, DefaultDialog):
247
                    continue
247
                    continue
248
                res = dlg.noyes('Change kernel configuration')
248
                res = dlg.noyes('Change kernel configuration')
249
                if res == 'n':
249
                if res == 'n':
250
                    dlg = DefaultDialog(dlg)
250
                    dlg = DefaultDialog(dlg)
251
            elif cmd == 'saveas':
251
            elif cmd == 'saveas':
252
                outf.write('%s = %s\n' % (args[1],defaults[args[0]]))
252
                outf.write('%s = %s\n' % (args[1],defaults[args[0]]))
253
               
253
               
254
            continue
254
            continue
255
           
255
           
256
        if line.startswith('!'):
256
        if line.startswith('!'):
257
            # Ask a question
257
            # Ask a question
258
            res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
258
            res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
259
            if not res:
259
            if not res:
260
                raise RuntimeError("Weird line: %s" % line)
260
                raise RuntimeError("Weird line: %s" % line)
261
            varname = res.group(2)
261
            varname = res.group(2)
262
            vartype = res.group(3)
262
            vartype = res.group(3)
263
 
263
 
264
            default = defaults.get(varname,None)
264
            default = defaults.get(varname,None)
265
 
265
 
266
            if res.group(1):
266
            if res.group(1):
267
                if not check_condition(res.group(1), defaults):
267
                if not check_condition(res.group(1), defaults):
268
                    if default is not None:
268
                    if default is not None:
269
                        outf.write('#!# %s = %s\n' % (varname, default))
269
                        outf.write('#!# %s = %s\n' % (varname, default))
270
                    # Clear cumulated values
270
                    # Clear cumulated values
271
                    comment = ''
271
                    comment = ''
272
                    default = None
272
                    default = None
273
                    choices = []
273
                    choices = []
274
                    continue
274
                    continue
275
 
275
 
276
            if vartype == 'y/n':
276
            if vartype == 'y/n':
277
                result = dlg.yesno(comment, default)
277
                result = dlg.yesno(comment, default)
278
            elif vartype == 'n/y':
278
            elif vartype == 'n/y':
279
                result = dlg.noyes(comment, default)
279
                result = dlg.noyes(comment, default)
280
            elif vartype == 'choice':
280
            elif vartype == 'choice':
281
                defopt = None
281
                defopt = None
282
                if default is not None:
282
                if default is not None:
283
                    for i,(key,val) in enumerate(choices):
283
                    for i,(key,val) in enumerate(choices):
284
                        if key == default:
284
                        if key == default:
285
                            defopt = i
285
                            defopt = i
286
                            break
286
                            break
287
                result = dlg.choice(comment, choices, defopt)
287
                result = dlg.choice(comment, choices, defopt)
288
            else:
288
            else:
289
                raise RuntimeError("Bad method: %s" % vartype)
289
                raise RuntimeError("Bad method: %s" % vartype)
290
            outf.write('%s = %s\n' % (varname, result))
290
            outf.write('%s = %s\n' % (varname, result))
291
            # Remeber the selected value
291
            # Remeber the selected value
292
            defaults[varname] = result
292
            defaults[varname] = result
293
            # Clear cumulated values
293
            # Clear cumulated values
294
            comment = ''
294
            comment = ''
295
            default = None
295
            default = None
296
            choices = []
296
            choices = []
297
            continue
297
            continue
298
       
298
       
299
        if line.startswith('@'):
299
        if line.startswith('@'):
300
            # Add new line into the 'choice array' 
300
            # Add new line into the 'choice array' 
301
            res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
301
            res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
302
            if not res:
302
            if not res:
303
                raise RuntimeError("Bad line: %s" % line)
303
                raise RuntimeError("Bad line: %s" % line)
304
            if res.group(1):
304
            if res.group(1):
305
                if not check_condition(res.group(1),defaults):
305
                if not check_condition(res.group(1),defaults):
306
                    continue
306
                    continue
307
            choices.append((res.group(2), res.group(3)))
307
            choices.append((res.group(2), res.group(3)))
308
            continue
308
            continue
309
 
309
 
310
        # All other things print to output file
310
        # All other things print to output file
311
        outf.write(line)
311
        outf.write(line)
312
        if re.match(r'^#[^#]', line):
312
        if re.match(r'^#[^#]', line):
313
            # Last comment before question will be displayed to the user
313
            # Last comment before question will be displayed to the user
314
            comment = line[1:].strip()
314
            comment = line[1:].strip()
315
        elif line.startswith('##'):
315
        elif line.startswith('## '):
316
            # Set title of the dialog window
316
            # Set title of the dialog window
317
            dlg.set_title(line[2:].strip())
317
            dlg.set_title(line[2:].strip())
318
       
318
       
319
    outf.close()
319
    outf.close()
320
    f.close()
320
    f.close()
321
 
321
 
322
def main():
322
def main():
323
    defaults = {}
323
    defaults = {}
324
    try:
324
    try:
325
        dlg = Dialog()
325
        dlg = Dialog()
326
    except NotImplementedError:
326
    except NotImplementedError:
327
        dlg = NoDialog()
327
        dlg = NoDialog()
328
 
328
 
329
    # Default run will update the configuration file
329
    # Default run will update the configuration file
330
    # with newest options
330
    # with newest options
331
    if len(sys.argv) == 2 and sys.argv[1]=='default':
331
    if len(sys.argv) == 2 and sys.argv[1]=='default':
332
        dlg = DefaultDialog(dlg)
332
        dlg = DefaultDialog(dlg)
333
 
333
 
334
    if os.path.exists(OUTPUT):
334
    if os.path.exists(OUTPUT):
335
        read_defaults(OUTPUT, defaults)
335
        read_defaults(OUTPUT, defaults)
336
   
336
   
337
    parse_config(INPUT, TMPOUTPUT, dlg, defaults)
337
    parse_config(INPUT, TMPOUTPUT, dlg, defaults)
338
    if os.path.exists(OUTPUT):
338
    if os.path.exists(OUTPUT):
339
        os.unlink(OUTPUT)
339
        os.unlink(OUTPUT)
340
    os.rename(TMPOUTPUT, OUTPUT)
340
    os.rename(TMPOUTPUT, OUTPUT)
341
       
341
       
342
 
342
 
343
if __name__ == '__main__':
343
if __name__ == '__main__':
344
    main()
344
    main()
345
 
345