llvm-build: Fill in more of component parsing to be more strict and
authorDaniel Dunbar <daniel@zuster.org>
Thu, 3 Nov 2011 17:56:10 +0000 (17:56 +0000)
committerDaniel Dunbar <daniel@zuster.org>
Thu, 3 Nov 2011 17:56:10 +0000 (17:56 +0000)
differentiate between strings and lists.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@143622 91177308-0d34-0410-b5e6-96231b3b80d8

utils/llvm-build/llvmbuild/componentinfo.py
utils/llvm-build/llvmbuild/util.py [new file with mode: 0644]

index a434bd884c0ea2bf976e7a6e8514e4ec64ae5ece..e15dbda756f9e3796cb9f497534b2bc2919026b2 100644 (file)
@@ -5,6 +5,11 @@ Descriptor objects for entities that are part of the LLVM project.
 import ConfigParser
 import sys
 
+from util import *
+
+class ParseError(Exception):
+    pass
+
 class ComponentInfo(object):
     """
     Base class for component descriptions.
@@ -12,6 +17,15 @@ class ComponentInfo(object):
 
     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
@@ -31,14 +45,28 @@ class GroupComponentInfo(ComponentInfo):
 
     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)
 
 class LibraryComponentInfo(ComponentInfo):
     type_name = 'Library'
 
-    def __init__(self, subpath, name, dependencies, parent, library_name = None,
-                 required_libraries = [], add_to_library_groups = []):
+    @staticmethod
+    def parse(subpath, items):
+        kwargs = ComponentInfo.parse_items(items)
+        kwargs['library_name'] = items.get_optional_string('name')
+        kwargs['required_libraries'] = items.get_list('required_libraries')
+        kwargs['add_to_library_groups'] = items.get_list(
+            'add_to_library_groups')
+        return LibraryComponentInfo(subpath, **kwargs)
+
+    def __init__(self, subpath, name, dependencies, parent, library_name,
+                 required_libraries, add_to_library_groups):
         ComponentInfo.__init__(self, subpath, name, dependencies, parent)
 
         # If given, the name to use for the library instead of deriving it from
@@ -56,6 +84,14 @@ class LibraryComponentInfo(ComponentInfo):
 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)
@@ -71,8 +107,14 @@ class LibraryGroupComponentInfo(ComponentInfo):
 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 = []):
+                 required_libraries):
         ComponentInfo.__init__(self, subpath, name, dependencies, parent)
 
         # The names of the library components which are required to link this
@@ -82,6 +124,38 @@ class ToolComponentInfo(ComponentInfo):
 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
+
 _component_type_map = dict(
     (t.type_name, t)
     for t in (GroupComponentInfo,
@@ -97,38 +171,32 @@ def load_from_path(path, subpath):
     for section in parser.sections():
         if not section.startswith('component'):
             # We don't expect arbitrary sections currently, warn the user.
-            print >>sys.stderr, "warning: ignoring unknown section %r in %r" % (
-                section, path)
+            warning("ignoring unknown section %r in %r" % (section, path))
             continue
 
-        # Load the component that this section describes. For now we just do
-        # this the trivial way by letting python validate the argument
-        # assignment. This is simple, but means users see lame diagnostics. We
-        # should audit the component manually, eventually.
+        # Determine the type of the component to instantiate.
         if not parser.has_option(section, 'type'):
-            print >>sys.stderr, "error: invalid component %r in %r: %s" % (
-                section, path, "no component type")
-            raise SystemExit, 1
+            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:
-            print >>sys.stderr, "error: invalid component %r in %r: %s" % (
-                section, path, "invalid component type: %r" % type_name)
-            raise SystemExit, 1
-
-        items = dict(parser.items(section))
-        items['subpath'] = subpath
-        items.pop('type')
+            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(**items)
+            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:
+            fatal("unable to load component %r in %r: %s" % (
+                    section, path, e.message))
 
         yield info
diff --git a/utils/llvm-build/llvmbuild/util.py b/utils/llvm-build/llvmbuild/util.py
new file mode 100644 (file)
index 0000000..cf6fa28
--- /dev/null
@@ -0,0 +1,20 @@
+import inspect
+import os
+import sys
+
+def _write_message(kind, message):
+    # Get the file/line where this message was generated.
+    f = inspect.currentframe()
+    # Step out of _write_message, and then out of wrapper.
+    f = f.f_back.f_back
+    file,line,_,_,_ = inspect.getframeinfo(f)
+    location = '%s:%d' % (os.path.basename(file), line)
+
+    print >>sys.stderr, '%s: %s: %s' % (location, kind, message)
+
+note = lambda message: _write_message('note', message)
+warning = lambda message: _write_message('warning', message)
+error = lambda message: _write_message('error', message)
+fatal = lambda message: (_write_message('fatal error', message), sys.exit(1))
+
+__all__ = ['note', 'warning', 'error', 'fatal']