1#!/usr/bin/env python
2
3import sys
4import os
5import re
6import argparse
7import yaml
8import subprocess
9from mako.template import Template
10
11valid_c_name_pattern = re.compile('[\W_]+')
12
13
14def parse_event(e):
15    e['name'] = valid_c_name_pattern.sub('_', e['name']).lower()
16    if e.get('filter') is None:
17        e.setdefault('filter', {}).setdefault('type', 'none')
18    if e.get('action') is None:
19        e.setdefault('action', {}).setdefault('type', 'noop')
20    return e
21
22
23def generate_cpp(args):
24    # Aggregate all the event YAML in the events.d directory
25    # into a single list of events.
26    events_dir = os.path.join(args.inputdir, 'events.d')
27    yaml_files = filter(
28        lambda x: x.endswith('.yaml'),
29        os.listdir(events_dir))
30
31    events = []
32    for x in yaml_files:
33        with open(os.path.join(events_dir, x), 'r') as fd:
34            for e in yaml.load(fd.read()).get('events', {}):
35                events.append(parse_event(e))
36
37    # Aggregate all the interface YAML in the interfaces.d
38    # directory into a single list of interfaces.
39    template = os.path.join(script_dir, 'generated.mako.cpp')
40    t = Template(filename=template)
41
42    interfaces_dir = os.path.join(args.inputdir, 'interfaces.d')
43    yaml_files = filter(
44        lambda x: x.endswith('.yaml'),
45        os.listdir(interfaces_dir))
46
47    interfaces = []
48    for x in yaml_files:
49        with open(os.path.join(interfaces_dir, x), 'r') as fd:
50            for i in yaml.load(fd.read()):
51                interfaces.append(i)
52
53    # Render the template with the provided events and interfaces.
54    template = os.path.join(script_dir, 'generated.mako.cpp')
55    t = Template(filename=template)
56    with open(os.path.join(args.outputdir, 'generated.cpp'), 'w') as fd:
57        fd.write(
58            t.render(
59                interfaces=interfaces,
60                events=events))
61
62    # Invoke sdbus++ to generate any extra interface bindings for
63    # extra interfaces that aren't defined externally.
64    yaml_files = []
65    extra_ifaces_dir = os.path.join(args.inputdir, 'extra_interfaces.d')
66    if os.path.exists(extra_ifaces_dir):
67        for directory, _, files in os.walk(extra_ifaces_dir):
68            if not files:
69                continue
70
71            yaml_files += map(
72                lambda f: os.path.relpath(
73                    os.path.join(directory, f),
74                    extra_ifaces_dir),
75                filter(lambda f: f.endswith('.interface.yaml'), files))
76
77    genfiles = {
78        'server-cpp': lambda x: '%s.cpp' % (
79            x.replace(os.sep, '.')),
80        'server-header': lambda x: os.path.join(
81            os.path.join(
82                *x.split('.')), 'server.hpp')
83    }
84
85    for i in yaml_files:
86        iface = i.replace('.interface.yaml', '').replace(os.sep, '.')
87        for process, f in genfiles.iteritems():
88
89            dest = os.path.join(args.outputdir, f(iface))
90            parent = os.path.dirname(dest)
91            if parent and not os.path.exists(parent):
92                os.makedirs(parent)
93
94            with open(dest, 'w') as fd:
95                subprocess.call([
96                    'sdbus++',
97                    '-r',
98                    extra_ifaces_dir,
99                    'interface',
100                    process,
101                    iface],
102                    stdout=fd)
103
104
105if __name__ == '__main__':
106    script_dir = os.path.dirname(os.path.realpath(__file__))
107
108    parser = argparse.ArgumentParser(
109        description='Phosphor Inventory Manager (PIM) YAML '
110        'scanner and code generator.')
111    parser.add_argument(
112        '-o', '--output-dir', dest='outputdir',
113        default='.', help='Output directory.')
114    parser.add_argument(
115        '-d', '--dir', dest='inputdir',
116        default=os.path.join(script_dir, 'example'),
117        help='Location of files to process.')
118
119    args = parser.parse_args()
120    generate_cpp(args)
121
122
123# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
124