Subversion Repositories HelenOS

Rev

Rev 4081 | 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'
4058 decky 43
PRECONF = 'defaults'
1802 decky 44
 
3803 decky 45
def read_defaults(fname, defaults):
46
    "Read saved values from last configuration run"
47
 
3881 decky 48
    inf = file(fname, 'r')
3803 decky 49
 
50
    for line in inf:
51
        res = re.match(r'^(?:#!# )?([^#]\w*)\s*=\s*(.*?)\s*$', line)
52
        if (res):
53
            defaults[res.group(1)] = res.group(2)
54
 
55
    inf.close()
1802 decky 56
 
3803 decky 57
def check_condition(text, defaults, ask_names):
3916 decky 58
    "Check that the condition specified on input line is True (only CNF and DNF is supported)"
3803 decky 59
 
60
    ctype = 'cnf'
61
 
62
    if ((')|' in text) or ('|(' in text)):
63
        ctype = 'dnf'
64
 
65
    if (ctype == 'cnf'):
66
        conds = text.split('&')
67
    else:
68
        conds = text.split('|')
69
 
70
    for cond in conds:
71
        if (cond.startswith('(')) and (cond.endswith(')')):
72
            cond = cond[1:-1]
73
 
74
        inside = check_inside(cond, defaults, ctype)
75
 
76
        if (ctype == 'cnf') and (not inside):
77
            return False
78
 
79
        if (ctype == 'dnf') and (inside):
80
            return True
81
 
82
    if (ctype == 'cnf'):
83
        return True
84
    return False
1802 decky 85
 
3803 decky 86
def check_inside(text, defaults, ctype):
3916 decky 87
    "Check for condition"
3803 decky 88
 
89
    if (ctype == 'cnf'):
90
        conds = text.split('|')
91
    else:
92
        conds = text.split('&')
93
 
94
    for cond in conds:
95
        res = re.match(r'^(.*?)(!?=)(.*)$', cond)
96
        if (not res):
97
            raise RuntimeError("Invalid condition: %s" % cond)
98
 
99
        condname = res.group(1)
100
        oper = res.group(2)
101
        condval = res.group(3)
102
 
103
        if (not defaults.has_key(condname)):
104
            varval = ''
105
        else:
106
            varval = defaults[condname]
3937 decky 107
            if (varval == '*'):
108
                varval = 'y'
3803 decky 109
 
110
        if (ctype == 'cnf'):
111
            if (oper == '=') and (condval == varval):
112
                return True
113
 
114
            if (oper == '!=') and (condval != varval):
115
                return True
116
        else:
117
            if (oper == '=') and (condval != varval):
118
                return False
119
 
120
            if (oper == '!=') and (condval == varval):
121
                return False
122
 
123
    if (ctype == 'cnf'):
124
        return False
125
 
126
    return True
1802 decky 127
 
3803 decky 128
def parse_config(fname, ask_names):
129
    "Parse configuration file"
130
 
131
    inf = file(fname, 'r')
132
 
133
    name = ''
134
    choices = []
135
 
136
    for line in inf:
137
 
138
        if (line.startswith('!')):
139
            # Ask a question
140
            res = re.search(r'!\s*(?:\[(.*?)\])?\s*([^\s]+)\s*\((.*)\)\s*$', line)
141
 
142
            if (not res):
143
                raise RuntimeError("Weird line: %s" % line)
144
 
145
            cond = res.group(1)
146
            varname = res.group(2)
147
            vartype = res.group(3)
148
 
149
            ask_names.append((varname, vartype, name, choices, cond))
150
            name = ''
151
            choices = []
152
            continue
153
 
154
        if (line.startswith('@')):
155
            # Add new line into the 'choices' array
156
            res = re.match(r'@\s*(?:\[(.*?)\])?\s*"(.*?)"\s*(.*)$', line)
157
 
158
            if not res:
159
                raise RuntimeError("Bad line: %s" % line)
160
 
161
            choices.append((res.group(2), res.group(3)))
