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