summaryrefslogtreecommitdiffhomepage
path: root/digital/ucoolib/build/tools/config-gen
blob: 9bc578bc39b712373c033e57198254e852b68d02 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
#!/usr/bin/python
"""Read build configuration and generate header files."""
import optparse
import ConfigParser
import collections
import sys
import re
import os

def read_config(modules_configs, project_config):
    """Read configuration definitions and default values from module
    configuration files, then merge project configuration."""
    parser = ConfigParser.SafeConfigParser()
    # Read definitions and default values.
    for mc in modules_configs:
        try:
            mcf = open(mc)
        except IOError:
            pass
        else:
            with mcf:
                parser.readfp(mcf)
    # Save the list of existing items for later check.
    def config_items(parser):
        return set('%s:%s' % (section.split(':')[0], item)
                for section in parser.sections()
                for item in parser.options(section))
    modules_items = config_items(parser)
    # Now read project configuration.
    if project_config is not None:
        try:
            pcf = open(project_config)
        except IOError:
            pass
        else:
            with pcf:
                parser.readfp(pcf)
        # Check for unknown items.
        project_items = config_items(parser)
        unknown_items = project_items - modules_items
        if unknown_items:
            raise RuntimeError("unknown configuration item: "
                    + " ".join(unknown_items))
    # OK, convert to more natural structure.
    config = collections.defaultdict (lambda: collections.defaultdict(dict))
    for section in parser.sections():
        items = parser.items(section)
        if ':' in section:
            section, target = section.split(':', 1)
        else:
            target = None
        for k, v in items:
            config[section][k][target] = v
    return config

def check_config(config, targets, subtargets):
    """Run consistency checks on configuration."""
    for section, section_dict in config.iteritems():
        for key, values in section_dict.iteritems():
            # Check targets.
            for ts in values:
                if ts is not None and ts not in subtargets:
                    raise RuntimeError("unknown target %s" % ts)
            values_targets = reduce(lambda a, b: a + b, (subtargets[ts]
                    for ts in values if ts is not None), [ ])
            values_targets_set = set(values_targets)
            # Check for items with no default value.
            if values[None] == '':
                if not values_targets:
                    raise RuntimeError("no value given for %s:%s"
                            % (section, key))
                else:
                    if values_targets_set < targets:
                        raise RuntimeError("no value given for %s:%s for"
                                " targets: %s" % (section, key,
                                    ', '.join (targets - values_targets_set)))
            # Check for items overridden several times for the same target.
            if len(values_targets) != len(values_targets_set):
                raise RuntimeError("several values given for %s:%s for the"
                        " same target" % (section, key))

def parse_targets(targets_option):
    """Parse a space separated target:subtarget list.  Return a set of
    targets, and a mapping of each subtarget to a list of target."""
    if targets_option is None:
        targets_option = ''
    targets = set()
    subtargets = collections.defaultdict(list)
    for tpair in targets_option.split():
        tpairl = tpair.split(':')
        if len(tpairl) != 2:
            raise RuntimeError("bad target:subtarget pair %s" % tpair)
        target, subtarget = tpairl
        targets.add(target)
        subtargets[subtarget].append(target)
    return targets, dict(subtargets)

def write_header(filename, section, section_dict):
    """Write (update) a section to a C header file."""
    # Prepare new content.
    items = [ ]
    section = section.replace('/', '_').upper()
    for key, values in section_dict.iteritems():
        cond = False
        for target, value in values.iteritems():
            if target is None: continue
            item_fmt = ('#ifdef TARGET_{target}\n'
                    '# define UCOO_CONFIG_{section}_{key} ({value})\n'
                    '#endif')
            items.append(item_fmt.format(section=section, key=key.upper(),
                target=target, value=value))
            cond = True
        item_fmt = '#define UCOO_CONFIG_{section}_{key} ({value})'
        if cond:
            item_fmt = '#ifndef UCOO_CONFIG_{section}_{key}\n# ' \
                    + item_fmt[1:] + '\n#endif'
        items.append(item_fmt.format(section=section, key=key.upper(),
            value=values[None]))
    guard = re.sub(r'\W', '_', filename)
    content = '\n'.join([
        '#ifndef %s' % guard,
        '#define %s' % guard,
        '// Generated from configuration.',
        ] + items + [
        '#endif',
        '' ])
    # Check old content.
    old_content = ''
    try:
        hf = open(filename)
    except IOError:
        pass
    else:
        with hf:
            old_content = hf.read()
    if old_content == content:
        return
    # Create output directory if needed.
    dirname = os.path.dirname(filename)
    if not os.path.exists(dirname):
        os.makedirs(dirname)
    # Write new content.
    with open(filename, 'w') as hf:
        hf.write(content)

def write_headers(filename_pattern, config):
    """Write (update) all sections to C header files."""
    for section, section_dict in config.iteritems():
        filename = filename_pattern.replace('%', section)
        write_header(filename, section, section_dict)

if __name__ == '__main__':
    parser = optparse.OptionParser(
            usage='%prog [options] modules configuration files...')
    parser.add_option('-p', '--project-config', metavar='FILE',
            help="project configuration file")
    parser.add_option('-H', '--c-header-template', metavar='TEMPLATE',
            help="name template for C header files"
            + " (use % as section placeholder)")
    parser.add_option('-T', '--targets', metavar='"LIST"',
            help="space separated list of target:subtarget pairs (used for"
            + " error checking)")
    options, modules_configs = parser.parse_args()

    try:
        targets, subtargets = parse_targets(options.targets)
        config = read_config(modules_configs, options.project_config)
        check_config(config, targets, subtargets)
        if options.c_header_template:
            write_headers(options.c_header_template, config)
    except RuntimeError, e:
        print >> sys.stderr, e
        sys.exit(1)
    except EnvironmentError, e:
        print >> sys.stderr, e
        sys.exit(1)