162
            continue
163
 
164
        if (line.startswith('%')):
165
            # Name of the option
166
            name = line[1:].strip()
167
            continue
168
 
169
        if ((line.startswith('#')) or (line == '\n')):
170
            # Comment or empty line
171
            continue
172
 
173
 
174
        raise RuntimeError("Unknown syntax: %s" % line)
175
 
176
    inf.close()
1802 decky 177
 
3803 decky 178
def yes_no(default):
179
    "Return '*' if yes, ' ' if no"
180
 
181
    if (default == 'y'):
182
        return '*'
183
 
184
    return ' '
1802 decky 185
 
3804 decky 186
def subchoice(screen, name, choices, default):
3803 decky 187
    "Return choice of choices"
188
 
3804 decky 189
    maxkey = 0
190
    for key, val in choices:
191
        length = len(key)
192
        if (length > maxkey):
193
            maxkey = length
3803 decky 194
 
195
    options = []
3804 decky 196
    position = None
197
    cnt = 0
198
    for key, val in choices:
199
        if ((default) and (key == default)):
200
            position = cnt
201
 
202
        options.append(" %-*s  %s " % (maxkey, key, val))
203
        cnt += 1
3803 decky 204
 
3804 decky 205
    (button, value) = xtui.choice_window(screen, name, 'Choose value', options, position)
3803 decky 206
 
3804 decky 207
    if (button == 'cancel'):
3803 decky 208
        return None
209
 
3804 decky 210
    return choices[value][0]
1802 decky 211
 
3803 decky 212
def check_choices(defaults, ask_names):
213
    "Check whether all accessible variables have a default"
214
 
3804 decky 215
    for varname, vartype, name, choices, cond in ask_names:
3803 decky 216
        if ((cond) and (not check_condition(cond, defaults, ask_names))):
217
            continue
218
 
219
        if (not defaults.has_key(varname)):
220
            return False
221
 
222
    return True
1802 decky 223
 
3881 decky 224
def create_output(mkname, mcname, dfname, defaults, ask_names):
3803 decky 225
    "Create output configuration"
226
 
3881 decky 227
    revision = commands.getoutput('svnversion . 2> /dev/null')
228
    timestamp = commands.getoutput('date "+%Y-%m-%d %H:%M:%S"')
3803 decky 229
 
3881 decky 230
    outmk = file(mkname, 'w')
231
    outmc = file(mcname, 'w')
232
    outdf = file(dfname, 'w')
3803 decky 233
 
3881 decky 234
    outmk.write('#########################################\n')
235
    outmk.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
236
    outmk.write('#########################################\n\n')
237
 
238
    outmc.write('/***************************************\n')
239
    outmc.write(' * AUTO-GENERATED FILE, DO NOT EDIT!!! *\n')
240
    outmc.write(' ***************************************/\n\n')
241
 
242
    outdf.write('#########################################\n')
243
    outdf.write('## AUTO-GENERATED FILE, DO NOT EDIT!!! ##\n')
244
    outdf.write('#########################################\n\n')
245
    outdf.write('CONFIG_DEFS =')
246
 
3804 decky 247
    for varname, vartype, name, choices, cond in ask_names:
3803 decky 248
        if ((cond) and (not check_condition(cond, defaults, ask_names))):
249
            continue
250
 
251
        if (not defaults.has_key(varname)):
252
            default = ''
253
        else:
254
            default = defaults[varname]
3937 decky 255
            if (default == '*'):
256
                default = 'y'
3803 decky 257
 
3881 decky 258
        outmk.write('# %s\n%s = %s\n\n' % (name, varname, default))
259
 
4081 decky 260
        if ((vartype == "y") or (vartype == "n") or (vartype == "y/n") or (vartype == "n/y")):
3881 decky 261
            if (default == "y"):
262
                outmc.write('/* %s */\n#define %s\n\n' % (name, varname))
263
                outdf.write(' -D%s' % varname)
264
        else:
