| """ |
| 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 |