Subversion Repositories HelenOS

Rev

Rev 3909 | Rev 4054 | 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]
106
 
107
        if (ctype == 'cnf'):
108
            if (oper == '=') and (condval == varval):
109
                return True
110
 
111
            if (oper == '!=') and (condval != varval):
112
                return True
113
        else:
114
            if (oper == '=') and (condval != varval):
115
                return False
116
 
117
            if (oper == '!=') and (condval == varval):
118
                return False
119
 
120
    if (ctype == 'cnf'):
121
        return False
122
 
123
    return True
1802 decky 124
 
3803 decky 125
def parse_config(fname, ask_names):
126
    "Parse configuration file"
127
 
128
    inf = file(fname, 'r')
129
 
130
    name = ''
131
    choices = []
132
 
133
    for line in inf:
134
 
135
        if (line.startswith('!')):
136
            # Ask a question
137
            res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
138
 
139
            if (not res):
140
                raise RuntimeError("Weird line: %s" % line)
141
 
142
            cond = res.group(1)
143
            varname = res.group(2)
144
            vartype = res.group(3)
145
 
146
            ask_names.append((varname, vartype, name, choices, cond))
147
            name = ''
148
            choices = []
149
            continue
150
 
151
        if (line.startswith('@')):
152
            # Add new line into the 'choices' array
153
            res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
154
 
155
            if not res:
156
                raise RuntimeError("Bad line: %s" % line)
157
 
158
            choices.append((res.group(2), res.group(3)))
159
            continue
160
 
161
        if (line.startswith('%')):
162
            # Name of the option
163
            name = line[1:].strip()
164
            continue
165
 
166
        if ((line.startswith('#')) or (line == '\n')):
167
            # Comment or empty line
168
            continue
169
 
170
 
171
        raise RuntimeError("Unknown syntax: %s" % line)
172
 
173
    inf.close()
1802 decky 174
 
3803 decky 175
def yes_no(default):
176
    "Return '*' if yes, ' ' if no"
177
 
178
    if (default == 'y'):
179
        return '*'
180
 
181
    return ' '
1802 decky 182
 
3804 decky 183
def subchoice(screen, name, choices, default):
3803 decky 184
    "Return choice of choices"
185
 
3804 decky 186
    maxkey = 0
187
    for key, val in choices:
188
        length = len(key)
189
        if (length > maxkey):
190
            maxkey = length
3803 decky 191
 
192
    options = []
3804 decky 193
    position = None
194
    cnt = 0
195
    for key, val in choices:
196
        if ((default) and (key == default)):
197
            position = cnt
198
 
199
        options.append(" %-*s  %s " % (maxkey, key, val))
200
        cnt += 1
3803 decky 201
 
3804 decky 202
    (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
3803 decky 203
 
3804 decky 204
    if (button == 'cancel'):
3803 decky 205
        return None
206
 
3804 decky 207
    return choices[value][0]
1802 decky 208
 
3803 decky 209
def check_choices(defaults, ask_names):
210
    "Check whether all accessible variables have a default"
211
 
3804 decky 212
    for varname, vartype, name, choices, cond in ask_names:
3803 decky 213
        if ((cond) and (not check_condition(cond, defaults, ask_names))):
214
            continue
215
 
216
        if (not defaults.has_key(varname)):
217
            return False
218
 
219
    return True
1802 decky 220
 
3881 decky 221
def create_output(mkname, mcname, dfname, defaults, ask_names):
3803 decky 222
    "Create output configuration"
223
 
3881 decky 224
    revision = commands.getoutput('svnversion . 2> /dev/null')
225
    timestamp = commands.getoutput('date "+%Y-%m-%d %H:%M:%S"')
3803 decky 226
 
3881 decky 227
    outmk = file(mkname, 'w')
228
    outmc = file(mcname, 'w')
229
    outdf = file(dfname, 'w')
3803 decky 230
 
3881 decky 231
    outmk.write('#########################################\n')
232
    outmk.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
233
    outmk.write('#########################################\n\n')
234
 
235
    outmc.write('/***************************************\n')
236
    outmc.write(' * AUTO-GENERATED FILE, DO NOT EDIT!!! *\n')
237
    outmc.write(' ***************************************/\n\n')
238
 
239
    outdf.write('#########################################\n')
240
    outdf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
241
    outdf.write('#########################################\n\n')
242
    outdf.write('CONFIG_DEFS =')
243
 
3804 decky 244
    for varname, vartype, name, choices, cond in ask_names:
3803 decky 245
        if ((cond) and (not check_condition(cond, defaults, ask_names))):
246
            continue
247
 
248
        if (not defaults.has_key(varname)):
249
            default = ''
250
        else:
251
            default = defaults[varname]
252
 
3881 decky 253
        outmk.write('# %s\n%s = %s\n\n' % (name, varname, default))
254
 
255
        if ((vartype == "y") or (vartype == "y/n") or (vartype == "n/y")):
256
            if (default == "y"):
257
                outmc.write('/* %s */\n#define %s\n\n' % (name, varname))
258
                outdf.write(' -D%s' % varname)
259
        else:
3909 decky 260
            outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, default, varname, default))