3909 decky 265
            outmc.write('/* %s */\n#define %s %s\n#define %s_%s\n\n' % (name, varname, default, varname, default))
266
            outdf.write(' -D%s=%s -D%s_%s' % (varname, default, varname, default))
3803 decky 267
 
3881 decky 268
    outmk.write('REVISION = %s\n' % revision)
269
    outmk.write('TIMESTAMP = %s\n' % timestamp)
270
 
271
    outmc.write('#define REVISION %s\n' % revision)
272
    outmc.write('#define TIMESTAMP %s\n' % timestamp)
273
 
274
    outdf.write(' "-DREVISION=%s" "-DTIMESTAMP=%s"\n' % (revision, timestamp))
275
 
276
    outmk.close()
277
    outmc.close()
278
    outdf.close()
1802 decky 279
 
4058 decky 280
def sorted_dir(root):
281
    list = os.listdir(root)
282
    list.sort()
283
    return list
284
 
285
def read_preconfigured(root, fname, screen, defaults):
286
    options = []
287
    opt2path = {}
288
    cnt = 0
289
 
290
    # Look for profiles
291
    for name in sorted_dir(root):
292
        path = os.path.join(root, name)
293
        canon = os.path.join(path, fname)
294
 
295
        if ((os.path.isdir(path)) and (os.path.exists(canon)) and (os.path.isfile(canon))):
296
            subprofile = False
297
 
298
            # Look for subprofiles
299
            for subname in sorted_dir(path):
300
                subpath = os.path.join(path, subname)
301
                subcanon = os.path.join(subpath, fname)
302
 
303
                if ((os.path.isdir(subpath)) and (os.path.exists(subcanon)) and (os.path.isfile(subcanon))):
304
                    subprofile = True
305
                    options.append("%s (%s)" % (name, subname))
306
                    opt2path[cnt] = (canon, subcanon)
307
                    cnt += 1
308
 
309
            if (not subprofile):
310
                options.append(name)
311
                opt2path[cnt] = (canon, None)
312
                cnt += 1
313
 
314
    (button, value) = xtui.choice_window(screen, 'Load preconfigured defaults', 'Choose configuration profile', options, None)
315
 
316
    if (button == 'cancel'):
317
        return None
318
 
319
    read_defaults(opt2path[value][0], defaults)
320
    if (opt2path[value][1] != None):
321
        read_defaults(opt2path[value][1], defaults)
322
 
1802 decky 323
def main():
3803 decky 324
    defaults = {}
325
    ask_names = []
1802 decky 326
 
3803 decky 327
    # Parse configuration file
328
    parse_config(INPUT, ask_names)
1802 decky 329
 
3803 decky 330
    # Read defaults from previous run
3881 decky 331
    if os.path.exists(MAKEFILE):
332
        read_defaults(MAKEFILE, defaults)
1802 decky 333
 
3803 decky 334
    # Default mode: only check defaults and regenerate configuration
335
    if ((len(sys.argv) >= 3) and (sys.argv[2] == 'default')):
336
        if (check_choices(defaults, ask_names)):
3881 decky 337
            create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
3803 decky 338
            return 0
339
 
3808 decky 340
    # Check mode: only check defaults
341
    if ((len(sys.argv) >= 3) and (sys.argv[2] == 'check')):
342
        if (check_choices(defaults, ask_names)):
343
            return 0
344
        return 1
345
 
3804 decky 346
    screen = xtui.screen_init()
3803 decky 347
    try:
348
        selname = None
4058 decky 349
        position = None
3803 decky 350
        while True:
351
 
3916 decky 352
            # Cancel out all defaults which have to be deduced
353
            for varname, vartype, name, choices, cond in ask_names:
3937 decky 354
                if ((vartype == 'y') and (defaults.has_key(varname)) and (defaults[varname] == '*')):
3916 decky 355
                    defaults[varname] = None
356
 
3803 decky 357
            options = []
358
            opt2row = {}
4058 decky 359
            cnt = 1
360
 
