1#!/usr/bin/env python 2 3'''Phosphor DBus Monitor YAML parser and code generator. 4 5The parser workflow is broken down as follows: 6 1 - Import YAML files as native python type(s) instance(s). 7 2 - Create an instance of the Everything class from the 8 native python type instance(s) with the Everything.load 9 method. 10 3 - The Everything class constructor orchestrates conversion of the 11 native python type(s) instances(s) to render helper types. 12 Each render helper type constructor imports its attributes 13 from the native python type(s) instances(s). 14 4 - Present the converted YAML to the command processing method 15 requested by the script user. 16''' 17 18import os 19import sys 20import yaml 21import mako.lookup 22from argparse import ArgumentParser 23from sdbusplus.renderer import Renderer 24from sdbusplus.namedelement import NamedElement 25import sdbusplus.property 26 27 28class InvalidConfigError(BaseException): 29 '''General purpose config file parsing error.''' 30 31 def __init__(self, path, msg): 32 '''Display configuration file with the syntax 33 error and the error message.''' 34 35 self.config = path 36 self.msg = msg 37 38 39class NotUniqueError(InvalidConfigError): 40 '''Within a config file names must be unique. 41 Display the config file with the duplicate and 42 the duplicate itself.''' 43 44 def __init__(self, path, cls, *names): 45 fmt = 'Duplicate {0}: "{1}"' 46 super(NotUniqueError, self).__init__( 47 path, fmt.format(cls, ' '.join(names))) 48 49 50def get_index(objs, cls, name, config=None): 51 '''Items are usually rendered as C++ arrays and as 52 such are stored in python lists. Given an item name 53 its class, and an optional config file filter, find 54 the item index.''' 55 56 for i, x in enumerate(objs.get(cls, [])): 57 if config and x.configfile != config: 58 continue 59 if x.name != name: 60 continue 61 62 return i 63 raise InvalidConfigError(config, 'Could not find name: "{0}"'.format(name)) 64 65 66def exists(objs, cls, name, config=None): 67 '''Check to see if an item already exists in a list given 68 the item name.''' 69 70 try: 71 get_index(objs, cls, name, config) 72 except: 73 return False 74 75 return True 76 77 78def add_unique(obj, *a, **kw): 79 '''Add an item to one or more lists unless already present, 80 with an option to constrain the search to a specific config file.''' 81 82 for container in a: 83 if not exists(container, obj.cls, obj.name, config=kw.get('config')): 84 container.setdefault(obj.cls, []).append(obj) 85 86 87class Cast(object): 88 '''Decorate an argument by casting it.''' 89 90 def __init__(self, cast, target): 91 '''cast is the cast type (static, const, etc...). 92 target is the cast target type.''' 93 self.cast = cast 94 self.target = target 95 96 def __call__(self, arg): 97 return '{0}_cast<{1}>({2})'.format(self.cast, self.target, arg) 98 99 100class Literal(object): 101 '''Decorate an argument with a literal operator.''' 102 103 integer_types = [ 104 'int16', 105 'int32', 106 'int64', 107 'uint16', 108 'uint32', 109 'uint64' 110 ] 111 112 def __init__(self, type): 113 self.type = type 114 115 def __call__(self, arg): 116 if 'uint' in self.type: 117 arg = '{0}ull'.format(arg) 118 elif 'int' in self.type: 119 arg = '{0}ll'.format(arg) 120 121 if self.type in self.integer_types: 122 return Cast('static', '{0}_t'.format(self.type))(arg) 123 elif self.type == 'byte': 124 return Cast('static', 'uint8_t'.format(self.type))(arg) 125 126 if self.type == 'string': 127 return '{0}s'.format(arg) 128 129 return arg 130 131 132class FixBool(object): 133 '''Un-capitalize booleans.''' 134 135 def __call__(self, arg): 136 return '{0}'.format(arg.lower()) 137 138 139class Quote(object): 140 '''Decorate an argument by quoting it.''' 141 142 def __call__(self, arg): 143 return '"{0}"'.format(arg) 144 145 146class Argument(NamedElement, Renderer): 147 '''Define argument type inteface.''' 148 149 def __init__(self, **kw): 150 self.type = kw.pop('type', None) 151 super(Argument, self).__init__(**kw) 152 153 def argument(self, loader, indent): 154 raise NotImplementedError 155 156 157class TrivialArgument(Argument): 158 '''Non-array type arguments.''' 159 160 def __init__(self, **kw): 161 self.value = kw.pop('value') 162 self.decorators = kw.pop('decorators', []) 163 if kw.get('type', None): 164 self.decorators.insert(0, Literal(kw['type'])) 165 if kw.get('type', None) == 'string': 166 self.decorators.insert(0, Quote()) 167 if kw.get('type', None) == 'boolean': 168 self.decorators.insert(0, FixBool()) 169 170 super(TrivialArgument, self).__init__(**kw) 171 172 def argument(self, loader, indent): 173 a = str(self.value) 174 for d in self.decorators: 175 a = d(a) 176 177 return a 178 179 180class Indent(object): 181 '''Help templates be depth agnostic.''' 182 183 def __init__(self, depth=0): 184 self.depth = depth 185 186 def __add__(self, depth): 187 return Indent(self.depth + depth) 188 189 def __call__(self, depth): 190 '''Render an indent at the current depth plus depth.''' 191 return 4*' '*(depth + self.depth) 192 193 194class ConfigEntry(NamedElement): 195 '''Base interface for rendered items.''' 196 197 def __init__(self, *a, **kw): 198 '''Pop the configfile/class/subclass keywords.''' 199 200 self.configfile = kw.pop('configfile') 201 self.cls = kw.pop('class') 202 self.subclass = kw.pop(self.cls) 203 super(ConfigEntry, self).__init__(**kw) 204 205 def factory(self, objs): 206 ''' Optional factory interface for subclasses to add 207 additional items to be rendered.''' 208 209 pass 210 211 def setup(self, objs): 212 ''' Optional setup interface for subclasses, invoked 213 after all factory methods have been run.''' 214 215 pass 216 217 218class Path(ConfigEntry): 219 '''Path/metadata association.''' 220 221 def __init__(self, *a, **kw): 222 super(Path, self).__init__(**kw) 223 224 if self.name['meta'].upper() != self.name['meta']: 225 raise InvalidConfigError( 226 self.configfile, 227 'Metadata tag "{0}" must be upper case.'.format( 228 self.name['meta'])) 229 230 def factory(self, objs): 231 '''Create path and metadata elements.''' 232 233 args = { 234 'class': 'pathname', 235 'pathname': 'element', 236 'name': self.name['path'] 237 } 238 add_unique(ConfigEntry( 239 configfile=self.configfile, **args), objs) 240 241 args = { 242 'class': 'meta', 243 'meta': 'element', 244 'name': self.name['meta'] 245 } 246 add_unique(ConfigEntry( 247 configfile=self.configfile, **args), objs) 248 249 super(Path, self).factory(objs) 250 251 def setup(self, objs): 252 '''Resolve path and metadata names to indicies.''' 253 254 self.path = get_index( 255 objs, 'pathname', self.name['path']) 256 self.meta = get_index( 257 objs, 'meta', self.name['meta']) 258 259 super(Path, self).setup(objs) 260 261 262class Property(ConfigEntry): 263 '''Property/interface/metadata association.''' 264 265 def __init__(self, *a, **kw): 266 super(Property, self).__init__(**kw) 267 268 if self.name['meta'].upper() != self.name['meta']: 269 raise InvalidConfigError( 270 self.configfile, 271 'Metadata tag "{0}" must be upper case.'.format( 272 self.name['meta'])) 273 274 def factory(self, objs): 275 '''Create interface, property name and metadata elements.''' 276 277 args = { 278 'class': 'interface', 279 'interface': 'element', 280 'name': self.name['interface'] 281 } 282 add_unique(ConfigEntry( 283 configfile=self.configfile, **args), objs) 284 285 args = { 286 'class': 'propertyname', 287 'propertyname': 'element', 288 'name': self.name['property'] 289 } 290 add_unique(ConfigEntry( 291 configfile=self.configfile, **args), objs) 292 293 args = { 294 'class': 'meta', 295 'meta': 'element', 296 'name': self.name['meta'] 297 } 298 add_unique(ConfigEntry( 299 configfile=self.configfile, **args), objs) 300 301 super(Property, self).factory(objs) 302 303 def setup(self, objs): 304 '''Resolve interface, property and metadata to indicies.''' 305 306 self.interface = get_index( 307 objs, 'interface', self.name['interface']) 308 self.prop = get_index( 309 objs, 'propertyname', self.name['property']) 310 self.meta = get_index( 311 objs, 'meta', self.name['meta']) 312 313 super(Property, self).setup(objs) 314 315 316class Instance(ConfigEntry): 317 '''Property/Path association.''' 318 319 def __init__(self, *a, **kw): 320 super(Instance, self).__init__(**kw) 321 322 def setup(self, objs): 323 '''Resolve elements to indicies.''' 324 325 self.interface = get_index( 326 objs, 'interface', self.name['property']['interface']) 327 self.prop = get_index( 328 objs, 'propertyname', self.name['property']['property']) 329 self.propmeta = get_index( 330 objs, 'meta', self.name['property']['meta']) 331 self.path = get_index( 332 objs, 'pathname', self.name['path']['path']) 333 self.pathmeta = get_index( 334 objs, 'meta', self.name['path']['meta']) 335 336 super(Instance, self).setup(objs) 337 338 339class Group(ConfigEntry): 340 '''Pop the members keyword for groups.''' 341 342 def __init__(self, *a, **kw): 343 self.members = kw.pop('members') 344 super(Group, self).__init__(**kw) 345 346 347class ImplicitGroup(Group): 348 '''Provide a factory method for groups whose members are 349 not explicitly declared in the config files.''' 350 351 def __init__(self, *a, **kw): 352 super(ImplicitGroup, self).__init__(**kw) 353 354 def factory(self, objs): 355 '''Create group members.''' 356 357 factory = Everything.classmap(self.subclass, 'element') 358 for m in self.members: 359 args = { 360 'class': self.subclass, 361 self.subclass: 'element', 362 'name': m 363 } 364 365 obj = factory(configfile=self.configfile, **args) 366 add_unique(obj, objs) 367 obj.factory(objs) 368 369 super(ImplicitGroup, self).factory(objs) 370 371 372class GroupOfPaths(ImplicitGroup): 373 '''Path group config file directive.''' 374 375 def __init__(self, *a, **kw): 376 super(GroupOfPaths, self).__init__(**kw) 377 378 def setup(self, objs): 379 '''Resolve group members.''' 380 381 def map_member(x): 382 path = get_index( 383 objs, 'pathname', x['path']) 384 meta = get_index( 385 objs, 'meta', x['meta']) 386 return (path, meta) 387 388 self.members = map( 389 map_member, 390 self.members) 391 392 super(GroupOfPaths, self).setup(objs) 393 394 395class GroupOfProperties(ImplicitGroup): 396 '''Property group config file directive.''' 397 398 def __init__(self, *a, **kw): 399 self.type = kw.pop('type') 400 self.datatype = sdbusplus.property.Property( 401 name=kw.get('name'), 402 type=self.type).cppTypeName 403 404 super(GroupOfProperties, self).__init__(**kw) 405 406 def setup(self, objs): 407 '''Resolve group members.''' 408 409 def map_member(x): 410 iface = get_index( 411 objs, 'interface', x['interface']) 412 prop = get_index( 413 objs, 'propertyname', x['property']) 414 meta = get_index( 415 objs, 'meta', x['meta']) 416 417 return (iface, prop, meta) 418 419 self.members = map( 420 map_member, 421 self.members) 422 423 super(GroupOfProperties, self).setup(objs) 424 425 426class GroupOfInstances(ImplicitGroup): 427 '''A group of property instances.''' 428 429 def __init__(self, *a, **kw): 430 super(GroupOfInstances, self).__init__(**kw) 431 432 def setup(self, objs): 433 '''Resolve group members.''' 434 435 def map_member(x): 436 path = get_index(objs, 'pathname', x['path']['path']) 437 pathmeta = get_index(objs, 'meta', x['path']['meta']) 438 interface = get_index( 439 objs, 'interface', x['property']['interface']) 440 prop = get_index(objs, 'propertyname', x['property']['property']) 441 propmeta = get_index(objs, 'meta', x['property']['meta']) 442 instance = get_index(objs, 'instance', x) 443 444 return (path, pathmeta, interface, prop, propmeta, instance) 445 446 self.members = map( 447 map_member, 448 self.members) 449 450 super(GroupOfInstances, self).setup(objs) 451 452 453class HasPropertyIndex(ConfigEntry): 454 '''Handle config file directives that require an index to be 455 constructed.''' 456 457 def __init__(self, *a, **kw): 458 self.paths = kw.pop('paths') 459 self.properties = kw.pop('properties') 460 super(HasPropertyIndex, self).__init__(**kw) 461 462 def factory(self, objs): 463 '''Create a group of instances for this index.''' 464 465 members = [] 466 path_group = get_index( 467 objs, 'pathgroup', self.paths, config=self.configfile) 468 property_group = get_index( 469 objs, 'propertygroup', self.properties, config=self.configfile) 470 471 for path in objs['pathgroup'][path_group].members: 472 for prop in objs['propertygroup'][property_group].members: 473 member = { 474 'path': path, 475 'property': prop, 476 } 477 members.append(member) 478 479 args = { 480 'members': members, 481 'class': 'instancegroup', 482 'instancegroup': 'instance', 483 'name': '{0} {1}'.format(self.paths, self.properties) 484 } 485 486 group = GroupOfInstances(configfile=self.configfile, **args) 487 add_unique(group, objs, config=self.configfile) 488 group.factory(objs) 489 490 super(HasPropertyIndex, self).factory(objs) 491 492 def setup(self, objs): 493 '''Resolve path, property, and instance groups.''' 494 495 self.instances = get_index( 496 objs, 497 'instancegroup', 498 '{0} {1}'.format(self.paths, self.properties), 499 config=self.configfile) 500 self.paths = get_index( 501 objs, 502 'pathgroup', 503 self.paths, 504 config=self.configfile) 505 self.properties = get_index( 506 objs, 507 'propertygroup', 508 self.properties, 509 config=self.configfile) 510 self.datatype = objs['propertygroup'][self.properties].datatype 511 self.type = objs['propertygroup'][self.properties].type 512 513 super(HasPropertyIndex, self).setup(objs) 514 515 516class PropertyWatch(HasPropertyIndex): 517 '''Handle the property watch config file directive.''' 518 519 def __init__(self, *a, **kw): 520 self.callback = kw.pop('callback', None) 521 super(PropertyWatch, self).__init__(**kw) 522 523 def setup(self, objs): 524 '''Resolve optional callback.''' 525 526 if self.callback: 527 self.callback = get_index( 528 objs, 529 'callback', 530 self.callback, 531 config=self.configfile) 532 533 super(PropertyWatch, self).setup(objs) 534 535 536class Callback(HasPropertyIndex): 537 '''Interface and common logic for callbacks.''' 538 539 def __init__(self, *a, **kw): 540 super(Callback, self).__init__(**kw) 541 542 543class ConditionCallback(ConfigEntry, Renderer): 544 '''Handle the journal callback config file directive.''' 545 546 def __init__(self, *a, **kw): 547 self.condition = kw.pop('condition') 548 self.instance = kw.pop('instance') 549 self.defer = kw.pop('defer', None) 550 super(ConditionCallback, self).__init__(**kw) 551 552 def factory(self, objs): 553 '''Create a graph instance for this callback.''' 554 555 args = { 556 'configfile': self.configfile, 557 'members': [self.instance], 558 'class': 'callbackgroup', 559 'callbackgroup': 'callback', 560 'name': [self.instance] 561 } 562 563 entry = CallbackGraphEntry(**args) 564 add_unique(entry, objs, config=self.configfile) 565 566 super(ConditionCallback, self).factory(objs) 567 568 def setup(self, objs): 569 '''Resolve condition and graph entry.''' 570 571 self.graph = get_index( 572 objs, 573 'callbackgroup', 574 [self.instance], 575 config=self.configfile) 576 577 self.condition = get_index( 578 objs, 579 'condition', 580 self.name, 581 config=self.configfile) 582 583 super(ConditionCallback, self).setup(objs) 584 585 def construct(self, loader, indent): 586 return self.render( 587 loader, 588 'conditional.mako.cpp', 589 c=self, 590 indent=indent) 591 592 593class Condition(HasPropertyIndex): 594 '''Interface and common logic for conditions.''' 595 596 def __init__(self, *a, **kw): 597 self.callback = kw.pop('callback') 598 self.defer = kw.pop('defer', None) 599 super(Condition, self).__init__(**kw) 600 601 def factory(self, objs): 602 '''Create a callback instance for this conditional.''' 603 604 args = { 605 'configfile': self.configfile, 606 'condition': self.name, 607 'class': 'callback', 608 'callback': 'conditional', 609 'instance': self.callback, 610 'name': self.name, 611 'defer': self.defer 612 } 613 614 callback = ConditionCallback(**args) 615 add_unique(callback, objs, config=self.configfile) 616 callback.factory(objs) 617 618 super(Condition, self).factory(objs) 619 620 621class CountCondition(Condition, Renderer): 622 '''Handle the count condition config file directive.''' 623 624 def __init__(self, *a, **kw): 625 self.countop = kw.pop('countop') 626 self.countbound = kw.pop('countbound') 627 self.op = kw.pop('op') 628 self.bound = kw.pop('bound') 629 super(CountCondition, self).__init__(**kw) 630 631 def setup(self, objs): 632 '''Resolve type.''' 633 634 super(CountCondition, self).setup(objs) 635 self.bound = TrivialArgument( 636 type=self.type, 637 value=self.bound) 638 639 def construct(self, loader, indent): 640 return self.render( 641 loader, 642 'count.mako.cpp', 643 c=self, 644 indent=indent) 645 646 647class Journal(Callback, Renderer): 648 '''Handle the journal callback config file directive.''' 649 650 def __init__(self, *a, **kw): 651 self.severity = kw.pop('severity') 652 self.message = kw.pop('message') 653 super(Journal, self).__init__(**kw) 654 655 def construct(self, loader, indent): 656 return self.render( 657 loader, 658 'journal.mako.cpp', 659 c=self, 660 indent=indent) 661 662 663class Elog(Callback, Renderer): 664 '''Handle the elog callback config file directive.''' 665 666 def __init__(self, *a, **kw): 667 self.error = kw.pop('error') 668 super(Elog, self).__init__(**kw) 669 670 def construct(self, loader, indent): 671 672 with open('errors.hpp', 'a') as fd: 673 fd.write( 674 self.render( 675 loader, 676 'errors.mako.hpp', 677 c=self)) 678 return self.render( 679 loader, 680 'elog.mako.cpp', 681 c=self, 682 indent=indent) 683 684 685class Method(ConfigEntry, Renderer): 686 '''Handle the method callback config file directive.''' 687 688 def __init__(self, *a, **kw): 689 self.service = kw.pop('service') 690 self.path = kw.pop('path') 691 self.interface = kw.pop('interface') 692 self.method = kw.pop('method') 693 self.args = [TrivialArgument(**x) for x in kw.pop('args', {})] 694 super(Method, self).__init__(**kw) 695 696 def factory(self, objs): 697 args = { 698 'class': 'interface', 699 'interface': 'element', 700 'name': self.service 701 } 702 add_unique(ConfigEntry( 703 configfile=self.configfile, **args), objs) 704 705 args = { 706 'class': 'pathname', 707 'pathname': 'element', 708 'name': self.path 709 } 710 add_unique(ConfigEntry( 711 configfile=self.configfile, **args), objs) 712 713 args = { 714 'class': 'interface', 715 'interface': 'element', 716 'name': self.interface 717 } 718 add_unique(ConfigEntry( 719 configfile=self.configfile, **args), objs) 720 721 args = { 722 'class': 'propertyname', 723 'propertyname': 'element', 724 'name': self.method 725 } 726 add_unique(ConfigEntry( 727 configfile=self.configfile, **args), objs) 728 729 super(Method, self).factory(objs) 730 731 def setup(self, objs): 732 '''Resolve elements.''' 733 734 self.service = get_index( 735 objs, 736 'interface', 737 self.service) 738 739 self.path = get_index( 740 objs, 741 'pathname', 742 self.path) 743 744 self.interface = get_index( 745 objs, 746 'interface', 747 self.interface) 748 749 self.method = get_index( 750 objs, 751 'propertyname', 752 self.method) 753 754 super(Method, self).setup(objs) 755 756 def construct(self, loader, indent): 757 return self.render( 758 loader, 759 'method.mako.cpp', 760 c=self, 761 indent=indent) 762 763 764class CallbackGraphEntry(Group): 765 '''An entry in a traversal list for groups of callbacks.''' 766 767 def __init__(self, *a, **kw): 768 super(CallbackGraphEntry, self).__init__(**kw) 769 770 def setup(self, objs): 771 '''Resolve group members.''' 772 773 def map_member(x): 774 return get_index( 775 objs, 'callback', x, config=self.configfile) 776 777 self.members = map( 778 map_member, 779 self.members) 780 781 super(CallbackGraphEntry, self).setup(objs) 782 783 784class GroupOfCallbacks(ConfigEntry, Renderer): 785 '''Handle the callback group config file directive.''' 786 787 def __init__(self, *a, **kw): 788 self.members = kw.pop('members') 789 super(GroupOfCallbacks, self).__init__(**kw) 790 791 def factory(self, objs): 792 '''Create a graph instance for this group of callbacks.''' 793 794 args = { 795 'configfile': self.configfile, 796 'members': self.members, 797 'class': 'callbackgroup', 798 'callbackgroup': 'callback', 799 'name': self.members 800 } 801 802 entry = CallbackGraphEntry(**args) 803 add_unique(entry, objs, config=self.configfile) 804 805 super(GroupOfCallbacks, self).factory(objs) 806 807 def setup(self, objs): 808 '''Resolve graph entry.''' 809 810 self.graph = get_index( 811 objs, 'callbackgroup', self.members, config=self.configfile) 812 813 super(GroupOfCallbacks, self).setup(objs) 814 815 def construct(self, loader, indent): 816 return self.render( 817 loader, 818 'callbackgroup.mako.cpp', 819 c=self, 820 indent=indent) 821 822 823class Everything(Renderer): 824 '''Parse/render entry point.''' 825 826 @staticmethod 827 def classmap(cls, sub=None): 828 '''Map render item class and subclass entries to the appropriate 829 handler methods.''' 830 831 class_map = { 832 'path': { 833 'element': Path, 834 }, 835 'pathgroup': { 836 'path': GroupOfPaths, 837 }, 838 'propertygroup': { 839 'property': GroupOfProperties, 840 }, 841 'property': { 842 'element': Property, 843 }, 844 'watch': { 845 'property': PropertyWatch, 846 }, 847 'instance': { 848 'element': Instance, 849 }, 850 'callback': { 851 'journal': Journal, 852 'elog': Elog, 853 'group': GroupOfCallbacks, 854 'method': Method, 855 }, 856 'condition': { 857 'count': CountCondition, 858 }, 859 } 860 861 if cls not in class_map: 862 raise NotImplementedError('Unknown class: "{0}"'.format(cls)) 863 if sub not in class_map[cls]: 864 raise NotImplementedError('Unknown {0} type: "{1}"'.format( 865 cls, sub)) 866 867 return class_map[cls][sub] 868 869 @staticmethod 870 def load_one_yaml(path, fd, objs): 871 '''Parse a single YAML file. Parsing occurs in three phases. 872 In the first phase a factory method associated with each 873 configuration file directive is invoked. These factory 874 methods generate more factory methods. In the second 875 phase the factory methods created in the first phase 876 are invoked. In the last phase a callback is invoked on 877 each object created in phase two. Typically the callback 878 resolves references to other configuration file directives.''' 879 880 factory_objs = {} 881 for x in yaml.safe_load(fd.read()) or {}: 882 883 # Create factory object for this config file directive. 884 cls = x['class'] 885 sub = x.get(cls) 886 if cls == 'group': 887 cls = '{0}group'.format(sub) 888 889 factory = Everything.classmap(cls, sub) 890 obj = factory(configfile=path, **x) 891 892 # For a given class of directive, validate the file 893 # doesn't have any duplicate names (duplicates are 894 # ok across config files). 895 if exists(factory_objs, obj.cls, obj.name, config=path): 896 raise NotUniqueError(path, cls, obj.name) 897 898 factory_objs.setdefault(cls, []).append(obj) 899 objs.setdefault(cls, []).append(obj) 900 901 for cls, items in factory_objs.items(): 902 for obj in items: 903 # Add objects for template consumption. 904 obj.factory(objs) 905 906 @staticmethod 907 def load(args): 908 '''Aggregate all the YAML in the input directory 909 into a single aggregate.''' 910 911 objs = {} 912 yaml_files = filter( 913 lambda x: x.endswith('.yaml'), 914 os.listdir(args.inputdir)) 915 916 yaml_files.sort() 917 918 for x in yaml_files: 919 path = os.path.join(args.inputdir, x) 920 with open(path, 'r') as fd: 921 Everything.load_one_yaml(path, fd, objs) 922 923 # Configuration file directives reference each other via 924 # the name attribute; however, when rendered the reference 925 # is just an array index. 926 # 927 # At this point all objects have been created but references 928 # have not been resolved to array indicies. Instruct objects 929 # to do that now. 930 for cls, items in objs.items(): 931 for obj in items: 932 obj.setup(objs) 933 934 return Everything(**objs) 935 936 def __init__(self, *a, **kw): 937 self.pathmeta = kw.pop('path', []) 938 self.paths = kw.pop('pathname', []) 939 self.meta = kw.pop('meta', []) 940 self.pathgroups = kw.pop('pathgroup', []) 941 self.interfaces = kw.pop('interface', []) 942 self.properties = kw.pop('property', []) 943 self.propertynames = kw.pop('propertyname', []) 944 self.propertygroups = kw.pop('propertygroup', []) 945 self.instances = kw.pop('instance', []) 946 self.instancegroups = kw.pop('instancegroup', []) 947 self.watches = kw.pop('watch', []) 948 self.callbacks = kw.pop('callback', []) 949 self.callbackgroups = kw.pop('callbackgroup', []) 950 self.conditions = kw.pop('condition', []) 951 952 super(Everything, self).__init__(**kw) 953 954 def generate_cpp(self, loader): 955 '''Render the template with the provided data.''' 956 # errors.hpp is used by generated.hpp to included any error.hpp files 957 open('errors.hpp', 'w+') 958 959 with open(args.output, 'w') as fd: 960 fd.write( 961 self.render( 962 loader, 963 args.template, 964 meta=self.meta, 965 properties=self.properties, 966 propertynames=self.propertynames, 967 interfaces=self.interfaces, 968 paths=self.paths, 969 pathmeta=self.pathmeta, 970 pathgroups=self.pathgroups, 971 propertygroups=self.propertygroups, 972 instances=self.instances, 973 watches=self.watches, 974 instancegroups=self.instancegroups, 975 callbacks=self.callbacks, 976 callbackgroups=self.callbackgroups, 977 conditions=self.conditions, 978 indent=Indent())) 979 980if __name__ == '__main__': 981 script_dir = os.path.dirname(os.path.realpath(__file__)) 982 valid_commands = { 983 'generate-cpp': 'generate_cpp', 984 } 985 986 parser = ArgumentParser( 987 description='Phosphor DBus Monitor (PDM) YAML ' 988 'scanner and code generator.') 989 990 parser.add_argument( 991 "-o", "--out", dest="output", 992 default='generated.cpp', 993 help="Generated output file name and path.") 994 parser.add_argument( 995 '-t', '--template', dest='template', 996 default='generated.mako.hpp', 997 help='The top level template to render.') 998 parser.add_argument( 999 '-p', '--template-path', dest='template_search', 1000 default=script_dir, 1001 help='The space delimited mako template search path.') 1002 parser.add_argument( 1003 '-d', '--dir', dest='inputdir', 1004 default=os.path.join(script_dir, 'example'), 1005 help='Location of files to process.') 1006 parser.add_argument( 1007 'command', metavar='COMMAND', type=str, 1008 choices=valid_commands.keys(), 1009 help='%s.' % " | ".join(valid_commands.keys())) 1010 1011 args = parser.parse_args() 1012 1013 if sys.version_info < (3, 0): 1014 lookup = mako.lookup.TemplateLookup( 1015 directories=args.template_search.split(), 1016 disable_unicode=True) 1017 else: 1018 lookup = mako.lookup.TemplateLookup( 1019 directories=args.template_search.split()) 1020 try: 1021 function = getattr( 1022 Everything.load(args), 1023 valid_commands[args.command]) 1024 function(lookup) 1025 except InvalidConfigError as e: 1026 sys.stdout.write('{0}: {1}\n\n'.format(e.config, e.msg)) 1027 raise 1028