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 get_interfaces(args):
24    interfaces_dir = os.path.join(args.inputdir, 'interfaces.d')
25    yaml_files = filter(
26        lambda x: x.endswith('.yaml'),
27        os.listdir(interfaces_dir))
28
29    interfaces = []
30    for x in yaml_files:
31        with open(os.path.join(interfaces_dir, x), 'r') as fd:
32            for i in yaml.load(fd.read()):
33                interfaces.append(i)
34
35    return interfaces
36
37
38def list_interfaces(args):
39    print ' '.join(get_interfaces(args))
40
41
42def generate_cpp(args):
43    # Aggregate all the event YAML in the events.d directory
44    # into a single list of events.
45    events_dir = os.path.join(args.inputdir, 'events.d')
46    yaml_files = filter(
47        lambda x: x.endswith('.yaml'),
48        os.listdir(events_dir))
49
50    events = []
51    for x in yaml_files:
52        with open(os.path.join(events_dir, x), 'r') as fd:
53            for e in yaml.load(fd.read()).get('events', {}):
54                events.append(parse_event(e))
55
56    # Aggregate all the interface YAML in the interfaces.d
57    # directory into a single list of interfaces.
58    template = os.path.join(script_dir, 'generated.mako.cpp')
59    t = Template(filename=template)
60
61    interfaces = get_interfaces(args)
62
63    # Render the template with the provided events and interfaces.
64    template = os.path.join(script_dir, 'generated.mako.cpp')
65    t = Template(filename=template)
66    with open(os.path.join(args.outputdir, 'generated.cpp'), 'w') as fd:
67        fd.write(
68            t.render(
69                interfaces=interfaces,
70                events=events))
71
72    # Invoke sdbus++ to generate any extra interface bindings for
73    # extra interfaces that aren't defined externally.
74    yaml_files = []
75    extra_ifaces_dir = os.path.join(args.inputdir, 'extra_interfaces.d')
76    if os.path.exists(extra_ifaces_dir):
77        for directory, _, files in os.walk(extra_ifaces_dir):
78            if not files:
79                continue
80
81            yaml_files += map(
82                lambda f: os.path.relpath(
83                    os.path.join(directory, f),
84                    extra_ifaces_dir),
85                filter(lambda f: f.endswith('.interface.yaml'), files))
86
87    genfiles = {
88        'server-cpp': lambda x: '%s.cpp' % (
89            x.replace(os.sep, '.')),
90        'server-header': lambda x: os.path.join(
91            os.path.join(
92                *x.split('.')), 'server.hpp')
93    }
94
95    for i in yaml_files:
96        iface = i.replace('.interface.yaml', '').replace(os.sep, '.')
97        for process, f in genfiles.iteritems():
98
99            dest = os.path.join(args.outputdir, f(iface))
100            parent = os.path.dirname(dest)
101            if parent and not os.path.exists(parent):
102                os.makedirs(parent)
103
104            with open(dest, 'w') as fd:
105                subprocess.call([
106                    'sdbus++',
107                    '-r',
108                    extra_ifaces_dir,
109                    'interface',
110                    process,
111                    iface],
112                    stdout=fd)
113
114
115if __name__ == '__main__':
116    script_dir = os.path.dirname(os.path.realpath(__file__))
117    valid_commands = {
118        'generate-cpp': 'generate_cpp',
119        'list-interfaces': 'list_interfaces'}
120
121    parser = argparse.ArgumentParser(
122        description='Phosphor Inventory Manager (PIM) YAML '
123        'scanner and code generator.')
124    parser.add_argument(
125        '-o', '--output-dir', dest='outputdir',
126        default='.', help='Output directory.')
127    parser.add_argument(
128        '-d', '--dir', dest='inputdir',
129        default=os.path.join(script_dir, 'example'),
130        help='Location of files to process.')
131    parser.add_argument(
132        'command', metavar='COMMAND', type=str,
133        choices=valid_commands.keys(),
134        help='Command to run.')
135
136    args = parser.parse_args()
137    function = getattr(sys.modules[__name__], valid_commands[args.command])
138    function(args)
139
140
141# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
142