261
            outdf.write(' -D%s=%s -D%s_%s' % (varname, default, varname, default))
3803 decky 262
 
3881 decky 263
    outmk.write('REVISION = %s\n' % revision)
264
    outmk.write('TIMESTAMP = %s\n' % timestamp)
265
 
266
    outmc.write('#define REVISION %s\n' % revision)
267
    outmc.write('#define TIMESTAMP %s\n' % timestamp)
268
 
269
    outdf.write(' "-DREVISION=%s" "-DTIMESTAMP=%s"\n' % (revision, timestamp))
270
 
271
    outmk.close()
272
    outmc.close()
273
    outdf.close()
1802 decky 274
 
275
def main():
3803 decky 276
    defaults = {}
277
    ask_names = []
1802 decky 278
 
3803 decky 279
    # Parse configuration file
280
    parse_config(INPUT, ask_names)
1802 decky 281
 
3803 decky 282
    # Read defaults from previous run
3881 decky 283
    if os.path.exists(MAKEFILE):
284
        read_defaults(MAKEFILE, defaults)
1802 decky 285
 
3803 decky 286
    # Default mode: only check defaults and regenerate configuration
287
    if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
288
        if (check_choices(defaults, ask_names)):
3881 decky 289
            create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
3803 decky 290
            return 0
291
 
3808 decky 292
    # Check mode: only check defaults
293
    if ((len(sys.argv) >= 3) and (sys.argv[2] == 'check')):
294
        if (check_choices(defaults, ask_names)):
295
            return 0
296
        return 1
297
 
3804 decky 298
    screen = xtui.screen_init()
3803 decky 299
    try:
300
        selname = None
301
        while True:
302
 
3916 decky 303
            # Cancel out all defaults which have to be deduced
304
            for varname, vartype, name, choices, cond in ask_names:
305
                if (vartype == 'y'):
306
                    defaults[varname] = None
307
 
3803 decky 308
            options = []
309
            opt2row = {}
310
            position = None
311
            cnt = 0
3804 decky 312
            for varname, vartype, name, choices, cond in ask_names:
3803 decky 313
 
314
                if ((cond) and (not check_condition(cond, defaults, ask_names))):
315
                    continue
316
 
317
                if (varname == selname):
318
                    position = cnt
319
 
320
                if (not defaults.has_key(varname)):
321
                    default = None
322
                else:
323
                    default = defaults[varname]
324
 
325
                if (vartype == 'choice'):
326
                    # Check if the default is an acceptable value
327
                    if ((default) and (not default in [choice[0] for choice in choices])):
328
                        default = None
329
                        defaults.pop(varname)
330
 
331
                    # If there is just one option, use it
332
                    if (len(choices) == 1):
3881 decky 333
                        defaults[varname] = choices[0][0]
334
                        continue
3803 decky 335
 
336
                    options.append("     %s [%s] --> " % (name, default))
3881 decky 337
                elif (vartype == 'y'):
338
                    defaults[varname] = 'y'
339
                    continue
3803 decky 340
                elif (vartype == 'y/n'):
341
                    if (default == None):
342
                        default = 'y'
343
                        defaults[varname] = default
344
                    options.append(" <%s> %s " % (yes_no(default), name))
345
                elif (vartype == 'n/y'):
346
                    if (default == None):
347
                        default = 'n'
348
                        defaults[varname] = default
349
                    options.append(" <%s> %s " % (yes_no(default), name))
350
                else:
351
                    raise RuntimeError("Unknown variable type: %s" % vartype)
352
 
3804 decky 353
                opt2row[cnt] = (varname, vartype, name, choices)
3803 decky 354
 
355
                cnt += 1
356
 
3804 decky 357
            (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
3803 decky 358
 
3804 decky 359
            if (button == 'cancel'):
3803 decky 360
                return 'Configuration canceled'
361
 
3804 decky 362
            if (not opt2row.has_key(value)):
363
                raise RuntimeError("Error selecting value: %s" % value)
3803 decky 364
 
3804 decky 365
            (selname, seltype, name, choices) = opt2row[value]
3803 decky 366
 
3804 decky 367
            if (not defaults.has_key(selname)):
368
                    default = None
369
            else:
370
                default = defaults[selname]
371
 
372
            if (button == 'done'):
3803 decky 373
                if (check_choices(defaults, ask_names)):
374
                    break
375
                else:
3804 decky 376
                    xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
3803 decky 377
                    continue
378
 
379
            if (seltype == 'choice'):
3804 decky 380
                defaults[selname] = subchoice(screen, name, choices, default)
3803 decky 381
            elif ((seltype == 'y/n') or (seltype == 'n/y')):
382
                if (defaults[selname] == 'y'):
383
                    defaults[selname] = 'n'
384
                else:
385
                    defaults[selname] = 'y'
386
    finally:
3804 decky 387
        xtui.screen_done(screen)
3803 decky 388
 
3881 decky 389
    create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
3803 decky 390
    return 0
1802 decky 391
 
392
if __name__ == '__main__':
3821 decky 393
    sys.exit(main())