""" Descriptor objects for entities that are part of the LLVM project. """ from __future__ import absolute_import try: import configparser except: import ConfigParser as configparser import sys from llvmbuild.util import fatal, warning class ParseError(Exception): pass class ComponentInfo(object): """ Base class for component descriptions. """ type_name = None @staticmethod def parse_items(items, has_dependencies = True): kwargs = {} kwargs['name'] = items.get_string('name') kwargs['parent'] = items.get_optional_string('parent') if has_dependencies: kwargs['dependencies'] = items.get_list('dependencies') return kwargs def __init__(self, subpath, name, dependencies, parent): if not subpath.startswith('/'): raise ValueError("invalid subpath: %r" % subpath) self.subpath = subpath self.name = name self.dependencies = list(dependencies) # The name of the parent component to logically group this component # under. self.parent = parent # The parent instance, once loaded. self.parent_instance = None self.children = [] # The original source path. self._source_path = None # A flag to mark "special" components which have some amount of magic # handling (generally based on command line options). self._is_special_group = False def set_parent_instance(self, parent): assert parent.name == self.parent, "Unexpected parent!" self.parent_instance = parent self.parent_instance.children.append(self) def get_component_references(self): """get_component_references() -> iter Return an iterator over the named references to other components from this object. Items are of the form (reference-type, component-name). """ # Parent references are handled specially. for r in self.dependencies: yield ('dependency', r) def get_llvmbuild_fragment(self): abstract def get_parent_target_group(self): """get_parent_target_group() -> ComponentInfo or None Return the nearest parent target group (if any), or None if the component is not part of any target group. """ # If this is a target group, return it. if self.type_name == 'TargetGroup': return self # Otherwise recurse on the parent, if any. if self.parent_instance: return self.parent_instance.get_parent_target_group() class GroupComponentInfo(ComponentInfo): """ Group components have no semantics as far as the build system are concerned, but exist to help organize other components into a logical tree structure. """ type_name = 'Group' @staticmethod def parse(subpath, items): kwargs = ComponentInfo.parse_items(items, has_dependencies = False) return GroupComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, parent): ComponentInfo.__init__(self, subpath, name, [], parent) def get_llvmbuild_fragment(self): return """\ type = %s name = %s parent = %s """ % (self.type_name, self.name, self.parent) class LibraryComponentInfo(ComponentInfo): type_name = 'Library' @staticmethod def parse_items(items): kwargs = ComponentInfo.parse_items(items) kwargs['library_name'] = items.get_optional_string('library_name') kwargs['required_libraries'] = items.get_list('required_libraries') kwargs['add_to_library_groups'] = items.get_list( 'add_to_library_groups') kwargs['installed'] = items.get_optional_bool('installed', True) return kwargs @staticmethod def parse(subpath, items): kwargs = LibraryComponentInfo.parse_items(items) return LibraryComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, dependencies, parent, library_name, required_libraries, add_to_library_groups, installed): ComponentInfo.__init__(self, subpath, name, dependencies, parent) # If given, the name to use for the library instead of deriving it from # the component name. self.library_name = library_name # The names of the library components which are required when linking # with this component. self.required_libraries = list(required_libraries) # The names of the library group components this component should be # considered part of. self.add_to_library_groups = list(add_to_library_groups) # Whether or not this library is installed. self.installed = installed def get_component_references(self): for r in ComponentInfo.get_component_references(self): yield r for r in self.required_libraries: yield ('required library', r) for r in self.add_to_library_groups: yield ('library group', r) def get_llvmbuild_fragment(self): result = """\ type = %s name = %s parent = %s """ % (self.type_name, self.name, self.parent) if self.library_name is not None: result += 'library_name = %s\n' % self.library_name if self.required_libraries: result += 'required_libraries = %s\n' % ' '.join( self.required_libraries) if self.add_to_library_groups: result += 'add_to_library_groups = %s\n' % ' '.join( self.add_to_library_groups) if not self.installed: result += 'installed = 0\n' return result def get_library_name(self): return self.library_name or self.name def get_prefixed_library_name(self): """ get_prefixed_library_name() -> str Return the library name prefixed by the project name. This is generally what the library name will be on disk. """ basename = self.get_library_name() # FIXME: We need to get the prefix information from an explicit project # object, or something. if basename in ('gtest', 'gtest_main'): return basename return 'LLVM%s' % basename def get_llvmconfig_component_name(self): return self.get_library_name().lower() class OptionalLibraryComponentInfo(LibraryComponentInfo): type_name = "OptionalLibrary" @staticmethod def parse(subpath, items): kwargs = LibraryComponentInfo.parse_items(items) return OptionalLibraryComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, dependencies, parent, library_name, required_libraries, add_to_library_groups, installed): LibraryComponentInfo.__init__(self, subpath, name, dependencies, parent, library_name, required_libraries, add_to_library_groups, installed) class LibraryGroupComponentInfo(ComponentInfo): type_name = 'LibraryGroup' @staticmethod def parse(subpath, items): kwargs = ComponentInfo.parse_items(items, has_dependencies = False) kwargs['required_libraries'] = items.get_list('required_libraries') kwargs['add_to_library_groups'] = items.get_list( 'add_to_library_groups') return LibraryGroupComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, parent, required_libraries = [], add_to_library_groups = []): ComponentInfo.__init__(self, subpath, name, [], parent) # The names of the library components which are required when linking # with this component. self.required_libraries = list(required_libraries) # The names of the library group components this component should be # considered part of. self.add_to_library_groups = list(add_to_library_groups) def get_component_references(self): for r in ComponentInfo.get_component_references(self): yield r for r in self.required_libraries: yield ('required library', r) for r in self.add_to_library_groups: yield ('library group', r) def get_llvmbuild_fragment(self): result = """\ type = %s name = %s parent = %s """ % (self.type_name, self.name, self.parent) if self.required_libraries and not self._is_special_group: result += 'required_libraries = %s\n' % ' '.join( self.required_libraries) if self.add_to_library_groups: result += 'add_to_library_groups = %s\n' % ' '.join( self.add_to_library_groups) return result def get_llvmconfig_component_name(self): return self.name.lower() class TargetGroupComponentInfo(ComponentInfo): type_name = 'TargetGroup' @staticmethod def parse(subpath, items): kwargs = ComponentInfo.parse_items(items, has_dependencies = False) kwargs['required_libraries'] = items.get_list('required_libraries') kwargs['add_to_library_groups'] = items.get_list( 'add_to_library_groups') kwargs['has_jit'] = items.get_optional_bool('has_jit', False) kwargs['has_asmprinter'] = items.get_optional_bool('has_asmprinter', False) kwargs['has_asmparser'] = items.get_optional_bool('has_asmparser', False) kwargs['has_disassembler'] = items.get_optional_bool('has_disassembler', False) return TargetGroupComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, parent, required_libraries = [], add_to_library_groups = [], has_jit = False, has_asmprinter = False, has_asmparser = False, has_disassembler = False): ComponentInfo.__init__(self, subpath, name, [], parent) # The names of the library components which are required when linking # with this component. self.required_libraries = list(required_libraries) # The names of the library group components this component should be # considered part of. self.add_to_library_groups = list(add_to_library_groups) # Whether or not this target supports the JIT. self.has_jit = bool(has_jit) # Whether or not this target defines an assembly printer. self.has_asmprinter = bool(has_asmprinter) # Whether or not this target defines an assembly parser. self.has_asmparser = bool(has_asmparser) # Whether or not this target defines an disassembler. self.has_disassembler = bool(has_disassembler) # Whether or not this target is enabled. This is set in response to # configuration parameters. self.enabled = False def get_component_references(self): for r in ComponentInfo.get_component_references(self): yield r for r in self.required_libraries: yield ('required library', r) for r in self.add_to_library_groups: yield ('library group', r) def get_llvmbuild_fragment(self): result = """\ type = %s name = %s parent = %s """ % (self.type_name, self.name, self.parent) if self.required_libraries: result += 'required_libraries = %s\n' % ' '.join( self.required_libraries) if self.add_to_library_groups: result += 'add_to_library_groups = %s\n' % ' '.join( self.add_to_library_groups) for bool_key in ('has_asmparser', 'has_asmprinter', 'has_disassembler', 'has_jit'): if getattr(self, bool_key): result += '%s = 1\n' % (bool_key,) return result def get_llvmconfig_component_name(self): return self.name.lower() class ToolComponentInfo(ComponentInfo): type_name = 'Tool' @staticmethod def parse(subpath, items): kwargs = ComponentInfo.parse_items(items) kwargs['required_libraries'] = items.get_list('required_libraries') return ToolComponentInfo(subpath, **kwargs) def __init__(self, subpath, name, dependencies, parent, required_libraries): ComponentInfo.__init__(self, subpath, name, dependencies, parent) # The names of the library components which are required to link this # tool. self.required_libraries = list(required_libraries) def get_component_references(self): for r in ComponentInfo.get_component_references(self): yield r for r in self.required_libraries: yield ('required library', r) def get_llvmbuild_fragment(self): return """\ type = %s name = %s parent = %s required_libraries = %s """ % (self.type_name, self.name, self.parent, ' '.join(self.required_libraries)) class BuildToolComponentInfo(ToolComponentInfo): type_name = 'BuildTool' @staticmethod def parse(subpath, items): kwargs = ComponentInfo.parse_items(items) kwargs['required_libraries'] = items.get_list('required_libraries') return BuildToolComponentInfo(subpath, **kwargs) ### class IniFormatParser(dict): def get_list(self, key): # Check if the value is defined. value = self.get(key) if value is None: return [] # Lists are just whitespace separated strings. return value.split() def get_optional_string(self, key): value = self.get_list(key) if not value: return None if len(value) > 1: raise ParseError("multiple values for scalar key: %r" % key) return value[0] def get_string(self, key): value = self.get_optional_string(key) if not value: raise ParseError("missing value for required string: %r" % key) return value def get_optional_bool(self, key, default = None): value = self.get_optional_string(key) if not value: return default if value not in ('0', '1'): raise ParseError("invalid value(%r) for boolean property: %r" % ( value, key)) return bool(int(value)) def get_bool(self, key): value = self.get_optional_bool(key) if value is None: raise ParseError("missing value for required boolean: %r" % key) return value _component_type_map = dict( (t.type_name, t) for t in (GroupComponentInfo, LibraryComponentInfo, LibraryGroupComponentInfo, ToolComponentInfo, BuildToolComponentInfo, TargetGroupComponentInfo, OptionalLibraryComponentInfo)) def load_from_path(path, subpath): # Load the LLVMBuild.txt file as an .ini format file. parser = configparser.RawConfigParser() parser.read(path) # Extract the common section. if parser.has_section("common"): common = IniFormatParser(parser.items("common")) parser.remove_section("common") else: common = IniFormatParser({}) return common, _read_components_from_parser(parser, path, subpath) def _read_components_from_parser(parser, path, subpath): # We load each section which starts with 'component' as a distinct component # description (so multiple components can be described in one file). for section in parser.sections(): if not section.startswith('component'): # We don't expect arbitrary sections currently, warn the user. warning("ignoring unknown section %r in %r" % (section, path)) continue # Determine the type of the component to instantiate. if not parser.has_option(section, 'type'): fatal("invalid component %r in %r: %s" % ( section, path, "no component type")) type_name = parser.get(section, 'type') type_class = _component_type_map.get(type_name) if type_class is None: fatal("invalid component %r in %r: %s" % ( section, path, "invalid component type: %r" % type_name)) # Instantiate the component based on the remaining values. try: info = type_class.parse(subpath, IniFormatParser(parser.items(section))) except TypeError: print >>sys.stderr, "error: invalid component %r in %r: %s" % ( section, path, "unable to instantiate: %r" % type_name) import traceback traceback.print_exc() raise SystemExit(1) except ParseError: e = sys.exc_info()[1] fatal("unable to load component %r in %r: %s" % ( section, path, e.message)) info._source_path = path yield info