Commits

Juri Pakaste  committed 4eff1ea

Support reading from standard input. Also implemented file cache so extracting values should be faster.

  • Participants
  • Parent commits 4a537e9

Comments (0)

Files changed (3)

File NSStringFromEnumGenerator.py

 
 from collections import namedtuple, OrderedDict
 from functools import reduce
+from tempfile import NamedTemporaryFile
 import os.path
 import sys
 
         r.extend(print_tree(c, depth + 1))
     return r
 
+class FileReader(object):
+    def __init__(self, size):
+        self.size = size
+        self.files = OrderedDict()
+        self.fixed_files = {}
+
+    def insert_content(self, sourcefile, content):
+        self.fixed_files[os.path.abspath(sourcefile)] = content
+
+    def read_extent(self, sourcefile, extent):
+        sfpath = os.path.abspath(sourcefile)
+        content = self.fixed_files.get(sfpath)
+        if not content:
+            content = self.files.get(sfpath)
+            if not content:
+                with open(sfpath) as fo:
+                    content = fo.read()
+                self.files[sfpath] = content
+            else:
+                # refresh LRU order
+                del self.files[sfpath]
+                self.files[sfpath] = content
+            while len(self.files) > self.size:
+                self.files.popitem(last = False)
+        return content[extent.start.offset:extent.end.offset]
+
 def extract_value(sourcefile, extent):
     with open(sourcefile) as fo:
         fo.seek(extent.start.offset)
         bytes = fo.read(extent.end.offset - extent.start.offset)
         return bytes
 
-def handle_enum(node, typedef_name):
+def handle_enum(node, typedef_name, file_reader):
     # sys.stdout.write("\n".join(print_tree(node)) + "\n")
 
     pe = ParsedEnum(node.spelling, typedef_name)
 
             childlist = list(c.get_children())
             if len(childlist) > 0:
-                vbytes = extract_value(c.location.file.name, childlist[0].extent)
+                vbytes = file_reader.read_extent(c.location.file.name, childlist[0].extent)
                 value = vbytes
 
             pe.values.append(EnumValue(name, value))
     return pe
 
-def create_finder(path):
+def create_finder(path, file_reader):
     ppath = os.path.abspath(path)
 
     def find_enums(node, parents = ()):
 
         # we get nil files at the start for some reason
         if node.location.file is not None and os.path.abspath(node.location.file.name) != ppath:
-           return []
+            return []
         try:
             kind = node.kind
         except ValueError:
             typedef_name = None
             if len(parents) and parents[-1].kind == clang.cindex.CursorKind.TYPEDEF_DECL:
                 typedef_name = parents[-1].spelling
-            enums.append(handle_enum(node, typedef_name))
+            enums.append(handle_enum(node, typedef_name, file_reader))
         # typeref inside a typedecl pointing to an enum we can find with get_definition()
         elif kind == clang.cindex.CursorKind.TYPE_REF and node.type.kind == clang.cindex.TypeKind.ENUM and len(parents) > 0 and parents[-1].kind == clang.cindex.CursorKind.TYPEDEF_DECL:
-            enums.append(handle_enum(node.get_definition(), parents[-1].spelling))
+            enums.append(handle_enum(node.get_definition(), parents[-1].spelling, file_reader))
         else:
             for c in node.get_children():
                 enums.extend(find_enums(c, parents + (node,)))
 
 
 
-def funs_from_file(f, includes, mask):
+def funs_from_file(f, includes, mask, contents = None):
     index = clang.cindex.Index.create()
     includeargs = reduce(lambda acc, e: acc + e, [["-include", inc] for inc in includes], []) if includes else []
-    tu = index.parse(f, args = ["-x", "objective-c"] + includeargs)
+    unsaved = []
+    if contents:
+        # create a dummy file if we get contents, because seems libclang is not happy with a name like "-"
+        ntemp = NamedTemporaryFile(suffix = ".h")
+        f = ntemp.name
+        unsaved = [[f, contents]]
+    tu = index.parse(f, args = ["-x", "objective-c"] + includeargs, unsaved_files = unsaved)
     if not tu:
         raise NSGException("Unable to parse input {0} for unknown reason".format(f))
     if tu.diagnostics:
         msg = "\n".join([str(d) for d in tu.diagnostics])
         raise NSGException(msg)
 
-    finder = create_finder(f)
+    file_reader = FileReader(10)
+    for fname, content in unsaved:
+        file_reader.insert_content(fname, content)
+
+    finder = create_finder(f, file_reader)
     enums = finder(tu.cursor)
     creator = create_mask_fun if mask else create_fun
     return [creator(e) for e in enums if e.preferred_name]
    }
  }
 
+You can use ``-`` as the file name, in which case ``nsstringfromenumgen`` expects to receive the file contents from standard input. You can use this to feed a single enum definition ``nsstringfromenumgen``, instead of a whole source file. However, if you're using the recently added ``NS_ENUM`` and ``NS_OPTIONS`` macros, you'll need to use the ``--include`` flag to include ``NSObjCRuntime.h`` (or another header that includes it, such as ``Foundation.h``.)
+
 If your code relies on a prefix header, you need to include it on the command line with the ``--include`` flag. You can specify more than one of them if necessary.
 
 You can also instruct ``nsstringfromenumgen`` to treat your enums as options for a bitmask by including the ``--mask`` flag::

File nsstringfromenumgen

 from NSStringFromEnumGenerator import funs_from_file, NSGException
 
 aparse = argparse.ArgumentParser(description = "Create NSStringFromEnumeration Objective-C functions based on Objective-C source files")
-aparse.add_argument("files", type=str, nargs="+", help="Files to parse")
-aparse.add_argument("--include", action="append", help="Include file")
-aparse.add_argument("--mask", action="store_true", help="Assume enums are used as bitmasks")
+aparse.add_argument("files", type=str, nargs="+", help="files to parse, can be - to indicate standard input")
+aparse.add_argument("--include", action="append", help="include file")
+aparse.add_argument("--mask", action="store_true", help="assume enums are used as bitmasks")
 args = aparse.parse_args()
 
 try:
     for fname in args.files:
-        for fun in funs_from_file(fname, args.include, args.mask):
+        contents = None
+        if fname == '-':
+            contents = sys.stdin.read()
+            fname = "dummy.h"
+        for fun in funs_from_file(fname, args.include, args.mask, contents):
             sys.stdout.write(fun)
             sys.stdout.write("\n")
 except NSGException, e: