def findClassesAndLocations(where): """Return list of class names and dict of Class.cpp locations for each class""" classes = [] locations = {} def appendClasses( fileName, classList, prefix ): for c in classList: c.name = prefix + c.name classes.append( c ) locations[ c.name ] = fileName appendClasses( fileName, c._class, c.name ) def findCppHeaders(top): for dirname, subdirs, files in os.walk(top): if not re.search('\\bUnitTest\\b', dirname): for f in [ f for f in files if f.endswith(".h") ]: yield os.path.join(dirname, f) for fileName in findCppHeaders(where): if util.hasReflectionDeclaration( open(fileName).read() ): dom = headerToDom.headerToDom( fileName ) if dom.file.product != "NONE" and dom.file.platform != "NONE": filename = dom.file.destinationFilename(dom.localfilename, "Class.cpp") appendClasses( filename, dom.file._class, '' ) return classes, locations
def get_reflected_files(where, lst): for f in lst: full = os.path.join(where,f) if f.endswith(".h"): content = open(full).read() tkbms = util.extract_tkbms(content) valid_tkbms = lambda x : (not tkbms.has_key(x) or tkbms[x] != "NONE") if valid_tkbms("platform") and valid_tkbms("product") and util.hasReflectionDeclaration(content): yield full elif f.endswith(".hkclass") and not os.path.exists(full.replace(".hkclass",".h")): yield full
def classToHeader(classfile, options): if options.verbose: print "Processing '%s'" % classfile dom = hkcToDom.hkcToDom(classfile) headerfile, cppfile = classfile.replace(".hkclass",".h"), classfile.replace(".hkclass","Class.cpp") try: if util.hasReflectionDeclaration(open(headerfile).read()): raise RuntimeError("'%s' contains HK_DECLARE_REFLECTION. Not overwriting" % headerfile) except IOError: pass writeIfDifferent( domToHeader.domToHeader(dom), headerfile ) writeIfDifferent( domToClass.domToClass(dom), cppfile ) if options.cvsignore: removeFromCvsignore(classfile) addToCvsignore(headerfile) addToCvsignore(cppfile)
def processDir(where, options): ismeta = lambda f: f.endswith("h") and util.hasReflectionDeclaration( open(os.path.join(dirname, f)).read()) nfile = 0 for dirname, subdirs, files in os.walk(where): headers = [os.path.join(dirname, f) for f in files if ismeta(f)] hkclasses = [ os.path.join(dirname, f) for f in files if f.endswith(".hkclass") ] generated = {} for header in headers: nfile += 1 classfile = header.replace(".h", ".hkclass") if classfile in hkclasses: hkclasses.remove(classfile) headerToClass(header, options) generated[header.replace(".h", "Class.cpp")] = 1 for hkcname in hkclasses: nfile += 1 classToHeader(hkcname, options) generated[hkcname.replace(".hkclass", "Class.cpp")] = 1 for f in [ os.path.join(dirname, f) for f in files if (f.endswith("Class.cpp") and f != "hkClass.cpp") ]: if generated.get(f, 0) == 0 and not MAGIC_DESTINATION_MARK.search( open(f).read()): # rename stale files to numbered backup for i in range(0, 100): try: backup = "%s.%i" % (f, i) open(backup) except IOError: print "STALE %s -> %s" % (f, backup) os.rename(f, backup) break try: subdirs.remove("CVS") except ValueError: pass if options.local_only: break if not options.quiet: print "DONE", where, nfile, "files processed"
def classToHeader(classfile, options): if options.verbose: print "Processing '%s'" % classfile dom = hkcToDom.hkcToDom(classfile) headerfile, cppfile = classfile.replace(".hkclass", ".h"), classfile.replace( ".hkclass", "Class.cpp") try: if util.hasReflectionDeclaration(open(headerfile).read()): raise RuntimeError( "'%s' contains HK_DECLARE_REFLECTION. Not overwriting" % headerfile) except IOError: pass writeIfDifferent(domToHeader.domToHeader(dom), headerfile) writeIfDifferent(domToClass.domToClass(dom), cppfile) if options.cvsignore: removeFromCvsignore(classfile) addToCvsignore(headerfile) addToCvsignore(cppfile)
def processDir(where, options): ismeta = lambda f : f.endswith("h") and util.hasReflectionDeclaration(open(os.path.join(dirname,f)).read()) nfile = 0 for dirname, subdirs, files in os.walk(where): headers = [ os.path.join(dirname,f) for f in files if ismeta(f) ] hkclasses = [ os.path.join(dirname,f) for f in files if f.endswith(".hkclass") ] generated = {} for header in headers: nfile += 1 classfile = header.replace(".h",".hkclass") if classfile in hkclasses: hkclasses.remove(classfile) headerToClass(header, options) generated[header.replace(".h","Class.cpp")] = 1 for hkcname in hkclasses: nfile += 1 classToHeader(hkcname, options) generated[hkcname.replace(".hkclass","Class.cpp")] = 1 for f in [ os.path.join(dirname,f) for f in files if (f.endswith("Class.cpp") and f != "hkClass.cpp") ]: if generated.get(f,0) == 0 and not MAGIC_DESTINATION_MARK.search(open(f).read()): # rename stale files to numbered backup for i in range(0,100): try: backup = "%s.%i" % (f,i) open( backup ) except IOError: print "STALE %s -> %s" % (f,backup) os.rename(f, backup) break try: subdirs.remove("CVS") except ValueError: pass if options.local_only: break if not options.quiet: print "DONE", where, nfile, "files processed"
def parseClass(self, match, txt, declaredscope): if match.group("decltype") == "{": self.debug("CLASS (%s) (%s) (%s) (%s)" % (match.group("type"), match.group("namespace"), match.group("name"), match.group("parents"))) span = match.span() classEnd = self.findEndBrace(txt, span[1] ) klass = hkcToDom.Class() klass.scope = declaredscope klass.fromheader = 1 klass.name = match.group("name") parents = match.group("parents") if parents: getparentname = lambda x : x[len(x) > 1 and 1 or 0] parents = [getparentname(x.split()).split("<")[0].strip() for x in parents.split(",")] klass.parent = parents[0] for iname in parents[1:]: ic = hkcToDom.Interface() ic.name = iname klass.interface.append( ic ) body = txt[span[1]:classEnd-1] if match.group("type") == "class": currentAccess = "private" else: currentAccess = "public" def appendMember(mname, mtype, mvis, msrc, mflags=""): assert len(mname) if len(mtype) == 0: print >>sys.stderr, "Error: MISSING STRING AFTER '%s'" % (mname) assert len(mtype) if mname.startswith("m_"): mname = mname[2:] # check for synthetic member types if len(klass.member) >= 1: prev = klass.member[-1] prev2 = klass.member[-2] if len(klass.member) >= 2 else None if mname.startswith("num") and mname[3:].lower() == prev.name.lower(): errPrefix = "%s::%s " % (klass.name, mname) if prev2 and prev2.name.startswith(prev.name) and prev2.name.endswith("Class"): assert prev.type == "void*" assert prev2.type == "hkClass*" prev2.name = prev.name prev2.type = "hkHomogeneousArray" assert prev2.visibility == mvis and prev.visibility == mvis, errPrefix + "homogeneous parts have differing visibility" assert prev2.flags == "" and prev.flags == "", errPrefix + "homogeneous array attributes should go on last member" prev2.flags = mflags prev2.sourcecode += prev.sourcecode + msrc klass.member.pop() return elif prev.type.endswith("*"): prev.type = "hkSimpleArray<%s>" % prev.type.rsplit("*",1)[0].rstrip() assert prev.visibility == mvis, errPrefix + "simplearray parts have differing visibility" assert prev.flags == "", errPrefix + "simplearray attributes should apply to last member" prev.flags = mflags prev.sourcecode += msrc return elif mname.endswith("Class") and prev.name==mname[:-5] \ and mtype=="hkClass*" and prev.type=="void*": prev.type = "hkVariant" return member = hkcToDom.Member() member.name = mname member.type = mtype member.visibility = mvis member.flags = mflags member.sourcecode = msrc klass.member.append(member) def appendMethod(mtxt, parameters, qualifiers, visibility): if 0 not in [ mtxt.find(ignore) == -1 for ignore in "HK_DECLARE HK_ALIGN HK_SPU_VIRTUAL_DECLSPEC".split() ]: def extract(txt, reg): out = re.sub(reg, "", txt) return out, txt!=out method = hkcToDom.Method() method.description = "%s(%s)%s" % (mtxt,parameters,qualifiers) method.visibility = visibility mtxt, method.inline = extract(mtxt, r"\b(inline|HK_FORCE_INLINE)\b") mtxt, method.virtual = extract(mtxt, r"\bvirtual\b") mtxt, method.static = extract(mtxt, r"\bstatic\b") mtxt, constness = extract(mtxt, r"\b(const|explicit)\b") qualifiers, method.const = extract(qualifiers, r"\bconst\b") qualifiers, method.purevirtual = extract(qualifiers, "=\s*0") qualifiers, initializers = extract(qualifiers, r"([:,]\s*\w+\s*\([^\)]*\))+") if qualifiers.replace(")","").strip() != "": print >>sys.stderr, "---------------UNPARSED '%s' in '%s'" % (qualifiers,mtxt) method.name = mtxt.split()[-1] if killwhitespace(parameters): for i, x in enumerate(parameters.split(",")): param = hkcToDom.Parameter() param.description = x valuelist = x.split("=") if len(valuelist) == 2: param.default = valuelist[-1].lstrip() plist = valuelist[0].split() if len([v for v in plist if v != "const"]) > 1 \ and plist[-1] not in [">", "*", "&"]: param.name = plist[-1] param.type = " ".join(v for v in plist[:-1] if v != "const") else: param.type = " ".join(v for v in plist if v != "const") param.name = "noname_"+str(i) method.parameter.append(param) if klass.name == method.name: self.debug("CONSTRUCTOR(%s,%s)" % (method.name, parameters)) assert len(mtxt.split()) == 1 klass.method.append( method ) elif "~"+klass.name == method.name: self.debug("DESTRUCTOR(%s)" % (method.name)) assert len(mtxt.split()) == 1 assert len(method.parameter) == 0 else: method.returns = " ".join( mtxt.split()[:-1] ) self.debug("METHOD(%s,%s::%s,%s)" % (method.returns, klass.name, method.name, parameters)) klass.method.append( method ) lastParsed = None while 1: item, match = self.classMatcher.run(body) if match == None: break newbody = None if item == self.re_accessControl: currentAccess = match.group(1) item = lastParsed # don't affect lastParsed elif item == self.re_classStart: newbody, k = self.parseClass(match, body, klass) if k: klass._class.append(k) elif item == self.re_enumStart: newbody, enum = self.parseEnum(match, body) if enum: enum.scope = klass klass.enum.append(enum) elif item == self.re_unionStart: newbody, union = self.parseUnion(match, body) elif item == self.re_attributesPlaceholder: newbody, attrs = self.parseAttributes(match, body) if lastParsed == None: target = klass elif lastParsed == self.re_classMember: if klass.member: target = klass.member[-1] else: target = None elif lastParsed == self.re_typedef: target = None # ignore attrs on typedefs for now else: raise "Unhandled case for attributes" if target: nosaveflag = False # apply nosave at end for k,v in attrs: if k=="nosave": target.serialized = False nosaveflag = True else: applyAttr(target, k, v) if nosaveflag: t = target.overridetype or target.type if t.endswith("*"): target.overridetype = "void*" else: partial = re.match("(hkArray|hkSimpleArray|hkEnum|hkFlags)\s*<\s*([^>,]+)\s*(,[^>]+)?\s*>", t) if partial: stem, c, sz = partial.groups() target.overridetype = stem + ("<void*" if c.strip().endswith("*") else "<void") + (sz or "") + ">" item = lastParsed # don't affect lastParsed elif item == self.re_classMethod: span = match.span() # actually a function pointer if self.re_functionPointer.search(match.group()): mtype = "void*" mname = match.group().split("(")[1].split("*")[1].split(")")[0].split()[-1] self.debug("MEMBER (%s)" % match.group()) appendMember(mname, mtype, currentAccess, match.group() ) if util.hasReflectionDeclaration(match.group()): self.debug(match.group()) klass.reflected = True elif self.re_memDeclaration.search(match.group()): klass.memory_declaration = (currentAccess, match.group()) self.debug("%s (%s)" % (match.group(), currentAccess)) else: if not klass.abstract and killwhitespace(match.group("qualifiers")).find("=0") != -1: self.debug("ABSTRACT") klass.abstract = True elif not klass.vtable and "virtual" in match.group().split(): self.debug("VIRTUAL") klass.vtable = True appendMethod(match.group("method"), match.group("params"), match.group("qualifiers"), currentAccess ) if match.group("decltype") == "{": methodEnd = self.findEndBrace(body, span[1] ) newbody = body[methodEnd:] elif item == self.re_classMember: bits = self.re_constInDecl.sub(" ", match.group().rstrip(";")) bits = self.re_whitespaceInArray.sub(r"[\1]", bits).split() if bits[0] not in ("friend", "static", "typedef"): mtype, mname = " ".join(bits[:-1]), bits[-1] k = klass #todo: should lookup parent first, but often don't have definition while k: # look at enclosing scopes too mtype = k.typedef.get(mtype, mtype) k = k.scope mflags = "" if bits[0] == "enum": print "%s:Implicitly sized enum is not portable (%s). Use hkEnum<> instead." % (self.location_of(re.compile("^\s*enum[^;]*"+bits[2],re.M)), " ".join(bits)) arrayBegin = mname.find("[") if arrayBegin != -1: mtype += mname[arrayBegin:] mname = mname[:arrayBegin] self.debug("MEMBER (%s)" % match.group()) appendMember(mname, mtype, currentAccess, match.group(), mflags) elif item == self.re_typedef: bits = [b for b in match.group()[:-1].split(" ") if b] klass.typedef[bits[-1]] = " ".join(bits[1:-1]) elif item == self.re_spuriousSemicolon: pass else: self.debug("NO_ACTION %s %s"% (match.group(), item)) lastParsed = item oldBodyLength = len(body) if newbody: body = newbody else: body = body[match.span()[1]:] if len(body) >= oldBodyLength: raise ("*** Breaking from infinite loop ***\n" \ "While parsing '%s'\n" \ "'%s'" % (klass.name, body) ) return txt[classEnd:], klass else: self.debug("FORWARD (%s) (%s) (%s) (%s)" % (match.group("type"), match.group("namespace"), match.group("name"), match.group("parents"))) return None, None