Subversion Repositories HelenOS

Rev

Rev 4343 | 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
#
3107 svoboda 3
# Copyright (c) 2006 Ondrej Palkovsky
4340 svoboda 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
"""
4340 svoboda 31
HelenOS configuration system
1802 decky 32
"""
33
import sys
34
import os
35
import re
36
import commands
4340 svoboda 37
import xtui
1802 decky 38
 
39
INPUT = sys.argv[1]
4342 svoboda 40
MAKEFILE = 'Makefile.config'
41
MACROS = 'config.h'
42
DEFS = 'config.defs'
1802 decky 43
 
4340 svoboda 44
def read_defaults(fname, defaults):
45
    "Read saved values from last configuration run"
46
 
4342 svoboda 47
    inf = file(fname, 'r')
4340 svoboda 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
 
4340 svoboda 56
def check_condition(text, defaults, ask_names):
4343 svoboda 57
    "Check that the condition specified on input line is True (only CNF and DNF is supported)"
4340 svoboda 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
 
4340 svoboda 85
def check_inside(text, defaults, ctype):
4343 svoboda 86
    "Check for condition"
4340 svoboda 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]
4344 svoboda 106
            if (varval == '*'):
107
                varval = 'y'
4340 svoboda 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
 
4340 svoboda 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
 
4340 svoboda 177
def yes_no(default):
178
    "Return '*' if yes, ' ' if no"
179
 
180
    if (default == 'y'):
181
        return '*'
182
 
183
    return ' '
1802 decky 184
 
4340 svoboda 185
def subchoice(screen, name, choices, default):
186
    "Return choice of choices"
187
 
188
    maxkey = 0
189
    for key, val in choices:
190
        length = len(key)
191
        if (length > maxkey):
192
            maxkey = length
193
 
194
    options = []
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
203
 
204
    (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
205
 
206
    if (button == 'cancel'):
207
        return None
208
 
209
    return choices[value][0]
1802 decky 210
 
4340 svoboda 211
def check_choices(defaults, ask_names):
212
    "Check whether all accessible variables have a default"
213
 
214
    for varname, vartype, name, choices, cond in ask_names:
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
 
4342 svoboda 223
def create_output(mkname, mcname, dfname, defaults, ask_names):
4340 svoboda 224
    "Create output configuration"
225
 
4342 svoboda 226
    revision = commands.getoutput('svnversion . 2> /dev/null')
227
    timestamp = commands.getoutput('date "+%Y-%m-%d %H:%M:%S"')
4340 svoboda 228
 
4342 svoboda 229
    outmk = file(mkname, 'w')
230
    outmc = file(mcname, 'w')
231
    outdf = file(dfname, 'w')
4340 svoboda 232
 
4342 svoboda 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
 
4340 svoboda 246
    for varname, vartype, name, choices, cond in ask_names:
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]
4344 svoboda 254
            if (default == '*'):
255
                default = 'y'
4340 svoboda 256
 
4342 svoboda 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:
4343 svoboda 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))
4340 svoboda 266
 
4342 svoboda 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():
4340 svoboda 280
    defaults = {}
281
    ask_names = []
1802 decky 282
 
4340 svoboda 283
    # Parse configuration file
284
    parse_config(INPUT, ask_names)
1802 decky 285
 
4340 svoboda 286
    # Read defaults from previous run
4342 svoboda 287
    if os.path.exists(MAKEFILE):
288
        read_defaults(MAKEFILE, defaults)
1802 decky 289
 
4340 svoboda 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)):
4342 svoboda 293
            create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
4340 svoboda 294
            return 0
295
 
4341 svoboda 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
 
4340 svoboda 302
    screen = xtui.screen_init()
303
    try:
304
        selname = None
305
        while True:
306
 
4343 svoboda 307
            # Cancel out all defaults which have to be deduced
308
            for varname, vartype, name, choices, cond in ask_names:
4344 svoboda 309
                if ((vartype == 'y') and (defaults.has_key(varname)) and (defaults[varname] == '*')):
4343 svoboda 310
                    defaults[varname] = None
311
 
4340 svoboda 312
            options = []
313
            opt2row = {}
314
            position = None
315
            cnt = 0
316
            for varname, vartype, name, choices, cond in ask_names:
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):
4342 svoboda 337
                        defaults[varname] = choices[0][0]
338
                        continue
4340 svoboda 339
 
340
                    options.append("     %s [%s] --> " % (name, default))
4342 svoboda 341
                elif (vartype == 'y'):
4344 svoboda 342
                    defaults[varname] = '*'
4342 svoboda 343
                    continue
4340 svoboda 344
                elif (vartype == 'y/n'):
345
                    if (default == None):
346
                        default = 'y'
347
                        defaults[varname] = default
348
                    options.append(" <%s> %s " % (yes_no(default), name))
349
                elif (vartype == 'n/y'):
350
                    if (default == None):
351
                        default = 'n'
352
                        defaults[varname] = default
353
                    options.append(" <%s> %s " % (yes_no(default), name))
354
                else:
355
                    raise RuntimeError("Unknown variable type: %s" % vartype)
356
 
357
                opt2row[cnt] = (varname, vartype, name, choices)
358
 
359
                cnt += 1
360
 
361
            (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
362
 
363
            if (button == 'cancel'):
364
                return 'Configuration canceled'
365
 
366
            if (not opt2row.has_key(value)):
367
                raise RuntimeError("Error selecting value: %s" % value)
368
 
369
            (selname, seltype, name, choices) = opt2row[value]
370
 
371
            if (not defaults.has_key(selname)):
372
                    default = None
373
            else:
374
                default = defaults[selname]
375
 
376
            if (button == 'done'):
377
                if (check_choices(defaults, ask_names)):
378
                    break
379
                else:
380
                    xtui.error_dialog(screen, 'Error', 'Some options have still undefined values.')
381
                    continue
382
 
383
            if (seltype == 'choice'):
384
                defaults[selname] = subchoice(screen, name, choices, default)
385
            elif ((seltype == 'y/n') or (seltype == 'n/y')):
386
                if (defaults[selname] == 'y'):
387
                    defaults[selname] = 'n'
388
                else:
389
                    defaults[selname] = 'y'
390
    finally:
391
        xtui.screen_done(screen)
392
 
4342 svoboda 393
    create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
4340 svoboda 394
    return 0
1802 decky 395
 
396
if __name__ == '__main__':
4341 svoboda 397
    sys.exit(main())