llvm-build: Fill in some details w.r.t. component's parents.
[oota-llvm.git] / utils / llvm-build / llvmbuild / componentinfo.py
1 """
2 Descriptor objects for entities that are part of the LLVM project.
3 """
4
5 import ConfigParser
6 import sys
7
8 from util import *
9
10 class ParseError(Exception):
11     pass
12
13 class ComponentInfo(object):
14     """
15     Base class for component descriptions.
16     """
17
18     type_name = None
19
20     @staticmethod
21     def parse_items(items, has_dependencies = True):
22         kwargs = {}
23         kwargs['name'] = items.get_string('name')
24         kwargs['parent'] = items.get_optional_string('parent')
25         if has_dependencies:
26             kwargs['dependencies'] = items.get_list('dependencies')
27         return kwargs
28
29     def __init__(self, subpath, name, dependencies, parent):
30         if not subpath.startswith('/'):
31             raise ValueError,"invalid subpath: %r" % subpath
32         self.subpath = subpath
33         self.name = name
34         self.dependencies = list(dependencies)
35
36         # The name of the parent component to logically group this component
37         # under.
38         self.parent = parent
39
40         # The parent instance, once loaded.
41         self.parent_instance = None
42         self.children = []
43
44     def set_parent_instance(self, parent):
45         assert parent.name == self.parent, "Unexpected parent!"
46         self.parent_instance = parent
47         self.parent_instance.children.append(self)
48
49     def get_component_references(self):
50         """get_component_references() -> iter
51
52         Return an iterator over the named references to other components from
53         this object. Items are of the form (reference-type, component-name).
54         """
55
56         # Parent references are handled specially.
57         for r in self.dependencies:
58             yield ('dependency', r)
59
60 class GroupComponentInfo(ComponentInfo):
61     """
62     Group components have no semantics as far as the build system are concerned,
63     but exist to help organize other components into a logical tree structure.
64     """
65
66     type_name = 'Group'
67
68     @staticmethod
69     def parse(subpath, items):
70         kwargs = ComponentInfo.parse_items(items, has_dependencies = False)
71         return GroupComponentInfo(subpath, **kwargs)
72
73     def __init__(self, subpath, name, parent):
74         ComponentInfo.__init__(self, subpath, name, [], parent)
75
76 class LibraryComponentInfo(ComponentInfo):
77     type_name = 'Library'
78
79     @staticmethod
80     def parse(subpath, items):
81         kwargs = ComponentInfo.parse_items(items)
82         kwargs['library_name'] = items.get_optional_string('name')
83         kwargs['required_libraries'] = items.get_list('required_libraries')
84         kwargs['add_to_library_groups'] = items.get_list(
85             'add_to_library_groups')
86         return LibraryComponentInfo(subpath, **kwargs)
87
88     def __init__(self, subpath, name, dependencies, parent, library_name,
89                  required_libraries, add_to_library_groups):
90         ComponentInfo.__init__(self, subpath, name, dependencies, parent)
91
92         # If given, the name to use for the library instead of deriving it from
93         # the component name.
94         self.library_name = library_name
95
96         # The names of the library components which are required when linking
97         # with this component.
98         self.required_libraries = list(required_libraries)
99
100         # The names of the library group components this component should be
101         # considered part of.
102         self.add_to_library_groups = list(add_to_library_groups)
103
104     def get_component_references(self):
105         for r in ComponentInfo.get_component_references(self):
106             yield r
107         for r in self.required_libraries:
108             yield ('required library', r)
109         for r in self.add_to_library_groups:
110             yield ('library group', r)
111
112 class LibraryGroupComponentInfo(ComponentInfo):
113     type_name = 'LibraryGroup'
114
115     @staticmethod
116     def parse(subpath, items):
117         kwargs = ComponentInfo.parse_items(items, has_dependencies = False)
118         kwargs['required_libraries'] = items.get_list('required_libraries')
119         kwargs['add_to_library_groups'] = items.get_list(
120             'add_to_library_groups')
121         return LibraryGroupComponentInfo(subpath, **kwargs)
122
123     def __init__(self, subpath, name, parent, required_libraries = [],
124                  add_to_library_groups = []):
125         ComponentInfo.__init__(self, subpath, name, [], parent)
126
127         # The names of the library components which are required when linking
128         # with this component.
129         self.required_libraries = list(required_libraries)
130
131         # The names of the library group components this component should be
132         # considered part of.
133         self.add_to_library_groups = list(add_to_library_groups)
134
135     def get_component_references(self):
136         for r in ComponentInfo.get_component_references(self):
137             yield r
138         for r in self.required_libraries:
139             yield ('required library', r)
140         for r in self.add_to_library_groups:
141             yield ('library group', r)
142
143 class ToolComponentInfo(ComponentInfo):
144     type_name = 'Tool'
145
146     @staticmethod
147     def parse(subpath, items):
148         kwargs = ComponentInfo.parse_items(items)
149         kwargs['required_libraries'] = items.get_list('required_libraries')
150         return ToolComponentInfo(subpath, **kwargs)
151
152     def __init__(self, subpath, name, dependencies, parent,
153                  required_libraries):
154         ComponentInfo.__init__(self, subpath, name, dependencies, parent)
155
156         # The names of the library components which are required to link this
157         # tool.
158         self.required_libraries = list(required_libraries)
159
160     def get_component_references(self):
161         for r in ComponentInfo.get_component_references(self):
162             yield r
163         for r in self.required_libraries:
164             yield ('required library', r)
165
166 class BuildToolComponentInfo(ToolComponentInfo):
167     type_name = 'BuildTool'
168
169     @staticmethod
170     def parse(subpath, items):
171         kwargs = ComponentInfo.parse_items(items)
172         kwargs['required_libraries'] = items.get_list('required_libraries')
173         return BuildToolComponentInfo(subpath, **kwargs)
174
175 ###
176
177 class IniFormatParser(dict):
178     def get_list(self, key):
179         # Check if the value is defined.
180         value = self.get(key)
181         if value is None:
182             return []
183
184         # Lists are just whitespace separated strings.
185         return value.split()
186
187     def get_optional_string(self, key):
188         value = self.get_list(key)
189         if not value:
190             return None
191         if len(value) > 1:
192             raise ParseError("multiple values for scalar key: %r" % key)
193         return value[0]
194
195     def get_string(self, key):
196         value = self.get_optional_string(key)
197         if not value:
198             raise ParseError("missing value for required string: %r" % key)
199         return value
200
201 _component_type_map = dict(
202     (t.type_name, t)
203     for t in (GroupComponentInfo,
204               LibraryComponentInfo, LibraryGroupComponentInfo,
205               ToolComponentInfo, BuildToolComponentInfo))
206 def load_from_path(path, subpath):
207     # Load the LLVMBuild.txt file as an .ini format file.
208     parser = ConfigParser.RawConfigParser()
209     parser.read(path)
210
211     # We load each section which starts with 'component' as a distinct component
212     # description (so multiple components can be described in one file).
213     for section in parser.sections():
214         if not section.startswith('component'):
215             # We don't expect arbitrary sections currently, warn the user.
216             warning("ignoring unknown section %r in %r" % (section, path))
217             continue
218
219         # Determine the type of the component to instantiate.
220         if not parser.has_option(section, 'type'):
221             fatal("invalid component %r in %r: %s" % (
222                     section, path, "no component type"))
223
224         type_name = parser.get(section, 'type')
225         type_class = _component_type_map.get(type_name)
226         if type_class is None:
227             fatal("invalid component %r in %r: %s" % (
228                     section, path, "invalid component type: %r" % type_name))
229
230         # Instantiate the component based on the remaining values.
231         try:
232             info = type_class.parse(subpath,
233                                     IniFormatParser(parser.items(section)))
234         except TypeError:
235             print >>sys.stderr, "error: invalid component %r in %r: %s" % (
236                 section, path, "unable to instantiate: %r" % type_name)
237             import traceback
238             traceback.print_exc()
239             raise SystemExit, 1
240         except ParseError,e:
241             fatal("unable to load component %r in %r: %s" % (
242                     section, path, e.message))
243
244         yield info