1"""
2Descriptor objects for entities that are part of the LLVM project.
3"""
4
5from __future__ import absolute_import
6try:
7    import configparser
8except:
9    import ConfigParser as configparser
10import sys
11
12from llvmbuild.util import fatal, warning
13
14class ParseError(Exception):
15    pass
16
17class ComponentInfo(object):
18    """
19    Base class for component descriptions.
20    """
21
22    type_name = None
23
24    @staticmethod
25    def parse_items(items, has_dependencies = True):
26        kwargs = {}
27        kwargs['name'] = items.get_string('name')
28        kwargs['parent'] = items.get_optional_string('parent')
29        if has_dependencies:
30            kwargs['dependencies'] = items.get_list('dependencies')
31        return kwargs
32
33    def __init__(self, subpath, name, dependencies, parent):
34        if not subpath.startswith('/'):
35            raise ValueError("invalid subpath: %r" % subpath)
36        self.subpath = subpath
37        self.name = name
38        self.dependencies = list(dependencies)
39
40        # The name of the parent component to logically group this component
41        # under.
42        self.parent = parent
43
44        # The parent instance, once loaded.
45        self.parent_instance = None
46        self.children = []
47
48        # The original source path.
49        self._source_path = None
50
51        # A flag to mark "special" components which have some amount of magic
52        # handling (generally based on command line options).
53        self._is_special_group = False
54
55    def set_parent_instance(self, parent):
56        assert parent.name == self.parent, "Unexpected parent!"
57        self.parent_instance = parent
58        self.parent_instance.children.append(self)
59
60    def get_component_references(self):
61        """get_component_references() -> iter
62
63        Return an iterator over the named references to other components from
64        this object. Items are of the form (reference-type, component-name).
65        """
66
67        # Parent references are handled specially.
68        for r in self.dependencies:
69            yield ('dependency', r)
70
71    def get_llvmbuild_fragment(self):
72        abstract
73
74    def get_parent_target_group(self):
75        """get_parent_target_group() -> ComponentInfo or None
76
77        Return the nearest parent target group (if any), or None if the
78        component is not part of any target group.
79        """
80
81        # If this is a target group, return it.
82        if self.type_name == 'TargetGroup':
83            return self
84
85        # Otherwise recurse on the parent, if any.
86        if self.parent_instance:
87            return self.parent_instance.get_parent_target_group()
88
89class GroupComponentInfo(ComponentInfo):
90    """
91    Group components have no semantics as far as the build system are concerned,
92    but exist to help organize other components into a logical tree structure.
93    """
94
95    type_name = 'Group'
96
97    @staticmethod
98    def parse(subpath, items):
99        kwargs = ComponentInfo.parse_items(items, has_dependencies = False)
100        return GroupComponentInfo(subpath, **kwargs)
101
102    def __init__(self, subpath, name, parent):
103        ComponentInfo.__init__(self, subpath, name, [], parent)
104
105    def get_llvmbuild_fragment(self):
106        return """\
107type = %s
108name = %s
109parent = %s
110""" % (self.type_name, self.name, self.parent)
111
112class LibraryComponentInfo(ComponentInfo):
113    type_name = 'Library'
114
115    @staticmethod
116    def parse_items(items):
117        kwargs = ComponentInfo.parse_items(items)
118        kwargs['library_name'] = items.get_optional_string('library_name')
119        kwargs['required_libraries'] = items.get_list('required_libraries')
120        kwargs['add_to_library_groups'] = items.get_list(
121            'add_to_library_groups')
122        kwargs['installed'] = items.get_optional_bool('installed', True)
123        return kwargs
124
125    @staticmethod
126    def parse(subpath, items):
127        kwargs = LibraryComponentInfo.parse_items(items)
128        return LibraryComponentInfo(subpath, **kwargs)
129
130    def __init__(self, subpath, name, dependencies, parent, library_name,
131                 required_libraries, add_to_library_groups, installed):
132        ComponentInfo.__init__(self, subpath, name, dependencies, parent)
133
134        # If given, the name to use for the library instead of deriving it from
135        # the component name.
136        self.library_name = library_name
137
138        # The names of the library components which are required when linking
139        # with this component.
140        self.required_libraries = list(required_libraries)
141
142        # The names of the library group components this component should be
143        # considered part of.
144        self.add_to_library_groups = list(add_to_library_groups)
145
146        # Whether or not this library is installed.
147        self.installed = installed
148
149    def get_component_references(self):
150        for r in ComponentInfo.get_component_references(self):
151            yield r
152        for r in self.required_libraries:
153            yield ('required library', r)
154        for r in self.add_to_library_groups:
155            yield ('library group', r)
156
157    def get_llvmbuild_fragment(self):
158        result = """\
159type = %s
160name = %s
161parent = %s
162""" % (self.type_name, self.name, self.parent)
163        if self.library_name is not None:
164            result += 'library_name = %s\n' % self.library_name
165        if self.required_libraries:
166            result += 'required_libraries = %s\n' % ' '.join(
167                self.required_libraries)
168        if self.add_to_library_groups:
169            result += 'add_to_library_groups = %s\n' % ' '.join(
170                self.add_to_library_groups)
171        if not self.installed:
172            result += 'installed = 0\n'
173        return result
174
175    def get_library_name(self):
176        return self.library_name or self.name
177
178    def get_prefixed_library_name(self):
179        """
180        get_prefixed_library_name() -> str
181
182        Return the library name prefixed by the project name. This is generally
183        what the library name will be on disk.
184        """
185
186        basename = self.get_library_name()
187
188        # FIXME: We need to get the prefix information from an explicit project
189        # object, or something.
190        if basename in ('gtest', 'gtest_main'):
191            return basename
192
193        return 'LLVM%s' % basename
194
195    def get_llvmconfig_component_name(self):
196        return self.get_library_name().lower()
197
198class OptionalLibraryComponentInfo(LibraryComponentInfo):
199    type_name = "OptionalLibrary"
200
201    @staticmethod
202    def parse(subpath, items):
203      kwargs = LibraryComponentInfo.parse_items(items)
204      return OptionalLibraryComponentInfo(subpath, **kwargs)
205
206    def __init__(self, subpath, name, dependencies, parent, library_name,
207                 required_libraries, add_to_library_groups, installed):
208      LibraryComponentInfo.__init__(self, subpath, name, dependencies, parent,
209                                    library_name, required_libraries,
210                                    add_to_library_groups, installed)
211
212class LibraryGroupComponentInfo(ComponentInfo):
213    type_name = 'LibraryGroup'
214
215    @staticmethod
216    def parse(subpath, items):
217        kwargs = ComponentInfo.parse_items(items, has_dependencies = False)
218        kwargs['required_libraries'] = items.get_list('required_libraries')
219        kwargs['add_to_library_groups'] = items.get_list(
220            'add_to_library_groups')
221        return LibraryGroupComponentInfo(subpath, **kwargs)
222
223    def __init__(self, subpath, name, parent, required_libraries = [],
224                 add_to_library_groups = []):
225        ComponentInfo.__init__(self, subpath, name, [], parent)
226
227        # The names of the library components which are required when linking
228        # with this component.
229        self.required_libraries = list(required_libraries)
230
231        # The names of the library group components this component should be
232        # considered part of.
233        self.add_to_library_groups = list(add_to_library_groups)
234
235    def get_component_references(self):
236        for r in ComponentInfo.get_component_references(self):
237            yield r
238        for r in self.required_libraries:
239            yield ('required library', r)
240        for r in self.add_to_library_groups:
241            yield ('library group', r)
242
243    def get_llvmbuild_fragment(self):
244        result = """\
245type = %s
246name = %s
247parent = %s
248""" % (self.type_name, self.name, self.parent)
249        if self.required_libraries and not self._is_special_group:
250            result += 'required_libraries = %s\n' % ' '.join(
251                self.required_libraries)
252        if self.add_to_library_groups:
253            result += 'add_to_library_groups = %s\n' % ' '.join(
254                self.add_to_library_groups)
255        return result
256
257    def get_llvmconfig_component_name(self):
258        return self.name.lower()
259
260class TargetGroupComponentInfo(ComponentInfo):
261    type_name = 'TargetGroup'
262
263    @staticmethod
264    def parse(subpath, items):
265        kwargs = ComponentInfo.parse_items(items, has_dependencies = False)
266        kwargs['required_libraries'] = items.get_list('required_libraries')
267        kwargs['add_to_library_groups'] = items.get_list(
268            'add_to_library_groups')
269        kwargs['has_jit'] = items.get_optional_bool('has_jit', False)
270        kwargs['has_asmprinter'] = items.get_optional_bool('has_asmprinter',
271                                                           False)
272        kwargs['has_asmparser'] = items.get_optional_bool('has_asmparser',
273                                                          False)
274        kwargs['has_disassembler'] = items.get_optional_bool('has_disassembler',
275                                                             False)
276        return TargetGroupComponentInfo(subpath, **kwargs)
277
278    def __init__(self, subpath, name, parent, required_libraries = [],
279                 add_to_library_groups = [], has_jit = False,
280                 has_asmprinter = False, has_asmparser = False,
281                 has_disassembler = False):
282        ComponentInfo.__init__(self, subpath, name, [], parent)
283
284        # The names of the library components which are required when linking
285        # with this component.
286        self.required_libraries = list(required_libraries)
287
288        # The names of the library group components this component should be
289        # considered part of.
290        self.add_to_library_groups = list(add_to_library_groups)
291
292        # Whether or not this target supports the JIT.
293        self.has_jit = bool(has_jit)
294
295        # Whether or not this target defines an assembly printer.
296        self.has_asmprinter = bool(has_asmprinter)
297
298        # Whether or not this target defines an assembly parser.
299        self.has_asmparser = bool(has_asmparser)
300
301        # Whether or not this target defines an disassembler.
302        self.has_disassembler = bool(has_disassembler)
303
304        # Whether or not this target is enabled. This is set in response to
305        # configuration parameters.
306        self.enabled = False
307
308    def get_component_references(self):
309        for r in ComponentInfo.get_component_references(self):
310            yield r
311        for r in self.required_libraries:
312            yield ('required library', r)
313        for r in self.add_to_library_groups:
314            yield ('library group', r)
315
316    def get_llvmbuild_fragment(self):
317        result = """\
318type = %s
319name = %s
320parent = %s
321""" % (self.type_name, self.name, self.parent)
322        if self.required_libraries:
323            result += 'required_libraries = %s\n' % ' '.join(
324                self.required_libraries)
325        if self.add_to_library_groups:
326            result += 'add_to_library_groups = %s\n' % ' '.join(
327                self.add_to_library_groups)
328        for bool_key in ('has_asmparser', 'has_asmprinter', 'has_disassembler',
329                         'has_jit'):
330            if getattr(self, bool_key):
331                result += '%s = 1\n' % (bool_key,)
332        return result
333
334    def get_llvmconfig_component_name(self):
335        return self.name.lower()
336
337class ToolComponentInfo(ComponentInfo):
338    type_name = 'Tool'
339
340    @staticmethod
341    def parse(subpath, items):
342        kwargs = ComponentInfo.parse_items(items)
343        kwargs['required_libraries'] = items.get_list('required_libraries')
344        return ToolComponentInfo(subpath, **kwargs)
345
346    def __init__(self, subpath, name, dependencies, parent,
347                 required_libraries):
348        ComponentInfo.__init__(self, subpath, name, dependencies, parent)
349
350        # The names of the library components which are required to link this
351        # tool.
352        self.required_libraries = list(required_libraries)
353
354    def get_component_references(self):
355        for r in ComponentInfo.get_component_references(self):
356            yield r
357        for r in self.required_libraries:
358            yield ('required library', r)
359
360    def get_llvmbuild_fragment(self):
361        return """\
362type = %s
363name = %s
364parent = %s
365required_libraries = %s
366""" % (self.type_name, self.name, self.parent,
367       ' '.join(self.required_libraries))
368
369class BuildToolComponentInfo(ToolComponentInfo):
370    type_name = 'BuildTool'
371
372    @staticmethod
373    def parse(subpath, items):
374        kwargs = ComponentInfo.parse_items(items)
375        kwargs['required_libraries'] = items.get_list('required_libraries')
376        return BuildToolComponentInfo(subpath, **kwargs)
377
378###
379
380class IniFormatParser(dict):
381    def get_list(self, key):
382        # Check if the value is defined.
383        value = self.get(key)
384        if value is None:
385            return []
386
387        # Lists are just whitespace separated strings.
388        return value.split()
389
390    def get_optional_string(self, key):
391        value = self.get_list(key)
392        if not value:
393            return None
394        if len(value) > 1:
395            raise ParseError("multiple values for scalar key: %r" % key)
396        return value[0]
397
398    def get_string(self, key):
399        value = self.get_optional_string(key)
400        if not value:
401            raise ParseError("missing value for required string: %r" % key)
402        return value
403
404    def get_optional_bool(self, key, default = None):
405        value = self.get_optional_string(key)
406        if not value:
407            return default
408        if value not in ('0', '1'):
409            raise ParseError("invalid value(%r) for boolean property: %r" % (
410                    value, key))
411        return bool(int(value))
412
413    def get_bool(self, key):
414        value = self.get_optional_bool(key)
415        if value is None:
416            raise ParseError("missing value for required boolean: %r" % key)
417        return value
418
419_component_type_map = dict(
420    (t.type_name, t)
421    for t in (GroupComponentInfo,
422              LibraryComponentInfo, LibraryGroupComponentInfo,
423              ToolComponentInfo, BuildToolComponentInfo,
424              TargetGroupComponentInfo, OptionalLibraryComponentInfo))
425def load_from_path(path, subpath):
426    # Load the LLVMBuild.txt file as an .ini format file.
427    parser = configparser.RawConfigParser()
428    parser.read(path)
429
430    # Extract the common section.
431    if parser.has_section("common"):
432        common = IniFormatParser(parser.items("common"))
433        parser.remove_section("common")
434    else:
435        common = IniFormatParser({})
436
437    return common, _read_components_from_parser(parser, path, subpath)
438
439def _read_components_from_parser(parser, path, subpath):
440    # We load each section which starts with 'component' as a distinct component
441    # description (so multiple components can be described in one file).
442    for section in parser.sections():
443        if not section.startswith('component'):
444            # We don't expect arbitrary sections currently, warn the user.
445            warning("ignoring unknown section %r in %r" % (section, path))
446            continue
447
448        # Determine the type of the component to instantiate.
449        if not parser.has_option(section, 'type'):
450            fatal("invalid component %r in %r: %s" % (
451                    section, path, "no component type"))
452
453        type_name = parser.get(section, 'type')
454        type_class = _component_type_map.get(type_name)
455        if type_class is None:
456            fatal("invalid component %r in %r: %s" % (
457                    section, path, "invalid component type: %r" % type_name))
458
459        # Instantiate the component based on the remaining values.
460        try:
461            info = type_class.parse(subpath,
462                                    IniFormatParser(parser.items(section)))
463        except TypeError:
464            print >>sys.stderr, "error: invalid component %r in %r: %s" % (
465                section, path, "unable to instantiate: %r" % type_name)
466            import traceback
467            traceback.print_exc()
468            raise SystemExit(1)
469        except ParseError:
470            e = sys.exc_info()[1]
471            fatal("unable to load component %r in %r: %s" % (
472                    section, path, e.message))
473
474        info._source_path = path
475        yield info
476