361
            options.append("  --- Load preconfigured defaults ... ")
362
 
3804 decky 363
            for varname, vartype, name, choices, cond in ask_names:
3803 decky 364
 
365
                if ((cond) and (not check_condition(cond, defaults, ask_names))):
366
                    continue
367
 
368
                if (varname == selname):
369
                    position = cnt
370
 
371
                if (not defaults.has_key(varname)):
372
                    default = None
373
                else:
374
                    default = defaults[varname]
375
 
376
                if (vartype == 'choice'):
377
                    # Check if the default is an acceptable value
378
                    if ((default) and (not default in [choice[0] for choice in choices])):
379
                        default = None
380
                        defaults.pop(varname)
381
 
382
                    # If there is just one option, use it
383
                    if (len(choices) == 1):
3881 decky 384
                        defaults[varname] = choices[0][0]
385
                        continue
3803 decky 386
 
4054 decky 387
                    if (default == None):
388
                        options.append("?     %s --> " % name)
389
                    else:
390
                        options.append("      %s [%s] --> " % (name, default))
3881 decky 391
                elif (vartype == 'y'):
3937 decky 392
                    defaults[varname] = '*'
3881 decky 393
                    continue
4080 decky 394
                elif (vartype == 'n'):
395
                    defaults[varname] = 'n'
396
                    continue
3803 decky 397
                elif (vartype == 'y/n'):
398
                    if (default == None):
399
                        default = 'y'
400
                        defaults[varname] = default
4054 decky 401
                    options.append("  <%s> %s " % (yes_no(default), name))
3803 decky 402
                elif (vartype == 'n/y'):
403
                    if (default == None):
404
                        default = 'n'
405
                        defaults[varname] = default
4054 decky 406
                    options.append("  <%s> %s " % (yes_no(default), name))
3803 decky 407
                else:
408
                    raise RuntimeError("Unknown variable type: %s" % vartype)
409
 
3804 decky 410
                opt2row[cnt] = (varname, vartype, name, choices)
3803 decky 411
 
412
                cnt += 1
413
 
4058 decky 414
            if (position >= options):
415
                position = None
416
 
3804 decky 417
            (button, value) = xtui.choice_window(screen, 'HelenOS configuration', 'Choose configuration option', options, position)
3803 decky 418
 
3804 decky 419
            if (button == 'cancel'):
3803 decky 420
                return 'Configuration canceled'
421
 
4062 decky 422
            if (button == 'done'):
423
                if (check_choices(defaults, ask_names)):
424
                    break
425
                else:
426
                    xtui.error_dialog(screen, 'Error', 'Some options have still undefined values. These options are marked with the "?" sign.')
427
                    continue
428
 
4058 decky 429
            if (value == 0):
430
                read_preconfigured(PRECONF, MAKEFILE, screen, defaults)
431
                position = 1
432
                continue
433
 
434
            position = None
3804 decky 435
            if (not opt2row.has_key(value)):
436
                raise RuntimeError("Error selecting value: %s" % value)
3803 decky 437
 
3804 decky 438
            (selname, seltype, name, choices) = opt2row[value]
3803 decky 439
 
3804 decky 440
            if (not defaults.has_key(selname)):
441
                    default = None
442
            else:
443
                default = defaults[selname]
444
 
3803 decky 445
            if (seltype == 'choice'):
3804 decky 446
                defaults[selname] = subchoice(screen, name, choices, default)
3803 decky 447
            elif ((seltype == 'y/n') or (seltype == 'n/y')):
448
                if (defaults[selname] == 'y'):
449
                    defaults[selname] = 'n'
450
                else:
451
                    defaults[selname] = 'y'
452
    finally:
3804 decky 453
        xtui.screen_done(screen)
3803 decky 454
 
3881 decky 455
    create_output(MAKEFILE, MACROS, DEFS, defaults, ask_names)
3803 decky 456
    return 0
1802 decky 457
 
458
if __name__ == '__main__':
3821 decky 459
    sys.exit(main())