12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802 |
- #!/usr/bin/env python
- # generator.py
- # simple C++ generator, originally targetted for Spidermonkey bindings
- #
- # Copyright (c) 2011 - Zynga Inc.
- from clang import cindex
- import sys
- import ConfigParser
- import yaml
- import re
- import os
- import inspect
- import traceback
- from Cheetah.Template import Template
- type_map = {
- cindex.TypeKind.VOID : "void",
- cindex.TypeKind.BOOL : "bool",
- cindex.TypeKind.CHAR_U : "unsigned char",
- cindex.TypeKind.UCHAR : "unsigned char",
- cindex.TypeKind.CHAR16 : "char",
- cindex.TypeKind.CHAR32 : "char",
- cindex.TypeKind.USHORT : "unsigned short",
- cindex.TypeKind.UINT : "unsigned int",
- cindex.TypeKind.ULONG : "unsigned long",
- cindex.TypeKind.ULONGLONG : "unsigned long long",
- cindex.TypeKind.CHAR_S : "char",
- cindex.TypeKind.SCHAR : "char",
- cindex.TypeKind.WCHAR : "wchar_t",
- cindex.TypeKind.SHORT : "short",
- cindex.TypeKind.INT : "int",
- cindex.TypeKind.LONG : "long",
- cindex.TypeKind.LONGLONG : "long long",
- cindex.TypeKind.FLOAT : "float",
- cindex.TypeKind.DOUBLE : "double",
- cindex.TypeKind.LONGDOUBLE : "long double",
- cindex.TypeKind.NULLPTR : "NULL",
- cindex.TypeKind.OBJCID : "id",
- cindex.TypeKind.OBJCCLASS : "class",
- cindex.TypeKind.OBJCSEL : "SEL",
- # cindex.TypeKind.ENUM : "int"
- }
- INVALID_NATIVE_TYPE = "??"
- default_arg_type_arr = [
- # An integer literal.
- cindex.CursorKind.INTEGER_LITERAL,
- # A floating point number literal.
- cindex.CursorKind.FLOATING_LITERAL,
- # An imaginary number literal.
- cindex.CursorKind.IMAGINARY_LITERAL,
- # A string literal.
- cindex.CursorKind.STRING_LITERAL,
- # A character literal.
- cindex.CursorKind.CHARACTER_LITERAL,
- # [C++ 2.13.5] C++ Boolean Literal.
- cindex.CursorKind.CXX_BOOL_LITERAL_EXPR,
- # [C++0x 2.14.7] C++ Pointer Literal.
- cindex.CursorKind.CXX_NULL_PTR_LITERAL_EXPR,
- cindex.CursorKind.GNU_NULL_EXPR,
- # An expression that refers to some value declaration, such as a function,
- # varible, or enumerator.
- cindex.CursorKind.DECL_REF_EXPR
- ]
- stl_type_map = {
- 'std_function_args': 1000,
- 'std::unordered_map': 2,
- 'std::unordered_multimap': 2,
- 'std::map': 2,
- 'std::multimap': 2,
- 'std::vector': 1,
- 'std::list': 1,
- 'std::forward_list': 1,
- 'std::priority_queue': 1,
- 'std::set': 1,
- 'std::multiset': 1,
- 'std::unordered_set': 1,
- 'std::unordered_multiset': 1,
- 'std::stack': 1,
- 'std::queue': 1,
- 'std::deque': 1,
- 'std::array': 1,
- 'unordered_map': 2,
- 'unordered_multimap': 2,
- 'map': 2,
- 'multimap': 2,
- 'vector': 1,
- 'list': 1,
- 'forward_list': 1,
- 'priority_queue': 1,
- 'set': 1,
- 'multiset': 1,
- 'unordered_set': 1,
- 'unordered_multiset': 1,
- 'stack': 1,
- 'queue': 1,
- 'deque': 1,
- 'array': 1
- }
- def find_sub_string_count(s, start, end, substr):
- count = 0
- pos = s.find(substr, start, end)
- if pos != -1:
- next_count = find_sub_string_count(s, pos + 1, end, substr)
- count = next_count + 1
- return count
- def split_container_name(name):
- name = name.strip()
- left = name.find('<')
- right = -1
- if left != -1:
- right = name.rfind('>')
- if left == -1 or right == -1:
- return [name]
- first = name[:left]
- results = [first]
- comma = name.find(',', left + 1, right)
- if comma == -1:
- results.append(name[left+1:right].strip())
- return results
- left += 1
- while comma != -1:
- lt_count = find_sub_string_count(name, left, comma, '<')
- gt_count = find_sub_string_count(name, left, comma, '>')
- if lt_count == gt_count:
- results.append(name[left:comma].strip())
- left = comma + 1
- comma = name.find(',', comma + 1, right)
- if left < right:
- results.append(name[left:right].strip())
- name_len = len(name)
- if right < name_len - 1:
- results.append(name[right+1:].strip())
- return results
- def normalize_type_name_by_sections(sections):
- container_name = sections[0]
- suffix = ''
- index = len(sections) - 1
- while sections[index] == '*' or sections[index] == '&':
- suffix += sections[index]
- index -= 1
- name_for_search = container_name.replace('const ', '').replace('&', '').replace('*', '').strip()
- if name_for_search in stl_type_map:
- normalized_name = container_name + '<' + ', '.join(sections[1:1+stl_type_map[name_for_search]]) + '>' + suffix
- else:
- normalized_name = container_name + '<' + ', '.join(sections[1:]) + '>'
- return normalized_name
- def normalize_std_function_by_sections(sections):
- normalized_name = ''
- if sections[0] == 'std_function_args':
- normalized_name = '(' + ', '.join(sections[1:]) + ')'
- elif sections[0] == 'std::function' or sections[0] == 'function':
- normalized_name = 'std::function<' + sections[1] + ' ' + sections[2] + '>'
- else:
- assert(False)
- return normalized_name
- def normalize_type_str(s, depth=1):
- if s.find('std::function') == 0 or s.find('function') == 0:
- start = s.find('<')
- assert(start > 0)
- sections = [s[:start]] # std::function
- start += 1
- ret_pos = s.find('(', start)
- sections.append(s[start:ret_pos].strip()) # return type
- end = s.find(')', ret_pos + 1)
- sections.append('std_function_args<' + s[ret_pos+1:end].strip() + '>')
- else:
- sections = split_container_name(s)
- section_len = len(sections)
- if section_len == 1:
- return sections[0]
- # for section in sections:
- # print('>' * depth + section)
- if sections[0] == 'const std::basic_string' or sections[0] == 'const basic_string':
- last_section = sections[len(sections) - 1]
- if last_section == '&' or last_section == '*' or last_section.startswith('::'):
- return 'const std::string' + last_section
- else:
- return 'const std::string'
- elif sections[0] == 'std::basic_string' or sections[0] == 'basic_string':
- last_section = sections[len(sections) - 1]
- if last_section == '&' or last_section == '*' or last_section.startswith('::'):
- return 'std::string' + last_section
- else:
- return 'std::string'
- for i in range(1, section_len):
- sections[i] = normalize_type_str(sections[i], depth+1)
- if sections[0] == 'std::function' or sections[0] == 'function' or sections[0] == 'std_function_args':
- normalized_name = normalize_std_function_by_sections(sections)
- else:
- normalized_name = normalize_type_name_by_sections(sections)
- return normalized_name
- class BaseEnumeration(object):
- """
- Common base class for named enumerations held in sync with Index.h values.
- Subclasses must define their own _kinds and _name_map members, as:
- _kinds = []
- _name_map = None
- These values hold the per-subclass instances and value-to-name mappings,
- respectively.
- """
- def __init__(self, value):
- if value >= len(self.__class__._kinds):
- self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1)
- if self.__class__._kinds[value] is not None:
- raise ValueError('{0} value {1} already loaded'.format(
- str(self.__class__), value))
- self.value = value
- self.__class__._kinds[value] = self
- self.__class__._name_map = None
- def from_param(self):
- return self.value
- @property
- def name(self):
- """Get the enumeration name of this cursor kind."""
- if self._name_map is None:
- self._name_map = {}
- for key, value in self.__class__.__dict__.items():
- if isinstance(value, self.__class__):
- self._name_map[value] = key
- return self._name_map[self]
- @classmethod
- def from_id(cls, id):
- if id >= len(cls._kinds) or cls._kinds[id] is None:
- raise ValueError('Unknown template argument kind %d' % id)
- return cls._kinds[id]
- def __repr__(self):
- return '%s.%s' % (self.__class__, self.name,)
- ### Availability Kinds ###
- class AvailabilityKind(BaseEnumeration):
- """
- Describes the availability of an entity.
- """
- # The unique kind objects, indexed by id.
- _kinds = []
- _name_map = None
- def __repr__(self):
- return 'AvailabilityKind.%s' % (self.name,)
- AvailabilityKind.AVAILABLE = AvailabilityKind(0)
- AvailabilityKind.DEPRECATED = AvailabilityKind(1)
- AvailabilityKind.NOT_AVAILABLE = AvailabilityKind(2)
- AvailabilityKind.NOT_ACCESSIBLE = AvailabilityKind(3)
- def get_availability(cursor):
- """
- Retrieves the availability of the entity pointed at by the cursor.
- """
- if not hasattr(cursor, '_availability'):
- cursor._availability = cindex.conf.lib.clang_getCursorAvailability(cursor)
- return AvailabilityKind.from_id(cursor._availability)
- def native_name_from_type(ntype, underlying=False):
- kind = ntype.kind #get_canonical().kind
- const = "" #"const " if ntype.is_const_qualified() else ""
- if not underlying and kind == cindex.TypeKind.ENUM:
- decl = ntype.get_declaration()
- return get_namespaced_name(decl)
- elif kind in type_map:
- return const + type_map[kind]
- elif kind == cindex.TypeKind.RECORD:
- # might be an std::string
- decl = ntype.get_declaration()
- parent = decl.semantic_parent
- cdecl = ntype.get_canonical().get_declaration()
- cparent = cdecl.semantic_parent
- if decl.spelling == "string" and parent and parent.spelling == "std":
- return "std::string"
- elif cdecl.spelling == "function" and cparent and cparent.spelling == "std":
- return "std::function"
- else:
- # print >> sys.stderr, "probably a function pointer: " + str(decl.spelling)
- return const + decl.spelling
- else:
- # name = ntype.get_declaration().spelling
- # print >> sys.stderr, "Unknown type: " + str(kind) + " " + str(name)
- return INVALID_NATIVE_TYPE
- # pdb.set_trace()
- def build_namespace(cursor, namespaces=[]):
- '''
- build the full namespace for a specific cursor
- '''
- if cursor:
- parent = cursor.semantic_parent
- if parent:
- if parent.kind == cindex.CursorKind.NAMESPACE or parent.kind == cindex.CursorKind.CLASS_DECL:
- namespaces.append(parent.displayname)
- build_namespace(parent, namespaces)
- return namespaces
- def get_namespaced_name(declaration_cursor):
- ns_list = build_namespace(declaration_cursor, [])
- ns_list.reverse()
- ns = "::".join(ns_list)
- display_name = declaration_cursor.displayname.replace("::__ndk1", "")
- if len(ns) > 0:
- ns = ns.replace("::__ndk1", "")
- return ns + "::" + display_name
- return display_name
- def generate_namespace_list(cursor, namespaces=[]):
- '''
- build the full namespace for a specific cursor
- '''
- if cursor:
- parent = cursor.semantic_parent
- if parent:
- if parent.kind == cindex.CursorKind.NAMESPACE or parent.kind == cindex.CursorKind.CLASS_DECL:
- if parent.kind == cindex.CursorKind.NAMESPACE:
- namespaces.append(parent.displayname)
- generate_namespace_list(parent, namespaces)
- return namespaces
- def get_namespace_name(declaration_cursor):
- ns_list = generate_namespace_list(declaration_cursor, [])
- ns_list.reverse()
- ns = "::".join(ns_list)
- if len(ns) > 0:
- ns = ns.replace("::__ndk1", "")
- return ns + "::"
- return declaration_cursor.displayname
- class NativeType(object):
- def __init__(self):
- self.is_object = False
- self.is_function = False
- self.is_enum = False
- self.is_numeric = False
- self.not_supported = False
- self.param_types = []
- self.ret_type = None
- self.namespaced_name = "" # with namespace and class name
- self.namespace_name = "" # only contains namespace
- self.name = ""
- self.whole_name = None
- self.is_const = False
- self.is_pointer = False
- self.canonical_type = None
- @staticmethod
- def from_type(ntype):
- if ntype.kind == cindex.TypeKind.POINTER:
- nt = NativeType.from_type(ntype.get_pointee())
- if None != nt.canonical_type:
- nt.canonical_type.name += "*"
- nt.canonical_type.namespaced_name += "*"
- nt.canonical_type.whole_name += "*"
- nt.name += "*"
- nt.namespaced_name += "*"
- nt.whole_name = nt.namespaced_name
- nt.is_enum = False
- nt.is_const = ntype.get_pointee().is_const_qualified()
- nt.is_pointer = True
- if nt.is_const:
- nt.whole_name = "const " + nt.whole_name
- elif ntype.kind == cindex.TypeKind.LVALUEREFERENCE:
- nt = NativeType.from_type(ntype.get_pointee())
- nt.is_const = ntype.get_pointee().is_const_qualified()
- nt.whole_name = nt.namespaced_name + "&"
- if nt.is_const:
- nt.whole_name = "const " + nt.whole_name
- if None != nt.canonical_type:
- nt.canonical_type.whole_name += "&"
- else:
- nt = NativeType()
- decl = ntype.get_declaration()
- nt.namespaced_name = get_namespaced_name(decl).replace('::__ndk1', '')
- if decl.kind == cindex.CursorKind.CLASS_DECL \
- and not nt.namespaced_name.startswith('std::function') \
- and not nt.namespaced_name.startswith('std::string') \
- and not nt.namespaced_name.startswith('std::basic_string'):
- nt.is_object = True
- displayname = decl.displayname.replace('::__ndk1', '')
- nt.name = normalize_type_str(displayname)
- nt.namespaced_name = normalize_type_str(nt.namespaced_name)
- nt.namespace_name = get_namespace_name(decl)
- nt.whole_name = nt.namespaced_name
- else:
- if decl.kind == cindex.CursorKind.NO_DECL_FOUND:
- nt.name = native_name_from_type(ntype)
- else:
- nt.name = decl.spelling
- nt.namespace_name = get_namespace_name(decl)
- if len(nt.namespaced_name) > 0:
- nt.namespaced_name = normalize_type_str(nt.namespaced_name)
- if nt.namespaced_name.startswith("std::function"):
- nt.name = "std::function"
- if len(nt.namespaced_name) == 0 or nt.namespaced_name.find("::") == -1:
- nt.namespaced_name = nt.name
- nt.whole_name = nt.namespaced_name
- nt.is_const = ntype.is_const_qualified()
- if nt.is_const:
- nt.whole_name = "const " + nt.whole_name
- # Check whether it's a std::function typedef
- cdecl = ntype.get_canonical().get_declaration()
- if None != cdecl.spelling and 0 == cmp(cdecl.spelling, "function"):
- nt.name = "std::function"
- if nt.name != INVALID_NATIVE_TYPE and nt.name != "std::string" and nt.name != "std::function":
- if ntype.kind == cindex.TypeKind.UNEXPOSED or ntype.kind == cindex.TypeKind.TYPEDEF or ntype.kind == cindex.TypeKind.ELABORATED:
- ret = NativeType.from_type(ntype.get_canonical())
- if ret.name != "":
- if decl.kind == cindex.CursorKind.TYPEDEF_DECL:
- ret.canonical_type = nt
- return ret
- nt.is_enum = ntype.get_canonical().kind == cindex.TypeKind.ENUM
- if nt.name == "std::function":
- nt.is_object = False
- lambda_display_name = get_namespaced_name(cdecl)
- lambda_display_name = lambda_display_name.replace("::__ndk1", "")
- lambda_display_name = normalize_type_str(lambda_display_name)
- nt.namespaced_name = lambda_display_name
- r = re.compile('function<([^\s]+).*\((.*)\)>').search(nt.namespaced_name)
- (ret_type, params) = r.groups()
- params = filter(None, params.split(", "))
- nt.is_function = True
- nt.ret_type = NativeType.from_string(ret_type)
- nt.param_types = [NativeType.from_string(string) for string in params]
- # mark argument as not supported
- if nt.name == INVALID_NATIVE_TYPE:
- nt.not_supported = True
- if re.search("(short|int|double|float|long|size_t)$", nt.name) is not None:
- nt.is_numeric = True
- return nt
- @staticmethod
- def from_string(displayname):
- displayname = displayname.replace(" *", "*")
- nt = NativeType()
- nt.name = displayname.split("::")[-1]
- nt.namespaced_name = displayname
- nt.whole_name = nt.namespaced_name
- nt.is_object = True
- return nt
- @property
- def lambda_parameters(self):
- params = ["%s larg%d" % (str(nt), i) for i, nt in enumerate(self.param_types)]
- return ", ".join(params)
- @staticmethod
- def dict_has_key_re(dict, real_key_list):
- for real_key in real_key_list:
- for (k, v) in dict.items():
- if k.startswith('@'):
- k = k[1:]
- match = re.match("^" + k + "$", real_key)
- if match:
- return True
- else:
- if k == real_key:
- return True
- return False
- @staticmethod
- def dict_get_value_re(dict, real_key_list):
- for real_key in real_key_list:
- for (k, v) in dict.items():
- if k.startswith('@'):
- k = k[1:]
- match = re.match("^" + k + "$", real_key)
- if match:
- return v
- else:
- if k == real_key:
- return v
- return None
- @staticmethod
- def dict_replace_value_re(dict, real_key_list):
- for real_key in real_key_list:
- for (k, v) in dict.items():
- if k.startswith('@'):
- k = k[1:]
- match = re.match('.*' + k, real_key)
- if match:
- return re.sub(k, v, real_key)
- else:
- if k == real_key:
- return v
- return None
- def from_native(self, convert_opts):
- assert(convert_opts.has_key('generator'))
- generator = convert_opts['generator']
- keys = []
- if self.canonical_type != None:
- keys.append(self.canonical_type.name)
- keys.append(self.name)
- from_native_dict = generator.config['conversions']['from_native']
- if self.is_object:
- if not NativeType.dict_has_key_re(from_native_dict, keys):
- keys.append("object")
- elif self.is_enum:
- keys.append("int")
- if NativeType.dict_has_key_re(from_native_dict, keys):
- tpl = NativeType.dict_get_value_re(from_native_dict, keys)
- tpl = Template(tpl, searchList=[convert_opts])
- return str(tpl).rstrip()
- return "#pragma warning NO CONVERSION FROM NATIVE FOR " + self.name
- def to_native(self, convert_opts):
- assert('generator' in convert_opts)
- generator = convert_opts['generator']
- keys = []
- if self.canonical_type != None:
- keys.append(self.canonical_type.name)
- keys.append(self.name)
- to_native_dict = generator.config['conversions']['to_native']
- if self.is_object:
- if not NativeType.dict_has_key_re(to_native_dict, keys):
- keys.append("object")
- elif self.is_enum:
- keys.append("int")
- if self.is_function:
- tpl = Template(file=os.path.join(generator.target, "templates", "lambda.c"),
- searchList=[convert_opts, self])
- indent = convert_opts['level'] * "\t"
- return str(tpl).replace("\n", "\n" + indent)
- if NativeType.dict_has_key_re(to_native_dict, keys):
- tpl = NativeType.dict_get_value_re(to_native_dict, keys)
- tpl = Template(tpl, searchList=[convert_opts])
- return str(tpl).rstrip()
- return "#pragma warning NO CONVERSION TO NATIVE FOR " + self.name + "\n" + convert_opts['level'] * "\t" + "ok = false"
- def to_string(self, generator):
- conversions = generator.config['conversions']
- if conversions.has_key('native_types'):
- native_types_dict = conversions['native_types']
- if NativeType.dict_has_key_re(native_types_dict, [self.namespaced_name]):
- return NativeType.dict_get_value_re(native_types_dict, [self.namespaced_name])
- name = self.namespaced_name
- to_native_dict = generator.config['conversions']['to_native']
- from_native_dict = generator.config['conversions']['from_native']
- use_typedef = False
- typedef_name = self.canonical_type.name if None != self.canonical_type else None
- if None != typedef_name:
- if NativeType.dict_has_key_re(to_native_dict, [typedef_name]) or NativeType.dict_has_key_re(from_native_dict, [typedef_name]):
- use_typedef = True
- if use_typedef and self.canonical_type:
- name = self.canonical_type.namespaced_name
- return "const " + name if (self.is_pointer and self.is_const) else name
- def get_whole_name(self, generator):
- conversions = generator.config['conversions']
- to_native_dict = conversions['to_native']
- from_native_dict = conversions['from_native']
- use_typedef = False
- name = self.whole_name
- typedef_name = self.canonical_type.name if None != self.canonical_type else None
- if None != typedef_name:
- if NativeType.dict_has_key_re(to_native_dict, [typedef_name]) or NativeType.dict_has_key_re(from_native_dict, [typedef_name]):
- use_typedef = True
- if use_typedef and self.canonical_type:
- name = self.canonical_type.whole_name
- to_replace = None
- if conversions.has_key('native_types'):
- native_types_dict = conversions['native_types']
- to_replace = NativeType.dict_replace_value_re(native_types_dict, [name])
- if to_replace:
- name = to_replace
- return name
- def object_can_convert(self, generator, is_to_native = True):
- if self.is_object:
- keys = []
- if self.canonical_type != None:
- keys.append(self.canonical_type.name)
- keys.append(self.name)
- if is_to_native:
- to_native_dict = generator.config['conversions']['to_native']
- if NativeType.dict_has_key_re(to_native_dict, keys):
- return True
- else:
- from_native_dict = generator.config['conversions']['from_native']
- if NativeType.dict_has_key_re(from_native_dict, keys):
- return True
- return False
- def __str__(self):
- return self.canonical_type.whole_name if None != self.canonical_type else self.whole_name
- class NativeField(object):
- def __init__(self, cursor):
- cursor = cursor.canonical
- self.cursor = cursor
- self.name = cursor.displayname
- self.kind = cursor.type.kind
- self.location = cursor.location
- member_field_re = re.compile('m_(\w+)')
- match = member_field_re.match(self.name)
- self.signature_name = self.name
- self.ntype = NativeType.from_type(cursor.type)
- if match:
- self.pretty_name = match.group(1)
- else:
- self.pretty_name = self.name
- @staticmethod
- def can_parse(ntype):
- native_type = NativeType.from_type(ntype)
- if ntype.kind == cindex.TypeKind.UNEXPOSED and native_type.name != "std::string":
- return False
- return True
- def generate_code(self, current_class = None, generator = None):
- gen = current_class.generator if current_class else generator
- config = gen.config
- if config['definitions'].has_key('public_field'):
- tpl = Template(config['definitions']['public_field'],
- searchList=[current_class, self])
- self.signature_name = str(tpl)
- tpl = Template(file=os.path.join(gen.target, "templates", "public_field.c"),
- searchList=[current_class, self])
- gen.impl_file.write(str(tpl))
- # return True if found default argument.
- def iterate_param_node(param_node, depth=1):
- for node in param_node.get_children():
- # print(">"*depth+" "+str(node.kind))
- if node.kind in default_arg_type_arr:
- return True
- if iterate_param_node(node, depth + 1):
- return True
- return False
- class NativeFunction(object):
- def __init__(self, cursor):
- self.cursor = cursor
- self.func_name = cursor.spelling
- self.signature_name = self.func_name
- self.arguments = []
- self.argumtntTips = []
- self.static = cursor.kind == cindex.CursorKind.CXX_METHOD and cursor.is_static_method()
- self.implementations = []
- self.is_overloaded = False
- self.is_constructor = False
- self.not_supported = False
- self.is_override = False
- self.ret_type = NativeType.from_type(cursor.result_type)
- self.comment = self.get_comment(cursor.raw_comment)
- # parse the arguments
- # if self.func_name == "spriteWithFile":
- # pdb.set_trace()
- for arg in cursor.get_arguments():
- self.argumtntTips.append(arg.spelling)
- for arg in cursor.type.argument_types():
- nt = NativeType.from_type(arg)
- self.arguments.append(nt)
- # mark the function as not supported if at least one argument is not supported
- if nt.not_supported:
- self.not_supported = True
- found_default_arg = False
- index = -1
- for arg_node in self.cursor.get_children():
- if arg_node.kind == cindex.CursorKind.CXX_OVERRIDE_ATTR:
- self.is_override = True
- if arg_node.kind == cindex.CursorKind.PARM_DECL:
- index += 1
- if iterate_param_node(arg_node):
- found_default_arg = True
- break
- self.min_args = index if found_default_arg else len(self.arguments)
- def get_comment(self, comment):
- replaceStr = comment
- if comment is None:
- return ""
- regular_replace_list = [
- ("(\s)*//!",""),
- ("(\s)*//",""),
- ("(\s)*/\*\*",""),
- ("(\s)*/\*",""),
- ("\*/",""),
- ("\r\n", "\n"),
- ("\n(\s)*\*", "\n"),
- ("\n(\s)*@","\n"),
- ("\n(\s)*","\n"),
- ("\n(\s)*\n", "\n"),
- ("^(\s)*\n",""),
- ("\n(\s)*$", ""),
- ("\n","<br>\n"),
- ("\n", "\n-- ")
- ]
- for item in regular_replace_list:
- replaceStr = re.sub(item[0], item[1], replaceStr)
- return replaceStr
- def generate_code(self, current_class=None, generator=None, is_override=False, is_ctor=False):
- self.is_ctor = is_ctor
- gen = current_class.generator if current_class else generator
- config = gen.config
- if not is_ctor:
- tpl = Template(file=os.path.join(gen.target, "templates", "function.h"),
- searchList=[current_class, self])
- if not is_override:
- gen.head_file.write(str(tpl))
- if self.static:
- if config['definitions'].has_key('sfunction'):
- tpl = Template(config['definitions']['sfunction'],
- searchList=[current_class, self])
- self.signature_name = str(tpl)
- tpl = Template(file=os.path.join(gen.target, "templates", "sfunction.c"),
- searchList=[current_class, self])
- else:
- if not self.is_constructor:
- if config['definitions'].has_key('ifunction'):
- tpl = Template(config['definitions']['ifunction'],
- searchList=[current_class, self])
- self.signature_name = str(tpl)
- else:
- if config['definitions'].has_key('constructor'):
- if not is_ctor:
- tpl = Template(config['definitions']['constructor'],
- searchList=[current_class, self])
- else:
- tpl = Template(config['definitions']['ctor'],
- searchList=[current_class, self])
- self.signature_name = str(tpl)
- if self.is_constructor and gen.script_type == "spidermonkey" :
- if not is_ctor:
- tpl = Template(file=os.path.join(gen.target, "templates", "constructor.c"),
- searchList=[current_class, self])
- else:
- tpl = Template(file=os.path.join(gen.target, "templates", "ctor.c"),
- searchList=[current_class, self])
- else :
- tpl = Template(file=os.path.join(gen.target, "templates", "ifunction.c"),
- searchList=[current_class, self])
- if not is_override:
- gen.impl_file.write(str(tpl))
- if not is_ctor:
- apidoc_function_script = Template(file=os.path.join(gen.target,
- "templates",
- "apidoc_function.script"),
- searchList=[current_class, self])
- if gen.script_type == "spidermonkey":
- gen.doc_file.write(str(apidoc_function_script))
- else:
- if gen.script_type == "lua" and current_class != None :
- current_class.doc_func_file.write(str(apidoc_function_script))
- class NativeOverloadedFunction(object):
- def __init__(self, func_array):
- self.implementations = func_array
- self.func_name = func_array[0].func_name
- self.signature_name = self.func_name
- self.min_args = 100
- self.is_constructor = False
- self.is_overloaded = True
- self.is_ctor = False
- for m in func_array:
- self.min_args = min(self.min_args, m.min_args)
- self.comment = self.get_comment(func_array[0].cursor.raw_comment)
- def get_comment(self, comment):
- replaceStr = comment
- if comment is None:
- return ""
- regular_replace_list = [
- ("(\s)*//!",""),
- ("(\s)*//",""),
- ("(\s)*/\*\*",""),
- ("(\s)*/\*",""),
- ("\*/",""),
- ("\r\n", "\n"),
- ("\n(\s)*\*", "\n"),
- ("\n(\s)*@","\n"),
- ("\n(\s)*","\n"),
- ("\n(\s)*\n", "\n"),
- ("^(\s)*\n",""),
- ("\n(\s)*$", ""),
- ("\n","<br>\n"),
- ("\n", "\n-- ")
- ]
- for item in regular_replace_list:
- replaceStr = re.sub(item[0], item[1], replaceStr)
- return replaceStr
- def append(self, func):
- self.min_args = min(self.min_args, func.min_args)
- self.implementations.append(func)
- def generate_code(self, current_class=None, is_override=False, is_ctor=False):
- self.is_ctor = is_ctor
- gen = current_class.generator
- config = gen.config
- static = self.implementations[0].static
- if not is_ctor:
- tpl = Template(file=os.path.join(gen.target, "templates", "function.h"),
- searchList=[current_class, self])
- if not is_override:
- gen.head_file.write(str(tpl))
- if static:
- if config['definitions'].has_key('sfunction'):
- tpl = Template(config['definitions']['sfunction'],
- searchList=[current_class, self])
- self.signature_name = str(tpl)
- tpl = Template(file=os.path.join(gen.target, "templates", "sfunction_overloaded.c"),
- searchList=[current_class, self])
- else:
- if not self.is_constructor:
- if config['definitions'].has_key('ifunction'):
- tpl = Template(config['definitions']['ifunction'],
- searchList=[current_class, self])
- self.signature_name = str(tpl)
- else:
- if config['definitions'].has_key('constructor'):
- if not is_ctor:
- tpl = Template(config['definitions']['constructor'],
- searchList=[current_class, self])
- else:
- tpl = Template(config['definitions']['ctor'],
- searchList=[current_class, self])
- self.signature_name = str(tpl)
- tpl = Template(file=os.path.join(gen.target, "templates", "ifunction_overloaded.c"),
- searchList=[current_class, self])
- if not is_override:
- gen.impl_file.write(str(tpl))
- if current_class != None and not is_ctor:
- if gen.script_type == "lua":
- apidoc_function_overload_script = Template(file=os.path.join(gen.target,
- "templates",
- "apidoc_function_overload.script"),
- searchList=[current_class, self])
- current_class.doc_func_file.write(str(apidoc_function_overload_script))
- else:
- if gen.script_type == "spidermonkey":
- apidoc_function_overload_script = Template(file=os.path.join(gen.target,
- "templates",
- "apidoc_function_overload.script"),
- searchList=[current_class, self])
- gen.doc_file.write(str(apidoc_function_overload_script))
- class NativeClass(object):
- def __init__(self, cursor, generator):
- # the cursor to the implementation
- self.cursor = cursor
- self.class_name = cursor.displayname
- self.is_ref_class = self.class_name == "Ref"
- self.namespaced_class_name = self.class_name
- self.parents = []
- self.fields = []
- self.public_fields = []
- self.methods = {}
- self.static_methods = {}
- self.generator = generator
- self.is_abstract = self.class_name in generator.abstract_classes
- self._current_visibility = cindex.AccessSpecifier.PRIVATE
- #for generate lua api doc
- self.override_methods = {}
- self.has_constructor = False
- self.namespace_name = ""
- registration_name = generator.get_class_or_rename_class(self.class_name)
- if generator.remove_prefix:
- self.target_class_name = re.sub('^' + generator.remove_prefix, '', registration_name)
- else:
- self.target_class_name = registration_name
- self.namespaced_class_name = get_namespaced_name(cursor)
- self.namespace_name = get_namespace_name(cursor)
- self.parse()
- @property
- def underlined_class_name(self):
- return self.namespaced_class_name.replace("::", "_")
- def parse(self):
- '''
- parse the current cursor, getting all the necesary information
- '''
- self._deep_iterate(self.cursor)
- def methods_clean(self):
- '''
- clean list of methods (without the ones that should be skipped)
- '''
- ret = []
- for name, impl in self.methods.iteritems():
- should_skip = False
- if name == 'constructor':
- should_skip = True
- else:
- if self.generator.should_skip(self.class_name, name):
- should_skip = True
- if not should_skip:
- ret.append({"name": name, "impl": impl})
- return ret
- def static_methods_clean(self):
- '''
- clean list of static methods (without the ones that should be skipped)
- '''
- ret = []
- for name, impl in self.static_methods.iteritems():
- should_skip = self.generator.should_skip(self.class_name, name)
- if not should_skip:
- ret.append({"name": name, "impl": impl})
- return ret
- def override_methods_clean(self):
- '''
- clean list of override methods (without the ones that should be skipped)
- '''
- ret = []
- for name, impl in self.override_methods.iteritems():
- should_skip = self.generator.should_skip(self.class_name, name)
- if not should_skip:
- ret.append({"name": name, "impl": impl})
- return ret
- def generate_code(self):
- '''
- actually generate the code. it uses the current target templates/rules in order to
- generate the right code
- '''
- if not self.is_ref_class:
- self.is_ref_class = self._is_ref_class()
- config = self.generator.config
- prelude_h = Template(file=os.path.join(self.generator.target, "templates", "prelude.h"),
- searchList=[{"current_class": self}])
- prelude_c = Template(file=os.path.join(self.generator.target, "templates", "prelude.c"),
- searchList=[{"current_class": self}])
- apidoc_classhead_script = Template(file=os.path.join(self.generator.target,
- "templates",
- "apidoc_classhead.script"),
- searchList=[{"current_class": self}])
- if self.generator.script_type == "lua":
- docfuncfilepath = os.path.join(self.generator.outdir + "/api", self.class_name + ".lua")
- self.doc_func_file = open(docfuncfilepath, "w+")
- apidoc_fun_head_script = Template(file=os.path.join(self.generator.target,
- "templates",
- "apidoc_function_head.script"),
- searchList=[{"current_class": self}])
- self.doc_func_file.write(str(apidoc_fun_head_script))
- self.generator.head_file.write(str(prelude_h))
- self.generator.impl_file.write(str(prelude_c))
- self.generator.doc_file.write(str(apidoc_classhead_script))
- for m in self.methods_clean():
- m['impl'].generate_code(self)
- for m in self.static_methods_clean():
- m['impl'].generate_code(self)
- if self.generator.script_type == "lua":
- for m in self.override_methods_clean():
- m['impl'].generate_code(self, is_override = True)
- for m in self.public_fields:
- if self.generator.should_bind_field(self.class_name, m.name):
- m.generate_code(self)
- # generate register section
- register = Template(file=os.path.join(self.generator.target, "templates", "register.c"),
- searchList=[{"current_class": self}])
- apidoc_classfoot_script = Template(file=os.path.join(self.generator.target,
- "templates",
- "apidoc_classfoot.script"),
- searchList=[{"current_class": self}])
- self.generator.impl_file.write(str(register))
- self.generator.doc_file.write(str(apidoc_classfoot_script))
- if self.generator.script_type == "lua":
- apidoc_fun_foot_script = Template(file=os.path.join(self.generator.target,
- "templates",
- "apidoc_function_foot.script"),
- searchList=[{"current_class": self}])
- self.doc_func_file.write(str(apidoc_fun_foot_script))
- self.doc_func_file.close()
- def _deep_iterate(self, cursor=None, depth=0):
- for node in cursor.get_children():
- # print("%s%s - %s" % ("> " * depth, node.displayname, node.kind))
- if self._process_node(node):
- self._deep_iterate(node, depth + 1)
- @staticmethod
- def _is_method_in_parents(current_class, method_name):
- if len(current_class.parents) > 0:
- if method_name in current_class.parents[0].methods:
- return True
- return NativeClass._is_method_in_parents(current_class.parents[0], method_name)
- return False
- def _is_ref_class(self, depth = 0):
- """
- Mark the class as 'cocos2d::Ref' or its subclass.
- """
- # print ">" * (depth + 1) + " " + self.class_name
- for parent in self.parents:
- if parent._is_ref_class(depth + 1):
- return True
- if self.is_ref_class:
- return True
- return False
- def _process_node(self, cursor):
- '''
- process the node, depending on the type. If returns true, then it will perform a deep
- iteration on its children. Otherwise it will continue with its siblings (if any)
- @param: cursor the cursor to analyze
- '''
- if cursor.kind == cindex.CursorKind.CXX_BASE_SPECIFIER:
- parent = cursor.get_definition()
- parent_name = parent.displayname
- if not self.class_name in self.generator.classes_have_no_parents:
- if parent_name and parent_name not in self.generator.base_classes_to_skip:
- #if parent and self.generator.in_listed_classes(parent.displayname):
- if not self.generator.generated_classes.has_key(parent.displayname):
- parent = NativeClass(parent, self.generator)
- self.generator.generated_classes[parent.class_name] = parent
- else:
- parent = self.generator.generated_classes[parent.displayname]
- self.parents.append(parent)
- if parent_name == "Ref":
- self.is_ref_class = True
- elif cursor.kind == cindex.CursorKind.FIELD_DECL:
- self.fields.append(NativeField(cursor))
- if self._current_visibility == cindex.AccessSpecifier.PUBLIC and NativeField.can_parse(cursor.type):
- self.public_fields.append(NativeField(cursor))
- elif cursor.kind == cindex.CursorKind.CXX_ACCESS_SPEC_DECL:
- self._current_visibility = cursor.access_specifier
- elif cursor.kind == cindex.CursorKind.CXX_METHOD and get_availability(cursor) != AvailabilityKind.DEPRECATED:
- # skip if variadic
- if self._current_visibility == cindex.AccessSpecifier.PUBLIC and not cursor.type.is_function_variadic():
- m = NativeFunction(cursor)
- registration_name = self.generator.should_rename_function(self.class_name, m.func_name) or m.func_name
- # bail if the function is not supported (at least one arg not supported)
- if m.not_supported:
- return False
- if m.is_override:
- if NativeClass._is_method_in_parents(self, registration_name):
- if self.generator.script_type == "lua":
- if not self.override_methods.has_key(registration_name):
- self.override_methods[registration_name] = m
- else:
- previous_m = self.override_methods[registration_name]
- if isinstance(previous_m, NativeOverloadedFunction):
- previous_m.append(m)
- else:
- self.override_methods[registration_name] = NativeOverloadedFunction([m, previous_m])
- return False
- if m.static:
- if not self.static_methods.has_key(registration_name):
- self.static_methods[registration_name] = m
- else:
- previous_m = self.static_methods[registration_name]
- if isinstance(previous_m, NativeOverloadedFunction):
- previous_m.append(m)
- else:
- self.static_methods[registration_name] = NativeOverloadedFunction([m, previous_m])
- else:
- if not self.methods.has_key(registration_name):
- self.methods[registration_name] = m
- else:
- previous_m = self.methods[registration_name]
- if isinstance(previous_m, NativeOverloadedFunction):
- previous_m.append(m)
- else:
- self.methods[registration_name] = NativeOverloadedFunction([m, previous_m])
- return True
- elif self._current_visibility == cindex.AccessSpecifier.PUBLIC and cursor.kind == cindex.CursorKind.CONSTRUCTOR and not self.is_abstract:
- # Skip copy constructor
- if cursor.displayname == self.class_name + "(const " + self.namespaced_class_name + " &)":
- # print "Skip copy constructor: " + cursor.displayname
- return True
- m = NativeFunction(cursor)
- m.is_constructor = True
- self.has_constructor = True
- if not self.methods.has_key('constructor'):
- self.methods['constructor'] = m
- else:
- previous_m = self.methods['constructor']
- if isinstance(previous_m, NativeOverloadedFunction):
- previous_m.append(m)
- else:
- m = NativeOverloadedFunction([m, previous_m])
- m.is_constructor = True
- self.methods['constructor'] = m
- return True
- # else:
- # print >> sys.stderr, "unknown cursor: %s - %s" % (cursor.kind, cursor.displayname)
- return False
- class Generator(object):
- def __init__(self, opts):
- self.index = cindex.Index.create()
- self.outdir = opts['outdir']
- self.search_path = opts['search_path']
- self.prefix = opts['prefix']
- self.headers = opts['headers'].split(' ')
- self.classes = opts['classes']
- self.classes_need_extend = opts['classes_need_extend']
- self.classes_have_no_parents = opts['classes_have_no_parents'].split(' ')
- self.base_classes_to_skip = opts['base_classes_to_skip'].split(' ')
- self.abstract_classes = opts['abstract_classes'].split(' ')
- self.clang_args = opts['clang_args']
- self.target = opts['target']
- self.remove_prefix = opts['remove_prefix']
- self.target_ns = opts['target_ns']
- self.cpp_ns = opts['cpp_ns']
- self.impl_file = None
- self.head_file = None
- self.skip_classes = {}
- self.bind_fields = {}
- self.generated_classes = {}
- self.rename_functions = {}
- self.rename_classes = {}
- self.replace_headers = {}
- self.out_file = opts['out_file']
- self.script_control_cpp = opts['script_control_cpp'] == "yes"
- self.script_type = opts['script_type']
- self.macro_judgement = opts['macro_judgement']
- self.hpp_headers = opts['hpp_headers']
- self.cpp_headers = opts['cpp_headers']
- self.win32_clang_flags = opts['win32_clang_flags']
- extend_clang_args = []
- for clang_arg in self.clang_args:
- if not os.path.exists(clang_arg.replace("-I","")):
- pos = clang_arg.find("lib/clang/3.3/include")
- if -1 != pos:
- extend_clang_arg = clang_arg.replace("3.3", "3.4")
- if os.path.exists(extend_clang_arg.replace("-I","")):
- extend_clang_args.append(extend_clang_arg)
- if len(extend_clang_args) > 0:
- self.clang_args.extend(extend_clang_args)
- if sys.platform == 'win32' and self.win32_clang_flags != None:
- self.clang_args.extend(self.win32_clang_flags)
- if opts['skip']:
- list_of_skips = re.split(",\n?", opts['skip'])
- for skip in list_of_skips:
- class_name, methods = skip.split("::")
- self.skip_classes[class_name] = []
- match = re.match("\[([^]]+)\]", methods)
- if match:
- self.skip_classes[class_name] = match.group(1).split(" ")
- else:
- raise Exception("invalid list of skip methods")
- if opts['field']:
- list_of_fields = re.split(",\n?", opts['field'])
- for field in list_of_fields:
- class_name, fields = field.split("::")
- self.bind_fields[class_name] = []
- match = re.match("\[([^]]+)\]", fields)
- if match:
- self.bind_fields[class_name] = match.group(1).split(" ")
- else:
- raise Exception("invalid list of bind fields")
- if opts['rename_functions']:
- list_of_function_renames = re.split(",\n?", opts['rename_functions'])
- for rename in list_of_function_renames:
- class_name, methods = rename.split("::")
- self.rename_functions[class_name] = {}
- match = re.match("\[([^]]+)\]", methods)
- if match:
- list_of_methods = match.group(1).split(" ")
- for pair in list_of_methods:
- k, v = pair.split("=")
- self.rename_functions[class_name][k] = v
- else:
- raise Exception("invalid list of rename methods")
- if opts['rename_classes']:
- list_of_class_renames = re.split(",\n?", opts['rename_classes'])
- for rename in list_of_class_renames:
- class_name, renamed_class_name = rename.split("::")
- self.rename_classes[class_name] = renamed_class_name
- if opts['replace_headers']:
- list_of_replace_headers = re.split(",\n?", opts['replace_headers'])
- for replace in list_of_replace_headers:
- header, replaced_header = replace.split("::")
- self.replace_headers[header] = replaced_header
- def should_rename_function(self, class_name, method_name):
- if self.rename_functions.has_key(class_name) and self.rename_functions[class_name].has_key(method_name):
- # print >> sys.stderr, "will rename %s to %s" % (method_name, self.rename_functions[class_name][method_name])
- return self.rename_functions[class_name][method_name]
- return None
- def get_class_or_rename_class(self, class_name):
- if self.rename_classes.has_key(class_name):
- # print >> sys.stderr, "will rename %s to %s" % (method_name, self.rename_functions[class_name][method_name])
- return self.rename_classes[class_name]
- return class_name
- def should_skip(self, class_name, method_name, verbose=False):
- if class_name == "*" and self.skip_classes.has_key("*"):
- for func in self.skip_classes["*"]:
- if re.match(func, method_name):
- return True
- else:
- for key in self.skip_classes.iterkeys():
- if key == "*" or re.match("^" + key + "$", class_name):
- if verbose:
- print "%s in skip_classes" % (class_name)
- if len(self.skip_classes[key]) == 1 and self.skip_classes[key][0] == "*":
- if verbose:
- print "%s will be skipped completely" % (class_name)
- return True
- if method_name != None:
- for func in self.skip_classes[key]:
- if re.match(func, method_name):
- if verbose:
- print "%s will skip method %s" % (class_name, method_name)
- return True
- if verbose:
- print "%s will be accepted (%s, %s)" % (class_name, key, self.skip_classes[key])
- return False
- def should_bind_field(self, class_name, field_name, verbose=False):
- if class_name == "*" and self.bind_fields.has_key("*"):
- for func in self.bind_fields["*"]:
- if re.match(func, method_name):
- return True
- else:
- for key in self.bind_fields.iterkeys():
- if key == "*" or re.match("^" + key + "$", class_name):
- if verbose:
- print "%s in bind_fields" % (class_name)
- if len(self.bind_fields[key]) == 1 and self.bind_fields[key][0] == "*":
- if verbose:
- print "All public fields of %s will be bound" % (class_name)
- return True
- if field_name != None:
- for field in self.bind_fields[key]:
- if re.match(field, field_name):
- if verbose:
- print "Field %s of %s will be bound" % (field_name, class_name)
- return True
- return False
- def in_listed_classes(self, class_name):
- """
- returns True if the class is in the list of required classes and it's not in the skip list
- """
- for key in self.classes:
- md = re.match("^" + key + "$", class_name)
- if md and not self.should_skip(class_name, None):
- return True
- return False
- def in_listed_extend_classed(self, class_name):
- """
- returns True if the class is in the list of required classes that need to extend
- """
- for key in self.classes_need_extend:
- md = re.match("^" + key + "$", class_name)
- if md:
- return True
- return False
- def sorted_classes(self):
- '''
- sorted classes in order of inheritance
- '''
- sorted_list = []
- for class_name in self.generated_classes.iterkeys():
- nclass = self.generated_classes[class_name]
- sorted_list += self._sorted_parents(nclass)
- # remove dupes from the list
- no_dupes = []
- [no_dupes.append(i) for i in sorted_list if not no_dupes.count(i)]
- return no_dupes
- def _sorted_parents(self, nclass):
- '''
- returns the sorted list of parents for a native class
- '''
- sorted_parents = []
- for p in nclass.parents:
- if p.class_name in self.generated_classes.keys():
- sorted_parents += self._sorted_parents(p)
- if nclass.class_name in self.generated_classes.keys():
- sorted_parents.append(nclass.class_name)
- return sorted_parents
- def generate_code(self):
- # must read the yaml file first
- stream = file(os.path.join(self.target, "conversions.yaml"), "r")
- data = yaml.load(stream)
- self.config = data
- implfilepath = os.path.join(self.outdir, self.out_file + ".cpp")
- headfilepath = os.path.join(self.outdir, self.out_file + ".hpp")
- docfiledir = self.outdir + "/api"
- if not os.path.exists(docfiledir):
- os.makedirs(docfiledir)
- if self.script_type == "lua":
- docfilepath = os.path.join(docfiledir, self.out_file + "_api.lua")
- else:
- docfilepath = os.path.join(docfiledir, self.out_file + "_api.js")
- self.impl_file = open(implfilepath, "w+")
- self.head_file = open(headfilepath, "w+")
- self.doc_file = open(docfilepath, "w+")
- layout_h = Template(file=os.path.join(self.target, "templates", "layout_head.h"),
- searchList=[self])
- layout_c = Template(file=os.path.join(self.target, "templates", "layout_head.c"),
- searchList=[self])
- apidoc_ns_script = Template(file=os.path.join(self.target, "templates", "apidoc_ns.script"),
- searchList=[self])
- self.head_file.write(str(layout_h))
- self.impl_file.write(str(layout_c))
- self.doc_file.write(str(apidoc_ns_script))
- self._parse_headers()
- layout_h = Template(file=os.path.join(self.target, "templates", "layout_foot.h"),
- searchList=[self])
- layout_c = Template(file=os.path.join(self.target, "templates", "layout_foot.c"),
- searchList=[self])
- self.head_file.write(str(layout_h))
- self.impl_file.write(str(layout_c))
- if self.script_type == "lua":
- apidoc_ns_foot_script = Template(file=os.path.join(self.target, "templates", "apidoc_ns_foot.script"),
- searchList=[self])
- self.doc_file.write(str(apidoc_ns_foot_script))
- self.impl_file.close()
- self.head_file.close()
- self.doc_file.close()
- def _pretty_print(self, diagnostics):
- errors=[]
- for idx, d in enumerate(diagnostics):
- if d.severity > 2:
- errors.append(d)
- if len(errors) == 0:
- return
- print("====\nErrors in parsing headers:")
- severities=['Ignored', 'Note', 'Warning', 'Error', 'Fatal']
- for idx, d in enumerate(errors):
- print "%s. <severity = %s,\n location = %r,\n details = %r>" % (
- idx+1, severities[d.severity], d.location, d.spelling)
- print("====\n")
- def _parse_headers(self):
- for header in self.headers:
- tu = self.index.parse(header, self.clang_args)
- if len(tu.diagnostics) > 0:
- self._pretty_print(tu.diagnostics)
- is_fatal = False
- for d in tu.diagnostics:
- if d.severity >= cindex.Diagnostic.Error:
- is_fatal = True
- if is_fatal:
- print("*** Found errors - can not continue")
- raise Exception("Fatal error in parsing headers")
- self._deep_iterate(tu.cursor)
- def _deep_iterate(self, cursor, depth=0):
- def get_children_array_from_iter(iter):
- children = []
- for child in iter:
- children.append(child)
- return children
- # get the canonical type
- if cursor.kind == cindex.CursorKind.CLASS_DECL:
- if cursor == cursor.type.get_declaration() and len(get_children_array_from_iter(cursor.get_children())) > 0:
- is_targeted_class = True
- if self.cpp_ns:
- is_targeted_class = False
- namespaced_name = get_namespaced_name(cursor)
- for ns in self.cpp_ns:
- if namespaced_name.startswith(ns):
- is_targeted_class = True
- break
- if is_targeted_class and self.in_listed_classes(cursor.displayname):
- if not self.generated_classes.has_key(cursor.displayname):
- nclass = NativeClass(cursor, self)
- nclass.generate_code()
- self.generated_classes[cursor.displayname] = nclass
- return
- for node in cursor.get_children():
- # print("%s %s - %s" % (">" * depth, node.displayname, node.kind))
- self._deep_iterate(node, depth + 1)
- def scriptname_from_native(self, namespace_class_name, namespace_name):
- script_ns_dict = self.config['conversions']['ns_map']
- for (k, v) in script_ns_dict.items():
- if k == namespace_name:
- return namespace_class_name.replace("*","").replace("const ", "").replace(k, v)
- if namespace_class_name.find("::") >= 0:
- if namespace_class_name.find("std::") == 0:
- return namespace_class_name
- else:
- raise Exception("The namespace (%s) conversion wasn't set in 'ns_map' section of the conversions.yaml" % namespace_class_name)
- else:
- return namespace_class_name.replace("*","").replace("const ", "")
- def is_cocos_class(self, namespace_class_name):
- script_ns_dict = self.config['conversions']['ns_map']
- for (k, v) in script_ns_dict.items():
- if namespace_class_name.find("std::") == 0:
- return False
- if namespace_class_name.find(k) >= 0:
- return True
- return False
- def scriptname_cocos_class(self, namespace_class_name):
- script_ns_dict = self.config['conversions']['ns_map']
- for (k, v) in script_ns_dict.items():
- if namespace_class_name.find(k) >= 0:
- return namespace_class_name.replace("*","").replace("const ", "").replace(k,v)
- raise Exception("The namespace (%s) conversion wasn't set in 'ns_map' section of the conversions.yaml" % namespace_class_name)
- def js_typename_from_natve(self, namespace_class_name):
- script_ns_dict = self.config['conversions']['ns_map']
- if namespace_class_name.find("std::") == 0:
- if namespace_class_name.find("std::string") == 0:
- return "String"
- if namespace_class_name.find("std::vector") == 0:
- return "Array"
- if namespace_class_name.find("std::map") == 0 or namespace_class_name.find("std::unordered_map") == 0:
- return "map_object"
- if namespace_class_name.find("std::function") == 0:
- return "function"
- for (k, v) in script_ns_dict.items():
- if namespace_class_name.find(k) >= 0:
- if namespace_class_name.find("cocos2d::Vec2") == 0:
- return "vec2_object"
- if namespace_class_name.find("cocos2d::Vec3") == 0:
- return "vec3_object"
- if namespace_class_name.find("cocos2d::Vec4") == 0:
- return "vec4_object"
- if namespace_class_name.find("cocos2d::Mat4") == 0:
- return "mat4_object"
- if namespace_class_name.find("cocos2d::Vector") == 0:
- return "Array"
- if namespace_class_name.find("cocos2d::Map") == 0:
- return "map_object"
- if namespace_class_name.find("cocos2d::Point") == 0:
- return "point_object"
- if namespace_class_name.find("cocos2d::Size") == 0:
- return "size_object"
- if namespace_class_name.find("cocos2d::Rect") == 0:
- return "rect_object"
- if namespace_class_name.find("cocos2d::Color3B") == 0:
- return "color3b_object"
- if namespace_class_name.find("cocos2d::Color4B") == 0:
- return "color4b_object"
- if namespace_class_name.find("cocos2d::Color4F") == 0:
- return "color4f_object"
- else:
- return namespace_class_name.replace("*","").replace("const ", "").replace(k,v)
- return namespace_class_name.replace("*","").replace("const ", "")
- def lua_typename_from_natve(self, namespace_class_name, is_ret = False):
- script_ns_dict = self.config['conversions']['ns_map']
- if namespace_class_name.find("std::") == 0:
- if namespace_class_name.find("std::string") == 0:
- return "string"
- if namespace_class_name.find("std::vector") == 0:
- return "array_table"
- if namespace_class_name.find("std::map") == 0 or namespace_class_name.find("std::unordered_map") == 0:
- return "map_table"
- if namespace_class_name.find("std::function") == 0:
- return "function"
- for (k, v) in script_ns_dict.items():
- if namespace_class_name.find(k) >= 0:
- if namespace_class_name.find("cocos2d::Vec2") == 0:
- return "vec2_table"
- if namespace_class_name.find("cocos2d::Vec3") == 0:
- return "vec3_table"
- if namespace_class_name.find("cocos2d::Vec4") == 0:
- return "vec4_table"
- if namespace_class_name.find("cocos2d::Vector") == 0:
- return "array_table"
- if namespace_class_name.find("cocos2d::Mat4") == 0:
- return "mat4_table"
- if namespace_class_name.find("cocos2d::Map") == 0:
- return "map_table"
- if namespace_class_name.find("cocos2d::Point") == 0:
- return "point_table"
- if namespace_class_name.find("cocos2d::Size") == 0:
- return "size_table"
- if namespace_class_name.find("cocos2d::Rect") == 0:
- return "rect_table"
- if namespace_class_name.find("cocos2d::Color3B") == 0:
- return "color3b_table"
- if namespace_class_name.find("cocos2d::Color4B") == 0:
- return "color4b_table"
- if namespace_class_name.find("cocos2d::Color4F") == 0:
- return "color4f_table"
- if is_ret == 1:
- return namespace_class_name.replace("*","").replace("const ", "").replace(k,"")
- else:
- return namespace_class_name.replace("*","").replace("const ", "").replace(k,v)
- return namespace_class_name.replace("*","").replace("const ","")
- def api_param_name_from_native(self,native_name):
- lower_name = native_name.lower()
- if lower_name == "std::string" or lower_name == 'string' or lower_name == 'basic_string' or lower_name == 'std::basic_string':
- return "str"
- if lower_name.find("unsigned ") >= 0 :
- return native_name.replace("unsigned ","")
- if lower_name.find("unordered_map") >= 0 or lower_name.find("map") >= 0:
- return "map"
- if lower_name.find("vector") >= 0 :
- return "array"
- if lower_name == "std::function":
- return "func"
- else:
- return lower_name
- def js_ret_name_from_native(self, namespace_class_name, is_enum) :
- if self.is_cocos_class(namespace_class_name):
- if namespace_class_name.find("cocos2d::Vector") >=0:
- return "new Array()"
- if namespace_class_name.find("cocos2d::Map") >=0:
- return "map_object"
- if is_enum:
- return 0
- else:
- return self.scriptname_cocos_class(namespace_class_name)
- lower_name = namespace_class_name.lower()
- if lower_name.find("unsigned ") >= 0:
- lower_name = lower_name.replace("unsigned ","")
- if lower_name == "std::string":
- return ""
- if lower_name == "char" or lower_name == "short" or lower_name == "int" or lower_name == "float" or lower_name == "double" or lower_name == "long":
- return 0
- if lower_name == "bool":
- return "false"
- if lower_name.find("std::vector") >= 0 or lower_name.find("vector") >= 0:
- return "new Array()"
- if lower_name.find("std::map") >= 0 or lower_name.find("std::unordered_map") >= 0 or lower_name.find("unordered_map") >= 0 or lower_name.find("map") >= 0:
- return "map_object"
- if lower_name == "std::function":
- return "func"
- else:
- return namespace_class_name
- def main():
- from optparse import OptionParser
- parser = OptionParser("usage: %prog [options] {configfile}")
- parser.add_option("-s", action="store", type="string", dest="section",
- help="sets a specific section to be converted")
- parser.add_option("-t", action="store", type="string", dest="target",
- help="specifies the target vm. Will search for TARGET.yaml")
- parser.add_option("-o", action="store", type="string", dest="outdir",
- help="specifies the output directory for generated C++ code")
- parser.add_option("-n", action="store", type="string", dest="out_file",
- help="specifcies the name of the output file, defaults to the prefix in the .ini file")
- (opts, args) = parser.parse_args()
- # script directory
- workingdir = os.path.dirname(inspect.getfile(inspect.currentframe()))
- if len(args) == 0:
- parser.error('invalid number of arguments')
- userconfig = ConfigParser.SafeConfigParser()
- userconfig.read('userconf.ini')
- print 'Using userconfig \n ', userconfig.items('DEFAULT')
- clang_lib_path = os.path.join(userconfig.get('DEFAULT', 'cxxgeneratordir'), 'libclang')
- cindex.Config.set_library_path(clang_lib_path);
- config = ConfigParser.SafeConfigParser()
- config.read(args[0])
- if (0 == len(config.sections())):
- raise Exception("No sections defined in config file")
- sections = []
- if opts.section:
- if (opts.section in config.sections()):
- sections = []
- sections.append(opts.section)
- else:
- raise Exception("Section not found in config file")
- else:
- print("processing all sections")
- sections = config.sections()
- # find available targets
- targetdir = os.path.join(workingdir, "targets")
- targets = []
- if (os.path.isdir(targetdir)):
- targets = [entry for entry in os.listdir(targetdir)
- if (os.path.isdir(os.path.join(targetdir, entry)))]
- if 0 == len(targets):
- raise Exception("No targets defined")
- if opts.target:
- if (opts.target in targets):
- targets = []
- targets.append(opts.target)
- if opts.outdir:
- outdir = opts.outdir
- else:
- outdir = os.path.join(workingdir, "gen")
- if not os.path.exists(outdir):
- os.makedirs(outdir)
- for t in targets:
- # Fix for hidden '.svn', '.cvs' and '.git' etc. folders - these must be ignored or otherwise they will be interpreted as a target.
- if t == ".svn" or t == ".cvs" or t == ".git" or t == ".gitignore":
- continue
- print "\n.... Generating bindings for target", t
- for s in sections:
- print "\n.... .... Processing section", s, "\n"
- gen_opts = {
- 'prefix': config.get(s, 'prefix'),
- 'headers': (config.get(s, 'headers' , 0, dict(userconfig.items('DEFAULT')))),
- 'replace_headers': config.get(s, 'replace_headers') if config.has_option(s, 'replace_headers') else None,
- 'classes': config.get(s, 'classes').split(' '),
- 'classes_need_extend': config.get(s, 'classes_need_extend').split(' ') if config.has_option(s, 'classes_need_extend') else [],
- 'clang_args': (config.get(s, 'extra_arguments', 0, dict(userconfig.items('DEFAULT'))) or "").split(" "),
- 'target': os.path.join(workingdir, "targets", t),
- 'outdir': outdir,
- 'search_path': os.path.abspath(os.path.join(userconfig.get('DEFAULT', 'cocosdir'), 'cocos')),
- 'remove_prefix': config.get(s, 'remove_prefix'),
- 'target_ns': config.get(s, 'target_namespace'),
- 'cpp_ns': config.get(s, 'cpp_namespace').split(' ') if config.has_option(s, 'cpp_namespace') else None,
- 'classes_have_no_parents': config.get(s, 'classes_have_no_parents'),
- 'base_classes_to_skip': config.get(s, 'base_classes_to_skip'),
- 'abstract_classes': config.get(s, 'abstract_classes'),
- 'skip': config.get(s, 'skip'),
- 'field': config.get(s, 'field') if config.has_option(s, 'field') else None,
- 'rename_functions': config.get(s, 'rename_functions'),
- 'rename_classes': config.get(s, 'rename_classes'),
- 'out_file': opts.out_file or config.get(s, 'prefix'),
- 'script_control_cpp': config.get(s, 'script_control_cpp') if config.has_option(s, 'script_control_cpp') else 'no',
- 'script_type': t,
- 'macro_judgement': config.get(s, 'macro_judgement') if config.has_option(s, 'macro_judgement') else None,
- 'hpp_headers': config.get(s, 'hpp_headers', 0, dict(userconfig.items('DEFAULT'))).split(' ') if config.has_option(s, 'hpp_headers') else None,
- 'cpp_headers': config.get(s, 'cpp_headers', 0, dict(userconfig.items('DEFAULT'))).split(' ') if config.has_option(s, 'cpp_headers') else None,
- 'win32_clang_flags': (config.get(s, 'win32_clang_flags', 0, dict(userconfig.items('DEFAULT'))) or "").split(" ") if config.has_option(s, 'win32_clang_flags') else None
- }
- generator = Generator(gen_opts)
- generator.generate_code()
- if __name__ == '__main__':
- try:
- main()
- except Exception as e:
- traceback.print_exc()
- sys.exit(1)
|