Subversion Repositories HelenOS

Rev

Rev 3937 | Rev 4058 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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