gen_objc.py 81 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729
  1. #!/usr/bin/env python
  2. from __future__ import print_function, unicode_literals
  3. import sys, re, os.path, errno, fnmatch
  4. import json
  5. import logging
  6. import codecs
  7. import io
  8. from shutil import copyfile
  9. from pprint import pformat
  10. from string import Template
  11. if sys.version_info >= (3, 8): # Python 3.8+
  12. from shutil import copytree
  13. def copy_tree(src, dst):
  14. copytree(src, dst, dirs_exist_ok=True)
  15. else:
  16. from distutils.dir_util import copy_tree
  17. try:
  18. from io import StringIO # Python 3
  19. except:
  20. from io import BytesIO as StringIO
  21. SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
  22. # list of modules
  23. config = None
  24. ROOT_DIR = None
  25. total_files = 0
  26. updated_files = 0
  27. module_imports = []
  28. # list of namespaces, which should be skipped by wrapper generator
  29. # the list is loaded from misc/objc/gen_dict.json defined for the module only
  30. namespace_ignore_list = []
  31. # list of class names, which should be skipped by wrapper generator
  32. # the list is loaded from misc/objc/gen_dict.json defined for the module and its dependencies
  33. class_ignore_list = []
  34. # list of enum names, which should be skipped by wrapper generator
  35. enum_ignore_list = []
  36. # list of constant names, which should be skipped by wrapper generator
  37. # ignored constants can be defined using regular expressions
  38. const_ignore_list = []
  39. # list of private constants
  40. const_private_list = []
  41. # { Module : { public : [[name, val],...], private : [[]...] } }
  42. missing_consts = {}
  43. type_dict = {
  44. "" : {"objc_type" : ""}, # c-tor ret_type
  45. "void" : {"objc_type" : "void", "is_primitive" : True, "swift_type": "Void"},
  46. "bool" : {"objc_type" : "BOOL", "is_primitive" : True, "to_cpp": "(bool)%(n)s", "swift_type": "Bool"},
  47. "char" : {"objc_type" : "char", "is_primitive" : True, "swift_type": "Int8"},
  48. "int" : {"objc_type" : "int", "is_primitive" : True, "out_type" : "int*", "out_type_ptr": "%(n)s", "out_type_ref": "*(int*)(%(n)s)", "swift_type": "Int32"},
  49. "long" : {"objc_type" : "long", "is_primitive" : True, "swift_type": "Int"},
  50. "float" : {"objc_type" : "float", "is_primitive" : True, "out_type" : "float*", "out_type_ptr": "%(n)s", "out_type_ref": "*(float*)(%(n)s)", "swift_type": "Float"},
  51. "double" : {"objc_type" : "double", "is_primitive" : True, "out_type" : "double*", "out_type_ptr": "%(n)s", "out_type_ref": "*(double*)(%(n)s)", "swift_type": "Double"},
  52. "size_t" : {"objc_type" : "size_t", "is_primitive" : True},
  53. "int64" : {"objc_type" : "long", "is_primitive" : True, "swift_type": "Int"},
  54. "string" : {"objc_type" : "NSString*", "is_primitive" : True, "from_cpp": "[NSString stringWithUTF8String:%(n)s.c_str()]", "cast_to": "std::string", "swift_type": "String"}
  55. }
  56. # Defines a rule to add extra prefixes for names from specific namespaces.
  57. # In example, cv::fisheye::stereoRectify from namespace fisheye is wrapped as fisheye_stereoRectify
  58. namespaces_dict = {}
  59. # { module: { class | "*" : [ header ]} }
  60. AdditionalImports = {}
  61. # { class : { func : {declaration, implementation} } }
  62. ManualFuncs = {}
  63. # { class : { func : { arg_name : {"ctype" : ctype, "attrib" : [attrib]} } } }
  64. func_arg_fix = {}
  65. # { class : { func : { prolog : "", epilog : "" } } }
  66. header_fix = {}
  67. # { class : { enum: fixed_enum } }
  68. enum_fix = {}
  69. # { class : { enum: { const: fixed_const} } }
  70. const_fix = {}
  71. # { (class, func) : objc_signature }
  72. method_dict = {
  73. ("Mat", "convertTo") : "-convertTo:rtype:alpha:beta:",
  74. ("Mat", "setTo") : "-setToScalar:mask:",
  75. ("Mat", "zeros") : "+zeros:cols:type:",
  76. ("Mat", "ones") : "+ones:cols:type:",
  77. ("Mat", "dot") : "-dot:"
  78. }
  79. modules = []
  80. class SkipSymbolException(Exception):
  81. def __init__(self, text):
  82. self.t = text
  83. def __str__(self):
  84. return self.t
  85. def read_contents(fname):
  86. with open(fname, 'r') as f:
  87. data = f.read()
  88. return data
  89. def mkdir_p(path):
  90. ''' mkdir -p '''
  91. try:
  92. os.makedirs(path)
  93. except OSError as exc:
  94. if exc.errno == errno.EEXIST and os.path.isdir(path):
  95. pass
  96. else:
  97. raise
  98. def header_import(hdr):
  99. """ converts absolute header path to import parameter """
  100. pos = hdr.find('/include/')
  101. hdr = hdr[pos+9 if pos >= 0 else 0:]
  102. #pos = hdr.find('opencv2/')
  103. #hdr = hdr[pos+8 if pos >= 0 else 0:]
  104. return hdr
  105. def make_objcname(m):
  106. return "Cv"+m if (m[0] in "0123456789") else m
  107. def make_objcmodule(m):
  108. return "cv"+m if (m[0] in "0123456789") else m
  109. T_OBJC_CLASS_HEADER = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_class_header.template'))
  110. T_OBJC_CLASS_BODY = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_class_body.template'))
  111. T_OBJC_MODULE_HEADER = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_module_header.template'))
  112. T_OBJC_MODULE_BODY = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_module_body.template'))
  113. class GeneralInfo():
  114. def __init__(self, type, decl, namespaces):
  115. self.symbol_id, self.namespace, self.classpath, self.classname, self.name = self.parseName(decl[0], namespaces)
  116. for ns_ignore in namespace_ignore_list:
  117. if self.symbol_id.startswith(ns_ignore + '.'):
  118. raise SkipSymbolException('ignored namespace ({}): {}'.format(ns_ignore, self.symbol_id))
  119. # parse doxygen comments
  120. self.params={}
  121. self.deprecated = False
  122. if type == "class":
  123. docstring = "// C++: class " + self.name + "\n"
  124. else:
  125. docstring=""
  126. if len(decl)>5 and decl[5]:
  127. doc = decl[5]
  128. if re.search("(@|\\\\)deprecated", doc):
  129. self.deprecated = True
  130. docstring += sanitize_documentation_string(doc, type)
  131. elif type == "class":
  132. docstring += "/**\n * The " + self.name + " module\n */\n"
  133. self.docstring = docstring
  134. def parseName(self, name, namespaces):
  135. '''
  136. input: full name and available namespaces
  137. returns: (namespace, classpath, classname, name)
  138. '''
  139. name = name[name.find(" ")+1:].strip() # remove struct/class/const prefix
  140. spaceName = ""
  141. localName = name # <classes>.<name>
  142. for namespace in sorted(namespaces, key=len, reverse=True):
  143. if name.startswith(namespace + "."):
  144. spaceName = namespace
  145. localName = name.replace(namespace + ".", "")
  146. break
  147. pieces = localName.split(".")
  148. if len(pieces) > 2: # <class>.<class>.<class>.<name>
  149. return name, spaceName, ".".join(pieces[:-1]), pieces[-2], pieces[-1]
  150. elif len(pieces) == 2: # <class>.<name>
  151. return name, spaceName, pieces[0], pieces[0], pieces[1]
  152. elif len(pieces) == 1: # <name>
  153. return name, spaceName, "", "", pieces[0]
  154. else:
  155. return name, spaceName, "", "" # error?!
  156. def fullName(self, isCPP=False):
  157. result = ".".join([self.fullClass(), self.name])
  158. return result if not isCPP else get_cname(result)
  159. def fullClass(self, isCPP=False):
  160. result = ".".join([f for f in [self.namespace] + self.classpath.split(".") if len(f)>0])
  161. return result if not isCPP else get_cname(result)
  162. class ConstInfo(GeneralInfo):
  163. def __init__(self, decl, addedManually=False, namespaces=[], enumType=None):
  164. GeneralInfo.__init__(self, "const", decl, namespaces)
  165. self.cname = get_cname(self.name)
  166. self.swift_name = None
  167. self.value = decl[1]
  168. self.enumType = enumType
  169. self.addedManually = addedManually
  170. if self.namespace in namespaces_dict:
  171. self.name = '%s_%s' % (namespaces_dict[self.namespace], self.name)
  172. def __repr__(self):
  173. return Template("CONST $name=$value$manual").substitute(name=self.name,
  174. value=self.value,
  175. manual="(manual)" if self.addedManually else "")
  176. def isIgnored(self):
  177. for c in const_ignore_list:
  178. if re.match(c, self.name):
  179. return True
  180. return False
  181. def normalize_field_name(name):
  182. return name.replace(".","_").replace("[","").replace("]","").replace("_getNativeObjAddr()","_nativeObj")
  183. def normalize_class_name(name):
  184. return re.sub(r"^cv\.", "", name).replace(".", "_")
  185. def get_cname(name):
  186. return name.replace(".", "::")
  187. def cast_from(t):
  188. if t in type_dict and "cast_from" in type_dict[t]:
  189. return type_dict[t]["cast_from"]
  190. return t
  191. def cast_to(t):
  192. if t in type_dict and "cast_to" in type_dict[t]:
  193. return type_dict[t]["cast_to"]
  194. return t
  195. def gen_class_doc(docstring, module, members, enums):
  196. lines = docstring.splitlines()
  197. lines.insert(len(lines)-1, " *")
  198. if len(members) > 0:
  199. lines.insert(len(lines)-1, " * Member classes: " + ", ".join([("`" + m + "`") for m in members]))
  200. lines.insert(len(lines)-1, " *")
  201. else:
  202. lines.insert(len(lines)-1, " * Member of `" + module + "`")
  203. if len(enums) > 0:
  204. lines.insert(len(lines)-1, " * Member enums: " + ", ".join([("`" + m + "`") for m in enums]))
  205. return "\n".join(lines)
  206. class ClassPropInfo():
  207. def __init__(self, decl): # [f_ctype, f_name, '', '/RW']
  208. self.ctype = decl[0]
  209. self.name = decl[1]
  210. self.rw = "/RW" in decl[3]
  211. def __repr__(self):
  212. return Template("PROP $ctype $name").substitute(ctype=self.ctype, name=self.name)
  213. class ClassInfo(GeneralInfo):
  214. def __init__(self, decl, namespaces=[]): # [ 'class/struct cname', ': base', [modlist] ]
  215. GeneralInfo.__init__(self, "class", decl, namespaces)
  216. self.cname = self.name if not self.classname else self.classname + "_" + self.name
  217. self.real_cname = self.name if not self.classname else self.classname + "::" + self.name
  218. self.methods = []
  219. self.methods_suffixes = {}
  220. self.consts = [] # using a list to save the occurrence order
  221. self.private_consts = []
  222. self.imports = set()
  223. self.props= []
  224. self.objc_name = self.name if not self.classname else self.classname + self.name
  225. self.smart = None # True if class stores Ptr<T>* instead of T* in nativeObj field
  226. self.additionalImports = None # additional import files
  227. self.enum_declarations = None # Objective-C enum declarations stream
  228. self.method_declarations = None # Objective-C method declarations stream
  229. self.method_implementations = None # Objective-C method implementations stream
  230. self.objc_header_template = None # Objective-C header code
  231. self.objc_body_template = None # Objective-C body code
  232. for m in decl[2]:
  233. if m.startswith("="):
  234. self.objc_name = m[1:]
  235. self.base = ''
  236. self.is_base_class = True
  237. self.native_ptr_name = "nativePtr"
  238. self.member_classes = [] # Only relevant for modules
  239. self.member_enums = [] # Only relevant for modules
  240. if decl[1]:
  241. self.base = re.sub(r"^.*:", "", decl[1].split(",")[0]).strip()
  242. if self.base:
  243. self.is_base_class = False
  244. self.native_ptr_name = "nativePtr" + self.objc_name
  245. def __repr__(self):
  246. return Template("CLASS $namespace::$classpath.$name : $base").substitute(**self.__dict__)
  247. def getImports(self, module):
  248. return ["#import \"%s.h\"" % make_objcname(c) for c in sorted([m for m in [type_dict[m]["import_module"] if m in type_dict and "import_module" in type_dict[m] else m for m in self.imports] if m != self.name])]
  249. def isEnum(self, c):
  250. return c in type_dict and type_dict[c].get("is_enum", False)
  251. def getForwardDeclarations(self, module):
  252. enum_decl = [x for x in self.imports if self.isEnum(x) and type_dict[x]["import_module"] != module]
  253. enum_imports = sorted(list(set([type_dict[m]["import_module"] for m in enum_decl])))
  254. class_decl = [x for x in self.imports if not self.isEnum(x)]
  255. return ["#import \"%s.h\"" % make_objcname(c) for c in enum_imports] + [""] + ["@class %s;" % c for c in sorted(class_decl)]
  256. def addImports(self, ctype, is_out_type):
  257. if ctype == self.cname:
  258. return
  259. if ctype in type_dict:
  260. objc_import = None
  261. if "v_type" in type_dict[ctype]:
  262. objc_import = type_dict[type_dict[ctype]["v_type"]]["objc_type"]
  263. elif "v_v_type" in type_dict[ctype]:
  264. objc_import = type_dict[type_dict[ctype]["v_v_type"]]["objc_type"]
  265. elif not type_dict[ctype].get("is_primitive", False):
  266. objc_import = type_dict[ctype]["objc_type"]
  267. if objc_import is not None and objc_import not in ["NSNumber*", "NSString*"] and not (objc_import in type_dict and type_dict[objc_import].get("is_primitive", False)):
  268. objc_import = objc_import[:-1] if objc_import[-1] == "*" else objc_import # remove trailing "*"
  269. if objc_import != self.cname:
  270. self.imports.add(objc_import) # remove trailing "*"
  271. def getAllMethods(self):
  272. result = []
  273. result += [fi for fi in self.methods if fi.isconstructor]
  274. result += [fi for fi in self.methods if not fi.isconstructor]
  275. return result
  276. def addMethod(self, fi):
  277. self.methods.append(fi)
  278. def getConst(self, name):
  279. for cand in self.consts + self.private_consts:
  280. if cand.name == name:
  281. return cand
  282. return None
  283. def addConst(self, constinfo):
  284. # choose right list (public or private)
  285. consts = self.consts
  286. for c in const_private_list:
  287. if re.match(c, constinfo.name):
  288. consts = self.private_consts
  289. break
  290. consts.append(constinfo)
  291. def initCodeStreams(self, Module):
  292. self.additionalImports = StringIO()
  293. self.enum_declarations = StringIO()
  294. self.method_declarations = StringIO()
  295. self.method_implementations = StringIO()
  296. if self.base:
  297. self.objc_header_template = T_OBJC_CLASS_HEADER
  298. self.objc_body_template = T_OBJC_CLASS_BODY
  299. else:
  300. self.base = "NSObject"
  301. if self.name != Module:
  302. self.objc_header_template = T_OBJC_CLASS_HEADER
  303. self.objc_body_template = T_OBJC_CLASS_BODY
  304. else:
  305. self.objc_header_template = T_OBJC_MODULE_HEADER
  306. self.objc_body_template = T_OBJC_MODULE_BODY
  307. # misc handling
  308. if self.name == Module:
  309. for i in module_imports or []:
  310. self.imports.add(i)
  311. def cleanupCodeStreams(self):
  312. self.additionalImports.close()
  313. self.enum_declarations.close()
  314. self.method_declarations.close()
  315. self.method_implementations.close()
  316. def generateObjcHeaderCode(self, m, M, objcM):
  317. return Template(self.objc_header_template + "\n\n").substitute(
  318. module = M,
  319. additionalImports = self.additionalImports.getvalue(),
  320. importBaseClass = '#import "' + make_objcname(self.base) + '.h"' if not self.is_base_class else "",
  321. forwardDeclarations = "\n".join([_f for _f in self.getForwardDeclarations(objcM) if _f]),
  322. enumDeclarations = self.enum_declarations.getvalue(),
  323. nativePointerHandling = Template(
  324. """
  325. #ifdef __cplusplus
  326. @property(readonly)cv::Ptr<$cName> $native_ptr_name;
  327. #endif
  328. #ifdef __cplusplus
  329. - (instancetype)initWithNativePtr:(cv::Ptr<$cName>)nativePtr;
  330. + (instancetype)fromNative:(cv::Ptr<$cName>)nativePtr;
  331. #endif
  332. """
  333. ).substitute(
  334. cName = self.fullName(isCPP=True),
  335. native_ptr_name = self.native_ptr_name
  336. ),
  337. manualMethodDeclations = "",
  338. methodDeclarations = self.method_declarations.getvalue(),
  339. name = self.name,
  340. objcName = make_objcname(self.objc_name),
  341. cName = self.cname,
  342. imports = "\n".join(self.getImports(M)),
  343. docs = gen_class_doc(self.docstring, M, self.member_classes, self.member_enums),
  344. base = self.base)
  345. def generateObjcBodyCode(self, m, M):
  346. return Template(self.objc_body_template + "\n\n").substitute(
  347. module = M,
  348. nativePointerHandling=Template(
  349. """
  350. - (instancetype)initWithNativePtr:(cv::Ptr<$cName>)nativePtr {
  351. self = [super $init_call];
  352. if (self) {
  353. _$native_ptr_name = nativePtr;
  354. }
  355. return self;
  356. }
  357. + (instancetype)fromNative:(cv::Ptr<$cName>)nativePtr {
  358. return [[$objcName alloc] initWithNativePtr:nativePtr];
  359. }
  360. """
  361. ).substitute(
  362. cName = self.fullName(isCPP=True),
  363. objcName = self.objc_name,
  364. native_ptr_name = self.native_ptr_name,
  365. init_call = "init" if self.is_base_class else "initWithNativePtr:nativePtr"
  366. ),
  367. manualMethodDeclations = "",
  368. methodImplementations = self.method_implementations.getvalue(),
  369. name = self.name,
  370. objcName = self.objc_name,
  371. cName = self.cname,
  372. imports = "\n".join(self.getImports(M)),
  373. docs = gen_class_doc(self.docstring, M, self.member_classes, self.member_enums),
  374. base = self.base)
  375. class ArgInfo():
  376. def __init__(self, arg_tuple): # [ ctype, name, def val, [mod], argno ]
  377. self.pointer = False
  378. ctype = arg_tuple[0]
  379. if ctype.endswith("*"):
  380. ctype = ctype[:-1]
  381. self.pointer = True
  382. self.ctype = ctype
  383. self.name = arg_tuple[1]
  384. self.defval = arg_tuple[2]
  385. self.out = ""
  386. if "/O" in arg_tuple[3]:
  387. self.out = "O"
  388. if "/IO" in arg_tuple[3]:
  389. self.out = "IO"
  390. def __repr__(self):
  391. return Template("ARG $ctype$p $name=$defval").substitute(ctype=self.ctype,
  392. p=" *" if self.pointer else "",
  393. name=self.name,
  394. defval=self.defval)
  395. class FuncInfo(GeneralInfo):
  396. def __init__(self, decl, module, namespaces=[]): # [ funcname, return_ctype, [modifiers], [args] ]
  397. GeneralInfo.__init__(self, "func", decl, namespaces)
  398. self.cname = get_cname(decl[0])
  399. nested_type = self.classpath.find(".") != -1
  400. self.objc_name = self.name if not nested_type else self.classpath.replace(".", "")
  401. self.classname = self.classname if not nested_type else self.classpath.replace(".", "_")
  402. self.swift_name = self.name
  403. self.cv_name = self.fullName(isCPP=True)
  404. self.isconstructor = self.name == self.classname
  405. if "[" in self.name:
  406. self.objc_name = "getelem"
  407. if self.namespace in namespaces_dict:
  408. self.objc_name = '%s_%s' % (namespaces_dict[self.namespace], self.objc_name)
  409. self.swift_name = '%s_%s' % (namespaces_dict[self.namespace], self.swift_name)
  410. for m in decl[2]:
  411. if m.startswith("="):
  412. self.objc_name = m[1:]
  413. self.static = ["","static"][ "/S" in decl[2] ]
  414. self.ctype = re.sub(r"^CvTermCriteria", "TermCriteria", decl[1] or "")
  415. self.args = []
  416. func_fix_map = func_arg_fix.get(self.classname or module, {}).get(self.objc_name, {})
  417. header_fixes = header_fix.get(self.classname or module, {}).get(self.objc_name, {})
  418. self.prolog = header_fixes.get('prolog', None)
  419. self.epilog = header_fixes.get('epilog', None)
  420. for a in decl[3]:
  421. arg = a[:]
  422. arg_fix_map = func_fix_map.get(arg[1], {})
  423. arg[0] = arg_fix_map.get('ctype', arg[0]) #fixing arg type
  424. arg[2] = arg_fix_map.get('defval', arg[2]) #fixing arg defval
  425. arg[3] = arg_fix_map.get('attrib', arg[3]) #fixing arg attrib
  426. self.args.append(ArgInfo(arg))
  427. if type_complete(self.args, self.ctype):
  428. func_fix_map = func_arg_fix.get(self.classname or module, {}).get(self.signature(self.args), {})
  429. name_fix_map = func_fix_map.get(self.name, {})
  430. self.objc_name = name_fix_map.get('name', self.objc_name)
  431. self.swift_name = name_fix_map.get('swift_name', self.swift_name)
  432. for arg in self.args:
  433. arg_fix_map = func_fix_map.get(arg.name, {})
  434. arg.ctype = arg_fix_map.get('ctype', arg.ctype) #fixing arg type
  435. arg.defval = arg_fix_map.get('defval', arg.defval) #fixing arg type
  436. arg.name = arg_fix_map.get('name', arg.name) #fixing arg name
  437. def __repr__(self):
  438. return Template("FUNC <$ctype $namespace.$classpath.$name $args>").substitute(**self.__dict__)
  439. def __lt__(self, other):
  440. return self.__repr__() < other.__repr__()
  441. def signature(self, args):
  442. objc_args = build_objc_args(args)
  443. return "(" + type_dict[self.ctype]["objc_type"] + ")" + self.objc_name + " ".join(objc_args)
  444. def type_complete(args, ctype):
  445. for a in args:
  446. if a.ctype not in type_dict:
  447. if not a.defval and a.ctype.endswith("*"):
  448. a.defval = 0
  449. if a.defval:
  450. a.ctype = ''
  451. continue
  452. return False
  453. if ctype not in type_dict:
  454. return False
  455. return True
  456. def build_objc_args(args):
  457. objc_args = []
  458. for a in args:
  459. if a.ctype not in type_dict:
  460. if not a.defval and a.ctype.endswith("*"):
  461. a.defval = 0
  462. if a.defval:
  463. a.ctype = ''
  464. continue
  465. if not a.ctype: # hidden
  466. continue
  467. objc_type = type_dict[a.ctype]["objc_type"]
  468. if "v_type" in type_dict[a.ctype]:
  469. if "O" in a.out:
  470. objc_type = "NSMutableArray<" + objc_type + ">*"
  471. else:
  472. objc_type = "NSArray<" + objc_type + ">*"
  473. elif "v_v_type" in type_dict[a.ctype]:
  474. if "O" in a.out:
  475. objc_type = "NSMutableArray<NSMutableArray<" + objc_type + ">*>*"
  476. else:
  477. objc_type = "NSArray<NSArray<" + objc_type + ">*>*"
  478. if a.out and type_dict[a.ctype].get("out_type", ""):
  479. objc_type = type_dict[a.ctype]["out_type"]
  480. objc_args.append((a.name if len(objc_args) > 0 else '') + ':(' + objc_type + ')' + a.name)
  481. return objc_args
  482. def build_objc_method_name(args):
  483. objc_method_name = ""
  484. for a in args[1:]:
  485. if a.ctype not in type_dict:
  486. if not a.defval and a.ctype.endswith("*"):
  487. a.defval = 0
  488. if a.defval:
  489. a.ctype = ''
  490. continue
  491. if not a.ctype: # hidden
  492. continue
  493. objc_method_name += a.name + ":"
  494. return objc_method_name
  495. def get_swift_type(ctype):
  496. has_swift_type = "swift_type" in type_dict[ctype]
  497. swift_type = type_dict[ctype]["swift_type"] if has_swift_type else type_dict[ctype]["objc_type"]
  498. if swift_type[-1:] == "*":
  499. swift_type = swift_type[:-1]
  500. if not has_swift_type:
  501. if "v_type" in type_dict[ctype]:
  502. swift_type = "[" + swift_type + "]"
  503. elif "v_v_type" in type_dict[ctype]:
  504. swift_type = "[[" + swift_type + "]]"
  505. return swift_type
  506. def build_swift_extension_decl(name, args, constructor, static, ret_type):
  507. extension_decl = "@nonobjc " + ("class " if static else "") + (("func " + name) if not constructor else "convenience init") + "("
  508. swift_args = []
  509. for a in args:
  510. if a.ctype not in type_dict:
  511. if not a.defval and a.ctype.endswith("*"):
  512. a.defval = 0
  513. if a.defval:
  514. a.ctype = ''
  515. continue
  516. if not a.ctype: # hidden
  517. continue
  518. swift_type = get_swift_type(a.ctype)
  519. if "O" in a.out:
  520. if type_dict[a.ctype].get("primitive_type", False):
  521. swift_type = "UnsafeMutablePointer<" + swift_type + ">"
  522. elif "v_type" in type_dict[a.ctype] or "v_v_type" in type_dict[a.ctype] or type_dict[a.ctype].get("primitive_vector", False) or type_dict[a.ctype].get("primitive_vector_vector", False):
  523. swift_type = "inout " + swift_type
  524. swift_args.append(a.name + ': ' + swift_type)
  525. extension_decl += ", ".join(swift_args) + ")"
  526. if ret_type:
  527. extension_decl += " -> " + get_swift_type(ret_type)
  528. return extension_decl
  529. def extension_arg(a):
  530. return a.ctype in type_dict and (type_dict[a.ctype].get("primitive_vector", False) or type_dict[a.ctype].get("primitive_vector_vector", False) or (("v_type" in type_dict[a.ctype] or "v_v_type" in type_dict[a.ctype]) and "O" in a.out))
  531. def extension_tmp_arg(a):
  532. if a.ctype in type_dict:
  533. if type_dict[a.ctype].get("primitive_vector", False) or type_dict[a.ctype].get("primitive_vector_vector", False):
  534. return a.name + "Vector"
  535. elif ("v_type" in type_dict[a.ctype] or "v_v_type" in type_dict[a.ctype]) and "O" in a.out:
  536. return a.name + "Array"
  537. return a.name
  538. def make_swift_extension(args):
  539. for a in args:
  540. if extension_arg(a):
  541. return True
  542. return False
  543. def build_swift_signature(args):
  544. swift_signature = ""
  545. for a in args:
  546. if a.ctype not in type_dict:
  547. if not a.defval and a.ctype.endswith("*"):
  548. a.defval = 0
  549. if a.defval:
  550. a.ctype = ''
  551. continue
  552. if not a.ctype: # hidden
  553. continue
  554. swift_signature += a.name + ":"
  555. return swift_signature
  556. def build_unrefined_call(name, args, constructor, static, classname, has_ret):
  557. swift_refine_call = ("let ret = " if has_ret and not constructor else "") + ((make_objcname(classname) + ".") if static else "") + (name if not constructor else "self.init")
  558. call_args = []
  559. for a in args:
  560. if a.ctype not in type_dict:
  561. if not a.defval and a.ctype.endswith("*"):
  562. a.defval = 0
  563. if a.defval:
  564. a.ctype = ''
  565. continue
  566. if not a.ctype: # hidden
  567. continue
  568. call_args.append(a.name + ": " + extension_tmp_arg(a))
  569. swift_refine_call += "(" + ", ".join(call_args) + ")"
  570. return swift_refine_call
  571. def build_swift_logues(args):
  572. prologue = []
  573. epilogue = []
  574. for a in args:
  575. if a.ctype not in type_dict:
  576. if not a.defval and a.ctype.endswith("*"):
  577. a.defval = 0
  578. if a.defval:
  579. a.ctype = ''
  580. continue
  581. if not a.ctype: # hidden
  582. continue
  583. if a.ctype in type_dict:
  584. if type_dict[a.ctype].get("primitive_vector", False):
  585. prologue.append("let " + extension_tmp_arg(a) + " = " + type_dict[a.ctype]["objc_type"][:-1] + "(" + a.name + ")")
  586. if "O" in a.out:
  587. unsigned = type_dict[a.ctype].get("unsigned", False)
  588. array_prop = "array" if not unsigned else "unsignedArray"
  589. epilogue.append(a.name + ".removeAll()")
  590. epilogue.append(a.name + ".append(contentsOf: " + extension_tmp_arg(a) + "." + array_prop + ")")
  591. elif type_dict[a.ctype].get("primitive_vector_vector", False):
  592. if not "O" in a.out:
  593. prologue.append("let " + extension_tmp_arg(a) + " = " + a.name + ".map {" + type_dict[a.ctype]["objc_type"][:-1] + "($0) }")
  594. else:
  595. prologue.append("let " + extension_tmp_arg(a) + " = NSMutableArray(array: " + a.name + ".map {" + type_dict[a.ctype]["objc_type"][:-1] + "($0) })")
  596. epilogue.append(a.name + ".removeAll()")
  597. epilogue.append(a.name + ".append(contentsOf: " + extension_tmp_arg(a) + ".map { ($.0 as! " + type_dict[a.ctype]["objc_type"][:-1] + ").array })")
  598. elif ("v_type" in type_dict[a.ctype] or "v_v_type" in type_dict[a.ctype]) and "O" in a.out:
  599. prologue.append("let " + extension_tmp_arg(a) + " = NSMutableArray(array: " + a.name + ")")
  600. epilogue.append(a.name + ".removeAll()")
  601. epilogue.append(a.name + ".append(contentsOf: " + extension_tmp_arg(a) + " as! " + get_swift_type(a.ctype) + ")")
  602. return prologue, epilogue
  603. def add_method_to_dict(class_name, fi):
  604. static = fi.static if fi.classname else True
  605. if (class_name, fi.objc_name) not in method_dict:
  606. objc_method_name = ("+" if static else "-") + fi.objc_name + ":" + build_objc_method_name(fi.args)
  607. method_dict[(class_name, fi.objc_name)] = objc_method_name
  608. def see_lookup(objc_class, see):
  609. semi_colon = see.find("::")
  610. see_class = see[:semi_colon] if semi_colon > 0 else objc_class
  611. see_method = see[(semi_colon + 2):] if semi_colon != -1 else see
  612. if (see_class, see_method) in method_dict:
  613. method = method_dict[(see_class, see_method)]
  614. if see_class == objc_class:
  615. return method
  616. else:
  617. return ("-" if method[0] == "-" else "") + "[" + see_class + " " + method[1:] + "]"
  618. else:
  619. return see
  620. class ObjectiveCWrapperGenerator(object):
  621. def __init__(self):
  622. self.header_files = []
  623. self.clear()
  624. def clear(self):
  625. self.namespaces = ["cv"]
  626. mat_class_info = ClassInfo([ 'class Mat', '', [], [] ], self.namespaces)
  627. mat_class_info.namespace = "cv"
  628. self.classes = { "Mat" : mat_class_info }
  629. self.classes["Mat"].namespace = "cv"
  630. self.module = ""
  631. self.Module = ""
  632. self.extension_implementations = None # Swift extensions implementations stream
  633. self.ported_func_list = []
  634. self.skipped_func_list = []
  635. self.def_args_hist = {} # { def_args_cnt : funcs_cnt }
  636. def add_class(self, decl):
  637. classinfo = ClassInfo(decl, namespaces=self.namespaces)
  638. if classinfo.name in class_ignore_list:
  639. logging.info('ignored: %s', classinfo)
  640. return None
  641. if classinfo.name != self.Module:
  642. self.classes[self.Module].member_classes.append(classinfo.objc_name)
  643. name = classinfo.cname
  644. if self.isWrapped(name) and not classinfo.base:
  645. logging.warning('duplicated: %s', classinfo)
  646. return None
  647. if name in self.classes: # TODO implement inner namespaces
  648. if self.classes[name].symbol_id != classinfo.symbol_id:
  649. logging.warning('duplicated under new id: {} (was {})'.format(classinfo.symbol_id, self.classes[name].symbol_id))
  650. return None
  651. self.classes[name] = classinfo
  652. if name in type_dict and not classinfo.base:
  653. logging.warning('duplicated: %s', classinfo)
  654. return None
  655. if name != self.Module:
  656. type_dict.setdefault(name, {}).update(
  657. { "objc_type" : classinfo.objc_name + "*",
  658. "from_cpp" : "[" + classinfo.objc_name + " fromNative:%(n)s]",
  659. "to_cpp" : "*(%(n)s." + classinfo.native_ptr_name + ")" }
  660. )
  661. # missing_consts { Module : { public : [[name, val],...], private : [[]...] } }
  662. if name in missing_consts:
  663. if 'public' in missing_consts[name]:
  664. for (n, val) in missing_consts[name]['public']:
  665. classinfo.consts.append( ConstInfo([n, val], addedManually=True) )
  666. # class props
  667. for p in decl[3]:
  668. classinfo.props.append( ClassPropInfo(p) )
  669. if name != self.Module:
  670. type_dict.setdefault("Ptr_"+name, {}).update(
  671. { "objc_type" : classinfo.objc_name + "*",
  672. "c_type" : name,
  673. "real_c_type" : classinfo.real_cname,
  674. "to_cpp": "%(n)s." + classinfo.native_ptr_name,
  675. "from_cpp": "[" + name + " fromNative:%(n)s]"}
  676. )
  677. logging.info('ok: class %s, name: %s, base: %s', classinfo, name, classinfo.base)
  678. return classinfo
  679. def add_const(self, decl, scope=None, enumType=None): # [ "const cname", val, [], [] ]
  680. constinfo = ConstInfo(decl, namespaces=self.namespaces, enumType=enumType)
  681. if constinfo.isIgnored():
  682. logging.info('ignored: %s', constinfo)
  683. else:
  684. objc_type = enumType.rsplit(".", 1)[-1] if enumType else ""
  685. if constinfo.enumType and constinfo.classpath:
  686. new_name = constinfo.classname + '_' + constinfo.name
  687. const_fix.setdefault(constinfo.classpath, {}).setdefault(objc_type, {})[constinfo.name] = new_name
  688. constinfo.swift_name = constinfo.name
  689. constinfo.name = new_name
  690. logging.info('use outer class prefix: %s', constinfo)
  691. if constinfo.classpath in const_fix and objc_type in const_fix[constinfo.classpath]:
  692. fixed_consts = const_fix[constinfo.classpath][objc_type]
  693. if constinfo.name in fixed_consts:
  694. fixed_const = fixed_consts[constinfo.name]
  695. constinfo.name = fixed_const
  696. constinfo.cname = fixed_const
  697. if constinfo.value in fixed_consts:
  698. constinfo.value = fixed_consts[constinfo.value]
  699. if not self.isWrapped(constinfo.classname):
  700. logging.info('class not found: %s', constinfo)
  701. if not constinfo.name.startswith(constinfo.classname + "_"):
  702. constinfo.swift_name = constinfo.name
  703. constinfo.name = constinfo.classname + '_' + constinfo.name
  704. constinfo.classname = ''
  705. ci = self.getClass(constinfo.classname)
  706. duplicate = ci.getConst(constinfo.name)
  707. if duplicate:
  708. if duplicate.addedManually:
  709. logging.info('manual: %s', constinfo)
  710. else:
  711. logging.warning('duplicated: %s', constinfo)
  712. else:
  713. ci.addConst(constinfo)
  714. logging.info('ok: %s', constinfo)
  715. def add_enum(self, decl): # [ "enum cname", "", [], [] ]
  716. enumType = decl[0].rsplit(" ", 1)[1]
  717. if enumType.endswith("<unnamed>"):
  718. enumType = None
  719. else:
  720. ctype = normalize_class_name(enumType)
  721. constinfo = ConstInfo(decl[3][0], namespaces=self.namespaces, enumType=enumType)
  722. objc_type = enumType.rsplit(".", 1)[-1]
  723. if objc_type in enum_ignore_list:
  724. return
  725. if constinfo.classname in enum_fix:
  726. objc_type = enum_fix[constinfo.classname].get(objc_type, objc_type)
  727. import_module = constinfo.classname if constinfo.classname and constinfo.classname != objc_type else self.Module
  728. type_dict[ctype] = { "cast_from" : "int",
  729. "cast_to" : get_cname(enumType),
  730. "objc_type" : objc_type,
  731. "is_enum" : True,
  732. "import_module" : import_module,
  733. "from_cpp" : "(" + objc_type + ")%(n)s"}
  734. type_dict[objc_type] = { "cast_to" : get_cname(enumType),
  735. "objc_type": objc_type,
  736. "is_enum": True,
  737. "import_module": import_module,
  738. "from_cpp": "(" + objc_type + ")%(n)s"}
  739. self.classes[self.Module].member_enums.append(objc_type)
  740. const_decls = decl[3]
  741. for decl in const_decls:
  742. self.add_const(decl, self.Module, enumType)
  743. def add_func(self, decl):
  744. fi = FuncInfo(decl, self.Module, namespaces=self.namespaces)
  745. classname = fi.classname or self.Module
  746. if classname in class_ignore_list:
  747. logging.info('ignored: %s', fi)
  748. elif classname in ManualFuncs and fi.objc_name in ManualFuncs[classname]:
  749. logging.info('manual: %s', fi)
  750. if "objc_method_name" in ManualFuncs[classname][fi.objc_name]:
  751. method_dict[(classname, fi.objc_name)] = ManualFuncs[classname][fi.objc_name]["objc_method_name"]
  752. elif not self.isWrapped(classname):
  753. logging.warning('not found: %s', fi)
  754. else:
  755. ci = self.getClass(classname)
  756. if ci.symbol_id != fi.symbol_id[0:fi.symbol_id.rfind('.')] and ci.symbol_id != self.Module:
  757. # TODO fix this (inner namepaces)
  758. logging.warning('SKIP: mismatched class: {} (class: {})'.format(fi.symbol_id, ci.symbol_id))
  759. return
  760. ci.addMethod(fi)
  761. logging.info('ok: %s', fi)
  762. # calc args with def val
  763. cnt = len([a for a in fi.args if a.defval])
  764. self.def_args_hist[cnt] = self.def_args_hist.get(cnt, 0) + 1
  765. add_method_to_dict(classname, fi)
  766. def save(self, path, buf):
  767. global total_files, updated_files
  768. if len(buf) == 0:
  769. return
  770. total_files += 1
  771. if os.path.exists(path):
  772. with open(path, "rt") as f:
  773. content = f.read()
  774. if content == buf:
  775. return
  776. with codecs.open(path, "w", "utf-8") as f:
  777. f.write(buf)
  778. updated_files += 1
  779. def get_namespace_prefix(self, cname):
  780. namespace = self.classes[cname].namespace if cname in self.classes else "cv"
  781. return namespace.replace(".", "::") + "::"
  782. def gen(self, srcfiles, module, output_path, output_objc_path,
  783. common_headers, manual_classes, preprocessor_definitions=None):
  784. self.clear()
  785. self.module = module
  786. self.objcmodule = make_objcmodule(module)
  787. self.Module = module.capitalize()
  788. extension_implementations = StringIO() # Swift extensions implementations stream
  789. extension_signatures = []
  790. # TODO: support UMat versions of declarations (implement UMat-wrapper for Java)
  791. parser = hdr_parser.CppHeaderParser(
  792. generate_umat_decls=False,
  793. preprocessor_definitions=preprocessor_definitions
  794. )
  795. module_ci = self.add_class( ['class ' + self.Module, '', [], []]) # [ 'class/struct cname', ':bases', [modlist] [props] ]
  796. module_ci.header_import = module + '.hpp'
  797. # scan the headers and build more descriptive maps of classes, consts, functions
  798. includes = []
  799. for hdr in common_headers:
  800. logging.info("\n===== Common header : %s =====", hdr)
  801. includes.append(header_import(hdr))
  802. for hdr in srcfiles:
  803. decls = parser.parse(hdr)
  804. self.namespaces = sorted(parser.namespaces)
  805. logging.info("\n\n===== Header: %s =====", hdr)
  806. logging.info("Namespaces: %s", sorted(parser.namespaces))
  807. if decls:
  808. includes.append(header_import(hdr))
  809. else:
  810. logging.info("Ignore header: %s", hdr)
  811. for decl in decls:
  812. logging.info("\n--- Incoming ---\n%s", pformat(decl[:5], 4)) # without docstring
  813. name = decl[0]
  814. try:
  815. if name.startswith("struct") or name.startswith("class"):
  816. ci = self.add_class(decl)
  817. if ci:
  818. ci.header_import = header_import(hdr)
  819. elif name.startswith("const"):
  820. self.add_const(decl)
  821. elif name.startswith("enum"):
  822. # enum
  823. self.add_enum(decl)
  824. else: # function
  825. self.add_func(decl)
  826. except SkipSymbolException as e:
  827. logging.info('SKIP: {} due to {}'.format(name, e))
  828. self.classes[self.Module].member_classes += manual_classes
  829. logging.info("\n\n===== Generating... =====")
  830. package_path = os.path.join(output_objc_path, self.objcmodule)
  831. mkdir_p(package_path)
  832. extension_file = "%s/%sExt.swift" % (package_path, make_objcname(self.Module))
  833. for ci in sorted(self.classes.values(), key=lambda x: x.symbol_id):
  834. if ci.name == "Mat":
  835. continue
  836. ci.initCodeStreams(self.Module)
  837. self.gen_class(ci, self.module, extension_implementations, extension_signatures)
  838. classObjcHeaderCode = ci.generateObjcHeaderCode(self.module, self.Module, ci.objc_name)
  839. objc_mangled_name = make_objcname(ci.objc_name)
  840. header_file = "%s/%s.h" % (package_path, objc_mangled_name)
  841. self.save(header_file, classObjcHeaderCode)
  842. self.header_files.append(header_file)
  843. classObjcBodyCode = ci.generateObjcBodyCode(self.module, self.Module)
  844. self.save("%s/%s.mm" % (package_path, objc_mangled_name), classObjcBodyCode)
  845. ci.cleanupCodeStreams()
  846. self.save(extension_file, extension_implementations.getvalue())
  847. extension_implementations.close()
  848. self.save(os.path.join(output_path, self.objcmodule+".txt"), self.makeReport())
  849. def makeReport(self):
  850. '''
  851. Returns string with generator report
  852. '''
  853. report = StringIO()
  854. total_count = len(self.ported_func_list)+ len(self.skipped_func_list)
  855. report.write("PORTED FUNCs LIST (%i of %i):\n\n" % (len(self.ported_func_list), total_count))
  856. report.write("\n".join(self.ported_func_list))
  857. report.write("\n\nSKIPPED FUNCs LIST (%i of %i):\n\n" % (len(self.skipped_func_list), total_count))
  858. report.write("".join(self.skipped_func_list))
  859. for i in sorted(self.def_args_hist.keys()):
  860. report.write("\n%i def args - %i funcs" % (i, self.def_args_hist[i]))
  861. return report.getvalue()
  862. def fullTypeName(self, t):
  863. if not type_dict[t].get("is_primitive", False) or "cast_to" in type_dict[t]:
  864. if "cast_to" in type_dict[t]:
  865. return type_dict[t]["cast_to"]
  866. else:
  867. namespace_prefix = self.get_namespace_prefix(t)
  868. return namespace_prefix + t
  869. else:
  870. return t
  871. def build_objc2cv_prologue(self, prologue, vector_type, vector_full_type, objc_type, vector_name, array_name):
  872. if not (vector_type in type_dict and "to_cpp" in type_dict[vector_type] and type_dict[vector_type]["to_cpp"] != "%(n)s.nativeRef"):
  873. prologue.append("OBJC2CV(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ");")
  874. else:
  875. conv_macro = "CONV_" + array_name
  876. prologue.append("#define " + conv_macro + "(e) " + type_dict[vector_type]["to_cpp"] % {"n": "e"})
  877. prologue.append("OBJC2CV_CUSTOM(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ", " + conv_macro + ");")
  878. prologue.append("#undef " + conv_macro)
  879. def build_cv2objc_epilogue(self, epilogue, vector_type, vector_full_type, objc_type, vector_name, array_name):
  880. if not (vector_type in type_dict and "from_cpp" in type_dict[vector_type] and type_dict[vector_type]["from_cpp"] != ("[" + objc_type[:-1] + " fromNative:%(n)s]")):
  881. epilogue.append("CV2OBJC(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ");")
  882. else:
  883. unconv_macro = "UNCONV_" + array_name
  884. epilogue.append("#define " + unconv_macro + "(e) " + type_dict[vector_type]["from_cpp"] % {"n": "e"})
  885. epilogue.append("CV2OBJC_CUSTOM(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ", " + unconv_macro + ");")
  886. epilogue.append("#undef " + unconv_macro)
  887. def gen_func(self, ci, fi, extension_implementations, extension_signatures):
  888. logging.info("%s", fi)
  889. method_declarations = ci.method_declarations
  890. method_implementations = ci.method_implementations
  891. decl_args = []
  892. for a in fi.args:
  893. s = a.ctype or ' _hidden_ '
  894. if a.pointer:
  895. s += "*"
  896. elif a.out:
  897. s += "&"
  898. s += " " + a.name
  899. if a.defval:
  900. s += " = " + str(a.defval)
  901. decl_args.append(s)
  902. c_decl = "%s %s %s(%s)" % ( fi.static, fi.ctype, fi.cname, ", ".join(decl_args) )
  903. # comment
  904. method_declarations.write( "\n//\n// %s\n//\n" % c_decl )
  905. method_implementations.write( "\n//\n// %s\n//\n" % c_decl )
  906. # check if we 'know' all the types
  907. if fi.ctype not in type_dict: # unsupported ret type
  908. msg = "// Return type '%s' is not supported, skipping the function\n\n" % fi.ctype
  909. self.skipped_func_list.append(c_decl + "\n" + msg)
  910. method_declarations.write( " "*4 + msg )
  911. logging.warning("SKIP:" + c_decl.strip() + "\t due to RET type " + fi.ctype)
  912. return
  913. for a in fi.args:
  914. if a.ctype not in type_dict:
  915. if not a.defval and a.ctype.endswith("*"):
  916. a.defval = 0
  917. if a.defval:
  918. a.ctype = ''
  919. continue
  920. msg = "// Unknown type '%s' (%s), skipping the function\n\n" % (a.ctype, a.out or "I")
  921. self.skipped_func_list.append(c_decl + "\n" + msg)
  922. method_declarations.write( msg )
  923. logging.warning("SKIP:" + c_decl.strip() + "\t due to ARG type " + a.ctype + "/" + (a.out or "I"))
  924. return
  925. self.ported_func_list.append(c_decl)
  926. # args
  927. args = fi.args[:] # copy
  928. objc_signatures=[]
  929. while True:
  930. # method args
  931. cv_args = []
  932. prologue = []
  933. epilogue = []
  934. if fi.ctype:
  935. ci.addImports(fi.ctype, False)
  936. for a in args:
  937. if not "v_type" in type_dict[a.ctype] and not "v_v_type" in type_dict[a.ctype]:
  938. cast = ("(" + type_dict[a.ctype]["cast_to"] + ")") if "cast_to" in type_dict[a.ctype] else ""
  939. cv_name = type_dict[a.ctype].get("to_cpp", cast + "%(n)s") if a.ctype else a.defval
  940. if a.pointer and not cv_name == "0":
  941. cv_name = "&(" + cv_name + ")"
  942. if "O" in a.out and type_dict[a.ctype].get("out_type", ""):
  943. cv_name = type_dict[a.ctype].get("out_type_ptr" if a.pointer else "out_type_ref", "%(n)s")
  944. cv_args.append(type_dict[a.ctype].get("cv_name", cv_name) % {"n": a.name})
  945. if not a.ctype: # hidden
  946. continue
  947. ci.addImports(a.ctype, "O" in a.out)
  948. if "v_type" in type_dict[a.ctype]: # pass as vector
  949. vector_cpp_type = type_dict[a.ctype]["v_type"]
  950. objc_type = type_dict[a.ctype]["objc_type"]
  951. has_namespace = vector_cpp_type.find("::") != -1
  952. ci.addImports(a.ctype, False)
  953. vector_full_cpp_type = self.fullTypeName(vector_cpp_type) if not has_namespace else vector_cpp_type
  954. vector_cpp_name = a.name + "Vector"
  955. cv_args.append(vector_cpp_name)
  956. self.build_objc2cv_prologue(prologue, vector_cpp_type, vector_full_cpp_type, objc_type, vector_cpp_name, a.name)
  957. if "O" in a.out:
  958. self.build_cv2objc_epilogue(epilogue, vector_cpp_type, vector_full_cpp_type, objc_type, vector_cpp_name, a.name)
  959. if "v_v_type" in type_dict[a.ctype]: # pass as vector of vector
  960. vector_cpp_type = type_dict[a.ctype]["v_v_type"]
  961. objc_type = type_dict[a.ctype]["objc_type"]
  962. ci.addImports(a.ctype, False)
  963. vector_full_cpp_type = self.fullTypeName(vector_cpp_type)
  964. vector_cpp_name = a.name + "Vector2"
  965. cv_args.append(vector_cpp_name)
  966. prologue.append("OBJC2CV2(" + vector_full_cpp_type + ", " + objc_type[:-1] + ", " + vector_cpp_name + ", " + a.name + ");")
  967. if "O" in a.out:
  968. epilogue.append(
  969. "CV2OBJC2(" + vector_full_cpp_type + ", " + objc_type[:-1] + ", " + vector_cpp_name + ", " + a.name + ");")
  970. # calculate method signature to check for uniqueness
  971. objc_args = build_objc_args(args)
  972. objc_signature = fi.signature(args)
  973. swift_ext = make_swift_extension(args)
  974. logging.info("Objective-C: " + objc_signature)
  975. if objc_signature in objc_signatures:
  976. if args:
  977. args.pop()
  978. continue
  979. else:
  980. break
  981. # doc comment
  982. if fi.docstring:
  983. lines = fi.docstring.splitlines()
  984. toWrite = []
  985. for index, line in enumerate(lines):
  986. p0 = line.find("@param")
  987. if p0 != -1:
  988. p0 += 7 # len("@param" + 1)
  989. p1 = line.find(' ', p0)
  990. p1 = len(line) if p1 == -1 else p1
  991. name = line[p0:p1]
  992. for arg in args:
  993. if arg.name == name:
  994. toWrite.append(re.sub(r'\*\s*@param ', '* @param ', line))
  995. break
  996. else:
  997. s0 = line.find("@see")
  998. if s0 != -1:
  999. sees = line[(s0 + 5):].split(",")
  1000. toWrite.append(line[:(s0 + 5)] + ", ".join(["`" + see_lookup(ci.objc_name, see.strip()) + "`" for see in sees]))
  1001. else:
  1002. toWrite.append(line)
  1003. for line in toWrite:
  1004. method_declarations.write(line + "\n")
  1005. # public wrapper method impl (calling native one above)
  1006. # e.g.
  1007. # public static void add( Mat src1, Mat src2, Mat dst, Mat mask, int dtype )
  1008. # { add_0( src1.nativeObj, src2.nativeObj, dst.nativeObj, mask.nativeObj, dtype ); }
  1009. ret_type = fi.ctype
  1010. if fi.ctype.endswith('*'):
  1011. ret_type = ret_type[:-1]
  1012. ret_val = self.fullTypeName(fi.ctype) + " retVal = "
  1013. ret = "return retVal;"
  1014. tail = ""
  1015. constructor = False
  1016. if "v_type" in type_dict[ret_type]:
  1017. objc_type = type_dict[ret_type]["objc_type"]
  1018. vector_type = type_dict[ret_type]["v_type"]
  1019. full_cpp_type = (self.get_namespace_prefix(vector_type) if (vector_type.find("::") == -1) else "") + vector_type
  1020. prologue.append("NSMutableArray<" + objc_type + ">* retVal = [NSMutableArray new];")
  1021. ret_val = "std::vector<" + full_cpp_type + "> retValVector = "
  1022. self.build_cv2objc_epilogue(epilogue, vector_type, full_cpp_type, objc_type, "retValVector", "retVal")
  1023. elif "v_v_type" in type_dict[ret_type]:
  1024. objc_type = type_dict[ret_type]["objc_type"]
  1025. cpp_type = type_dict[ret_type]["v_v_type"]
  1026. if cpp_type.find("::") == -1:
  1027. cpp_type = self.get_namespace_prefix(cpp_type) + cpp_type
  1028. prologue.append("NSMutableArray<NSMutableArray<" + objc_type + ">*>* retVal = [NSMutableArray new];")
  1029. ret_val = "std::vector< std::vector<" + cpp_type + "> > retValVector = "
  1030. epilogue.append("CV2OBJC2(" + cpp_type + ", " + objc_type[:-1] + ", retValVector, retVal);")
  1031. elif ret_type.startswith("Ptr_"):
  1032. cpp_type = type_dict[ret_type]["c_type"]
  1033. real_cpp_type = type_dict[ret_type].get("real_c_type", cpp_type)
  1034. namespace_prefix = self.get_namespace_prefix(cpp_type)
  1035. ret_val = "cv::Ptr<" + namespace_prefix + real_cpp_type + "> retVal = "
  1036. ret = "return [" + type_dict[ret_type]["objc_type"][:-1] + " fromNative:retVal];"
  1037. elif ret_type == "void":
  1038. ret_val = ""
  1039. ret = ""
  1040. elif ret_type == "": # c-tor
  1041. constructor = True
  1042. ret_val = "return [self initWithNativePtr:cv::Ptr<" + fi.fullClass(isCPP=True) + ">(new "
  1043. tail = ")]"
  1044. ret = ""
  1045. elif self.isWrapped(ret_type): # wrapped class
  1046. namespace_prefix = self.get_namespace_prefix(ret_type)
  1047. ret_val = "cv::Ptr<" + namespace_prefix + ret_type + "> retVal = new " + namespace_prefix + ret_type + "("
  1048. tail = ")"
  1049. ret_type_dict = type_dict[ret_type]
  1050. from_cpp = ret_type_dict["from_cpp_ptr"] if "from_cpp_ptr" in ret_type_dict else ret_type_dict["from_cpp"]
  1051. ret = "return " + (from_cpp % { "n" : "retVal" }) + ";"
  1052. elif "from_cpp" in type_dict[ret_type]:
  1053. ret = "return " + (type_dict[ret_type]["from_cpp"] % { "n" : "retVal" }) + ";"
  1054. static = fi.static if fi.classname else True
  1055. objc_ret_type = type_dict[fi.ctype]["objc_type"] if type_dict[fi.ctype]["objc_type"] else "void" if not constructor else "instancetype"
  1056. if "v_type" in type_dict[ret_type]:
  1057. objc_ret_type = "NSArray<" + objc_ret_type + ">*"
  1058. elif "v_v_type" in type_dict[ret_type]:
  1059. objc_ret_type = "NSArray<NSArray<" + objc_ret_type + ">*>*"
  1060. prototype = Template("$static ($objc_ret_type)$objc_name$objc_args").substitute(
  1061. static = "+" if static else "-",
  1062. objc_ret_type = objc_ret_type,
  1063. objc_args = " ".join(objc_args),
  1064. objc_name = fi.objc_name if not constructor else ("init" + ("With" + (args[0].name[0].upper() + args[0].name[1:]) if len(args) > 0 else ""))
  1065. )
  1066. if fi.prolog is not None:
  1067. method_declarations.write("\n%s\n\n" % fi.prolog)
  1068. method_declarations.write( Template(
  1069. """$prototype$swift_name$deprecation_decl;
  1070. """
  1071. ).substitute(
  1072. prototype = prototype,
  1073. swift_name = " NS_SWIFT_NAME(" + fi.swift_name + "(" + build_swift_signature(args) + "))" if not constructor else "",
  1074. deprecation_decl = " DEPRECATED_ATTRIBUTE" if fi.deprecated else ""
  1075. )
  1076. )
  1077. if fi.epilog is not None:
  1078. method_declarations.write("%s\n\n" % fi.epilog)
  1079. method_implementations.write( Template(
  1080. """$prototype {$prologue
  1081. $ret_val$obj_deref$cv_name($cv_args)$tail;$epilogue$ret
  1082. }
  1083. """
  1084. ).substitute(
  1085. prototype = prototype,
  1086. ret = "\n " + ret if ret else "",
  1087. ret_val = ret_val,
  1088. prologue = "\n " + "\n ".join(prologue) if prologue else "",
  1089. epilogue = "\n " + "\n ".join(epilogue) if epilogue else "",
  1090. static = "+" if static else "-",
  1091. obj_deref = ("self." + ci.native_ptr_name + "->") if not static and not constructor else "",
  1092. cv_name = fi.cv_name if static else fi.fullClass(isCPP=True) if constructor else fi.name,
  1093. cv_args = ", ".join(cv_args),
  1094. tail = tail
  1095. )
  1096. )
  1097. if swift_ext:
  1098. prototype = build_swift_extension_decl(fi.swift_name, args, constructor, static, ret_type)
  1099. if not (ci.name, prototype) in extension_signatures and not (ci.base, prototype) in extension_signatures:
  1100. (pro, epi) = build_swift_logues(args)
  1101. extension_implementations.write( Template(
  1102. """public extension $classname {
  1103. $deprecation_decl$prototype {
  1104. $prologue
  1105. $unrefined_call$epilogue$ret
  1106. }
  1107. }
  1108. """
  1109. ).substitute(
  1110. classname = make_objcname(ci.name),
  1111. deprecation_decl = "@available(*, deprecated)\n " if fi.deprecated else "",
  1112. prototype = prototype,
  1113. prologue = " " + "\n ".join(pro),
  1114. unrefined_call = " " + build_unrefined_call(fi.swift_name, args, constructor, static, ci.name, ret_type is not None and ret_type != "void"),
  1115. epilogue = "\n " + "\n ".join(epi) if len(epi) > 0 else "",
  1116. ret = "\n return ret" if ret_type is not None and ret_type != "void" and not constructor else ""
  1117. )
  1118. )
  1119. extension_signatures.append((ci.name, prototype))
  1120. # adding method signature to dictionary
  1121. objc_signatures.append(objc_signature)
  1122. # processing args with default values
  1123. if args and args[-1].defval:
  1124. args.pop()
  1125. else:
  1126. break
  1127. def gen_class(self, ci, module, extension_implementations, extension_signatures):
  1128. logging.info("%s", ci)
  1129. additional_imports = []
  1130. if module in AdditionalImports:
  1131. if "*" in AdditionalImports[module]:
  1132. additional_imports += AdditionalImports[module]["*"]
  1133. if ci.name in AdditionalImports[module]:
  1134. additional_imports += AdditionalImports[module][ci.name]
  1135. if hasattr(ci, 'header_import'):
  1136. h = '"{}"'.format(ci.header_import)
  1137. if not h in additional_imports:
  1138. additional_imports.append(h)
  1139. h = '"{}.hpp"'.format(module)
  1140. if h in additional_imports:
  1141. additional_imports.remove(h)
  1142. h = '"opencv2/{}.hpp"'.format(module)
  1143. if not h in additional_imports:
  1144. additional_imports.insert(0, h)
  1145. if additional_imports:
  1146. ci.additionalImports.write('\n'.join(['#import %s' % make_objcname(h) for h in additional_imports]))
  1147. # constants
  1148. wrote_consts_pragma = False
  1149. consts_map = {c.name: c for c in ci.private_consts}
  1150. consts_map.update({c.name: c for c in ci.consts})
  1151. def const_value(v):
  1152. if v in consts_map:
  1153. target = consts_map[v]
  1154. assert target.value != v
  1155. return const_value(target.value)
  1156. return v
  1157. if ci.consts:
  1158. enumTypes = set([c.enumType for c in ci.consts])
  1159. grouped_consts = {enumType: [c for c in ci.consts if c.enumType == enumType] for enumType in enumTypes}
  1160. for typeName in sorted(grouped_consts.keys(), key=lambda x: str(x) if x is not None else ""):
  1161. consts = grouped_consts[typeName]
  1162. logging.info("%s", consts)
  1163. if typeName:
  1164. typeNameShort = typeName.rsplit(".", 1)[-1]
  1165. if ci.cname in enum_fix:
  1166. typeNameShort = enum_fix[ci.cname].get(typeNameShort, typeNameShort)
  1167. ci.enum_declarations.write("""
  1168. // C++: enum {1} ({2})
  1169. typedef NS_ENUM(int, {1}) {{
  1170. {0}\n}};\n\n""".format(
  1171. ",\n ".join(["%s = %s" % (c.name + (" NS_SWIFT_NAME(" + c.swift_name + ")" if c.swift_name else ""), c.value) for c in consts]),
  1172. typeNameShort, typeName)
  1173. )
  1174. else:
  1175. if not wrote_consts_pragma:
  1176. ci.method_declarations.write("#pragma mark - Class Constants\n\n")
  1177. wrote_consts_pragma = True
  1178. ci.method_declarations.write("""
  1179. {0}\n\n""".format("\n".join(["@property (class, readonly) int %s NS_SWIFT_NAME(%s);" % (c.name, c.name) for c in consts]))
  1180. )
  1181. declared_consts = []
  1182. match_alphabet = re.compile("[a-zA-Z]")
  1183. for c in consts:
  1184. value = str(c.value)
  1185. if match_alphabet.search(value):
  1186. for declared_const in sorted(declared_consts, key=len, reverse=True):
  1187. regex = re.compile("(?<!" + ci.cname + ".)" + declared_const)
  1188. value = regex.sub(ci.cname + "." + declared_const, value)
  1189. ci.method_implementations.write("+ (int)%s {\n return %s;\n}\n\n" % (c.name, value))
  1190. declared_consts.append(c.name)
  1191. ci.method_declarations.write("#pragma mark - Methods\n\n")
  1192. # methods
  1193. for fi in ci.getAllMethods():
  1194. self.gen_func(ci, fi, extension_implementations, extension_signatures)
  1195. # props
  1196. for pi in ci.props:
  1197. ci.method_declarations.write("\n //\n // C++: %s %s::%s\n //\n\n" % (pi.ctype, ci.fullName(isCPP=True), pi.name))
  1198. type_data = type_dict[pi.ctype] if pi.ctype != "uchar" else {"objc_type" : "unsigned char", "is_primitive" : True}
  1199. objc_type = type_data.get("objc_type", pi.ctype)
  1200. ci.addImports(pi.ctype, False)
  1201. ci.method_declarations.write("@property " + ("(readonly) " if not pi.rw else "") + objc_type + " " + pi.name + ";\n")
  1202. ptr_ref = "self." + ci.native_ptr_name + "->" if not ci.is_base_class else "self.nativePtr->"
  1203. if "v_type" in type_data:
  1204. vector_cpp_type = type_data["v_type"]
  1205. has_namespace = vector_cpp_type.find("::") != -1
  1206. vector_full_cpp_type = self.fullTypeName(vector_cpp_type) if not has_namespace else vector_cpp_type
  1207. ret_val = "std::vector<" + vector_full_cpp_type + "> retValVector = "
  1208. ci.method_implementations.write("-(NSArray<" + objc_type + ">*)" + pi.name + " {\n")
  1209. ci.method_implementations.write("\tNSMutableArray<" + objc_type + ">* retVal = [NSMutableArray new];\n")
  1210. ci.method_implementations.write("\t" + ret_val + ptr_ref + pi.name + ";\n")
  1211. epilogue = []
  1212. self.build_cv2objc_epilogue(epilogue, vector_cpp_type, vector_full_cpp_type, objc_type, "retValVector", "retVal")
  1213. ci.method_implementations.write("\t" + ("\n\t".join(epilogue)) + "\n")
  1214. ci.method_implementations.write("\treturn retVal;\n}\n\n")
  1215. elif "v_v_type" in type_data:
  1216. vector_cpp_type = type_data["v_v_type"]
  1217. has_namespace = vector_cpp_type.find("::") != -1
  1218. vector_full_cpp_type = self.fullTypeName(vector_cpp_type) if not has_namespace else vector_cpp_type
  1219. ret_val = "std::vector<std::vector<" + vector_full_cpp_type + ">> retValVectorVector = "
  1220. ci.method_implementations.write("-(NSArray<NSArray<" + objc_type + ">*>*)" + pi.name + " {\n")
  1221. ci.method_implementations.write("\tNSMutableArray<NSMutableArray<" + objc_type + ">*>* retVal = [NSMutableArray new];\n")
  1222. ci.method_implementations.write("\t" + ret_val + ptr_ref + pi.name + ";\n")
  1223. ci.method_implementations.write("\tCV2OBJC2(" + vector_full_cpp_type + ", " + objc_type[:-1] + ", retValVectorVector, retVal);\n")
  1224. ci.method_implementations.write("\treturn retVal;\n}\n\n")
  1225. elif self.isWrapped(pi.ctype): # wrapped class
  1226. namespace_prefix = self.get_namespace_prefix(pi.ctype)
  1227. ci.method_implementations.write("-(" + objc_type + ")" + pi.name + " {\n")
  1228. ci.method_implementations.write("\tcv::Ptr<" + namespace_prefix + pi.ctype + "> retVal = new " + namespace_prefix + pi.ctype + "(" + ptr_ref + pi.name + ");\n")
  1229. from_cpp = type_data["from_cpp_ptr"] if "from_cpp_ptr" in type_data else type_data["from_cpp"]
  1230. ci.method_implementations.write("\treturn " + (from_cpp % {"n": "retVal"}) + ";\n}\n\n")
  1231. else:
  1232. from_cpp = type_data.get("from_cpp", "%(n)s")
  1233. retVal = from_cpp % {"n": (ptr_ref + pi.name)}
  1234. ci.method_implementations.write("-(" + objc_type + ")" + pi.name + " {\n\treturn " + retVal + ";\n}\n\n")
  1235. if pi.rw:
  1236. if "v_type" in type_data:
  1237. vector_cpp_type = type_data["v_type"]
  1238. has_namespace = vector_cpp_type.find("::") != -1
  1239. vector_full_cpp_type = self.fullTypeName(vector_cpp_type) if not has_namespace else vector_cpp_type
  1240. ci.method_implementations.write("-(void)set" + pi.name[0].upper() + pi.name[1:] + ":(NSArray<" + objc_type + ">*)" + pi.name + "{\n")
  1241. prologue = []
  1242. self.build_objc2cv_prologue(prologue, vector_cpp_type, vector_full_cpp_type, objc_type, "valVector", pi.name)
  1243. ci.method_implementations.write("\t" + ("\n\t".join(prologue)) + "\n")
  1244. ci.method_implementations.write("\t" + ptr_ref + pi.name + " = valVector;\n}\n\n")
  1245. else:
  1246. to_cpp = type_data.get("to_cpp", ("(" + type_data.get("cast_to") + ")%(n)s") if "cast_to" in type_data else "%(n)s")
  1247. val = to_cpp % {"n": pi.name}
  1248. ci.method_implementations.write("-(void)set" + pi.name[0].upper() + pi.name[1:] + ":(" + objc_type + ")" + pi.name + " {\n\t" + ptr_ref + pi.name + " = " + val + ";\n}\n\n")
  1249. # manual ports
  1250. if ci.name in ManualFuncs:
  1251. for func in sorted(ManualFuncs[ci.name].keys()):
  1252. logging.info("manual function: %s", func)
  1253. fn = ManualFuncs[ci.name][func]
  1254. ci.method_declarations.write( "\n".join(fn["declaration"]) )
  1255. ci.method_implementations.write( "\n".join(fn["implementation"]) )
  1256. def getClass(self, classname):
  1257. return self.classes[classname or self.Module]
  1258. def isWrapped(self, classname):
  1259. name = classname or self.Module
  1260. return name in self.classes
  1261. def isSmartClass(self, ci):
  1262. '''
  1263. Check if class stores Ptr<T>* instead of T* in nativeObj field
  1264. '''
  1265. if ci.smart != None:
  1266. return ci.smart
  1267. # if parents are smart (we hope) then children are!
  1268. # if not we believe the class is smart if it has "create" method
  1269. ci.smart = False
  1270. if ci.base or ci.name == 'Algorithm':
  1271. ci.smart = True
  1272. else:
  1273. for fi in ci.methods:
  1274. if fi.name == "create":
  1275. ci.smart = True
  1276. break
  1277. return ci.smart
  1278. def smartWrap(self, ci, fullname):
  1279. '''
  1280. Wraps fullname with Ptr<> if needed
  1281. '''
  1282. if self.isSmartClass(ci):
  1283. return "Ptr<" + fullname + ">"
  1284. return fullname
  1285. def finalize(self, objc_target, output_objc_path, output_objc_build_path):
  1286. opencv_header_file = os.path.join(output_objc_path, framework_name + ".h")
  1287. opencv_header = "#import <Foundation/Foundation.h>\n\n"
  1288. opencv_header += "// ! Project version number\nFOUNDATION_EXPORT double " + framework_name + "VersionNumber;\n\n"
  1289. opencv_header += "// ! Project version string\nFOUNDATION_EXPORT const unsigned char " + framework_name + "VersionString[];\n\n"
  1290. opencv_header += "\n".join(["#define AVAILABLE_" + m['name'].upper() for m in config['modules']])
  1291. opencv_header += "\n\n"
  1292. opencv_header += "\n".join(["#import <" + framework_name + "/%s>" % os.path.basename(f) for f in self.header_files])
  1293. self.save(opencv_header_file, opencv_header)
  1294. opencv_modulemap_file = os.path.join(output_objc_path, framework_name + ".modulemap")
  1295. opencv_modulemap = "framework module " + framework_name + " {\n"
  1296. opencv_modulemap += " umbrella header \"" + framework_name + ".h\"\n"
  1297. opencv_modulemap += "\n".join([" header \"%s\"" % os.path.basename(f) for f in self.header_files])
  1298. opencv_modulemap += "\n export *\n module * {export *}\n}\n"
  1299. self.save(opencv_modulemap_file, opencv_modulemap)
  1300. available_modules = " ".join(["-DAVAILABLE_" + m['name'].upper() for m in config['modules']])
  1301. cmakelist_template = read_contents(os.path.join(SCRIPT_DIR, 'templates/cmakelists.template'))
  1302. cmakelist = Template(cmakelist_template).substitute(modules = ";".join(modules), framework = framework_name, objc_target=objc_target, module_availability_defines=available_modules)
  1303. self.save(os.path.join(dstdir, "CMakeLists.txt"), cmakelist)
  1304. mkdir_p(os.path.join(output_objc_build_path, "framework_build"))
  1305. mkdir_p(os.path.join(output_objc_build_path, "test_build"))
  1306. mkdir_p(os.path.join(output_objc_build_path, "doc_build"))
  1307. with open(os.path.join(SCRIPT_DIR, '../doc/README.md')) as readme_in:
  1308. readme_body = readme_in.read()
  1309. readme_body += "\n\n\n##Modules\n\n" + ", ".join(["`" + m.capitalize() + "`" for m in modules])
  1310. with open(os.path.join(output_objc_build_path, "doc_build/README.md"), "w") as readme_out:
  1311. readme_out.write(readme_body)
  1312. if framework_name != "OpenCV":
  1313. for dirname, dirs, files in os.walk(os.path.join(testdir, "test")):
  1314. if dirname.endswith('/resources'):
  1315. continue # don't touch resource binary files
  1316. for filename in files:
  1317. filepath = os.path.join(dirname, filename)
  1318. with io.open(filepath, encoding="utf-8", errors="ignore") as file:
  1319. body = file.read()
  1320. body = body.replace("import OpenCV", "import " + framework_name)
  1321. body = body.replace("#import <OpenCV/OpenCV.h>", "#import <" + framework_name + "/" + framework_name + ".h>")
  1322. with codecs.open(filepath, "w", "utf-8") as file:
  1323. file.write(body)
  1324. def copy_objc_files(objc_files_dir, objc_base_path, module_path, include = False):
  1325. global total_files, updated_files
  1326. objc_files = []
  1327. re_filter = re.compile(r'^.+\.(h|m|mm|swift)$')
  1328. for root, dirnames, filenames in os.walk(objc_files_dir):
  1329. objc_files += [os.path.join(root, filename) for filename in filenames if re_filter.match(filename)]
  1330. objc_files = [f.replace('\\', '/') for f in objc_files]
  1331. re_prefix = re.compile(r'^.+/(.+)\.(h|m|mm|swift)$')
  1332. for objc_file in objc_files:
  1333. src = objc_file
  1334. m = re_prefix.match(objc_file)
  1335. target_fname = (m.group(1) + '.' + m.group(2)) if m else os.path.basename(objc_file)
  1336. dest = os.path.join(objc_base_path, os.path.join(module_path, target_fname))
  1337. mkdir_p(os.path.dirname(dest))
  1338. total_files += 1
  1339. if include and m.group(2) == 'h':
  1340. generator.header_files.append(dest)
  1341. if (not os.path.exists(dest)) or (os.stat(src).st_mtime - os.stat(dest).st_mtime > 1):
  1342. copyfile(src, dest)
  1343. updated_files += 1
  1344. return objc_files
  1345. def unescape(str):
  1346. return str.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&")
  1347. def escape_underscore(str):
  1348. return str.replace('_', '\\_')
  1349. def escape_texttt(str):
  1350. return re.sub(re.compile('texttt{(.*?)}', re.DOTALL), lambda x: 'texttt{' + escape_underscore(x.group(1)) + '}', str)
  1351. def get_macros(tex):
  1352. out = ""
  1353. if re.search(r"\\fork\s*{", tex):
  1354. out += "\\newcommand{\\fork}[4]{ \\left\\{ \\begin{array}{l l} #1 & \\text{#2}\\\\\\\\ #3 & \\text{#4}\\\\\\\\ \\end{array} \\right.} "
  1355. if re.search(r"\\vecthreethree\s*{", tex):
  1356. out += "\\newcommand{\\vecthreethree}[9]{ \\begin{bmatrix} #1 & #2 & #3\\\\\\\\ #4 & #5 & #6\\\\\\\\ #7 & #8 & #9 \\end{bmatrix} } "
  1357. return out
  1358. def fix_tex(tex):
  1359. macros = get_macros(tex)
  1360. fix_escaping = escape_texttt(unescape(tex))
  1361. return macros + fix_escaping
  1362. def sanitize_documentation_string(doc, type):
  1363. if type == "class":
  1364. doc = doc.replace("@param ", "")
  1365. doc = re.sub(re.compile('`\\$\\$(.*?)\\$\\$`', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
  1366. doc = re.sub(re.compile('\\\\f\\{align\\*\\}\\{?(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$\\begin{aligned} ' + fix_tex(x.group(1)) + ' \\end{aligned}$$`', doc)
  1367. doc = re.sub(re.compile('\\\\f\\{equation\\*\\}\\{(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$\\begin{aligned} ' + fix_tex(x.group(1)) + ' \\end{aligned}$$`', doc)
  1368. doc = re.sub(re.compile('\\\\f\\$(.*?)\\\\f\\$', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
  1369. doc = re.sub(re.compile('\\\\f\\[(.*?)\\\\f\\]', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
  1370. doc = re.sub(re.compile('\\\\f\\{(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
  1371. doc = doc.replace("@anchor", "") \
  1372. .replace("@brief ", "").replace("\\brief ", "") \
  1373. .replace("@cite", "CITE:") \
  1374. .replace("@code{.cpp}", "<code>") \
  1375. .replace("@code{.txt}", "<code>") \
  1376. .replace("@code", "<code>") \
  1377. .replace("@copydoc", "") \
  1378. .replace("@copybrief", "") \
  1379. .replace("@date", "") \
  1380. .replace("@defgroup", "") \
  1381. .replace("@details ", "") \
  1382. .replace("@endcode", "</code>") \
  1383. .replace("@endinternal", "") \
  1384. .replace("@file", "") \
  1385. .replace("@include", "INCLUDE:") \
  1386. .replace("@ingroup", "") \
  1387. .replace("@internal", "") \
  1388. .replace("@overload", "") \
  1389. .replace("@param[in]", "@param") \
  1390. .replace("@param[out]", "@param") \
  1391. .replace("@ref", "REF:") \
  1392. .replace("@note", "NOTE:") \
  1393. .replace("@returns", "@return") \
  1394. .replace("@sa ", "@see ") \
  1395. .replace("@snippet", "SNIPPET:") \
  1396. .replace("@todo", "TODO:") \
  1397. lines = doc.splitlines()
  1398. in_code = False
  1399. for i,line in enumerate(lines):
  1400. if line.find("</code>") != -1:
  1401. in_code = False
  1402. lines[i] = line.replace("</code>", "")
  1403. if in_code:
  1404. lines[i] = unescape(line)
  1405. if line.find("<code>") != -1:
  1406. in_code = True
  1407. lines[i] = line.replace("<code>", "")
  1408. lines = list([x[x.find('*'):].strip() if x.lstrip().startswith("*") else x for x in lines])
  1409. lines = list(["* " + x[1:].strip() if x.startswith("*") and x != "*" else x for x in lines])
  1410. lines = list([x if x.startswith("*") else "* " + x if x and x != "*" else "*" for x in lines])
  1411. hasValues = False
  1412. for line in lines:
  1413. if line != "*":
  1414. hasValues = True
  1415. break
  1416. return "/**\n " + "\n ".join(lines) + "\n */" if hasValues else ""
  1417. if __name__ == "__main__":
  1418. # initialize logger
  1419. logging.basicConfig(filename='gen_objc.log', format=None, filemode='w', level=logging.INFO)
  1420. handler = logging.StreamHandler()
  1421. handler.setLevel(os.environ.get('LOG_LEVEL', logging.WARNING))
  1422. logging.getLogger().addHandler(handler)
  1423. # parse command line parameters
  1424. import argparse
  1425. arg_parser = argparse.ArgumentParser(description='OpenCV Objective-C Wrapper Generator')
  1426. arg_parser.add_argument('-p', '--parser', required=True, help='OpenCV header parser')
  1427. arg_parser.add_argument('-c', '--config', required=True, help='OpenCV modules config')
  1428. arg_parser.add_argument('-t', '--target', required=True, help='Target (either ios or osx or visionos)')
  1429. arg_parser.add_argument('-f', '--framework', required=True, help='Framework name')
  1430. args=arg_parser.parse_args()
  1431. # import header parser
  1432. hdr_parser_path = os.path.abspath(args.parser)
  1433. if hdr_parser_path.endswith(".py"):
  1434. hdr_parser_path = os.path.dirname(hdr_parser_path)
  1435. sys.path.append(hdr_parser_path)
  1436. import hdr_parser
  1437. with open(args.config) as f:
  1438. config = json.load(f)
  1439. ROOT_DIR = config['rootdir']; assert os.path.exists(ROOT_DIR)
  1440. if 'objc_build_dir' in config:
  1441. objc_build_dir = config['objc_build_dir']
  1442. assert os.path.exists(objc_build_dir), objc_build_dir
  1443. else:
  1444. objc_build_dir = os.getcwd()
  1445. dstdir = "./gen"
  1446. testdir = "./test"
  1447. objc_base_path = os.path.join(dstdir, 'objc'); mkdir_p(objc_base_path)
  1448. objc_test_base_path = testdir; mkdir_p(objc_test_base_path)
  1449. copy_objc_files(os.path.join(SCRIPT_DIR, '../test/test'), objc_test_base_path, 'test', False)
  1450. copy_objc_files(os.path.join(SCRIPT_DIR, '../test/dummy'), objc_test_base_path, 'dummy', False)
  1451. copyfile(os.path.join(SCRIPT_DIR, '../test/cmakelists.template'), os.path.join(objc_test_base_path, 'CMakeLists.txt'))
  1452. # launch Objective-C Wrapper generator
  1453. generator = ObjectiveCWrapperGenerator()
  1454. gen_dict_files = []
  1455. framework_name = args.framework
  1456. print("Objective-C: Processing OpenCV modules: %d" % len(config['modules']))
  1457. for e in config['modules']:
  1458. (module, module_location) = (e['name'], os.path.join(ROOT_DIR, e['location']))
  1459. logging.info("\n=== MODULE: %s (%s) ===\n" % (module, module_location))
  1460. modules.append(module)
  1461. module_imports = []
  1462. srcfiles = []
  1463. common_headers = []
  1464. misc_location = os.path.join(module_location, 'misc/objc')
  1465. srcfiles_fname = os.path.join(misc_location, 'filelist')
  1466. if os.path.exists(srcfiles_fname):
  1467. with open(srcfiles_fname) as f:
  1468. srcfiles = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()]
  1469. else:
  1470. re_bad = re.compile(r'(private|.inl.hpp$|_inl.hpp$|.detail.hpp$|.details.hpp$|_winrt.hpp$|/cuda/|/legacy/)')
  1471. # .h files before .hpp
  1472. h_files = []
  1473. hpp_files = []
  1474. for root, dirnames, filenames in os.walk(os.path.join(module_location, 'include')):
  1475. h_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.h')]
  1476. hpp_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.hpp')]
  1477. srcfiles = h_files + hpp_files
  1478. # Use relative paths to avoid being affected by the name of the parent directory.
  1479. # See https://github.com/opencv/opencv/issues/26712
  1480. srcfiles = [f for f in srcfiles if not re_bad.search(os.path.relpath(f, module_location).replace('\\', '/'))]
  1481. logging.info("\nFiles (%d):\n%s", len(srcfiles), pformat(srcfiles))
  1482. common_headers_fname = os.path.join(misc_location, 'filelist_common')
  1483. if os.path.exists(common_headers_fname):
  1484. with open(common_headers_fname) as f:
  1485. common_headers = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()]
  1486. logging.info("\nCommon headers (%d):\n%s", len(common_headers), pformat(common_headers))
  1487. gendict_fname = os.path.join(misc_location, 'gen_dict.json')
  1488. module_source_map = {}
  1489. if os.path.exists(gendict_fname):
  1490. with open(gendict_fname) as f:
  1491. gen_type_dict = json.load(f)
  1492. namespace_ignore_list = gen_type_dict.get("namespace_ignore_list", [])
  1493. class_ignore_list += gen_type_dict.get("class_ignore_list", [])
  1494. enum_ignore_list += gen_type_dict.get("enum_ignore_list", [])
  1495. const_ignore_list += gen_type_dict.get("const_ignore_list", [])
  1496. const_private_list += gen_type_dict.get("const_private_list", [])
  1497. missing_consts.update(gen_type_dict.get("missing_consts", {}))
  1498. type_dict.update(gen_type_dict.get("type_dict", {}))
  1499. AdditionalImports[module] = gen_type_dict.get("AdditionalImports", {})
  1500. ManualFuncs.update(gen_type_dict.get("ManualFuncs", {}))
  1501. func_arg_fix.update(gen_type_dict.get("func_arg_fix", {}))
  1502. header_fix.update(gen_type_dict.get("header_fix", {}))
  1503. enum_fix.update(gen_type_dict.get("enum_fix", {}))
  1504. const_fix.update(gen_type_dict.get("const_fix", {}))
  1505. module_source_map = gen_type_dict.get("SourceMap", {})
  1506. namespaces_dict.update(gen_type_dict.get("namespaces_dict", {}))
  1507. module_imports += gen_type_dict.get("module_imports", [])
  1508. objc_files_dir = os.path.join(misc_location, 'common')
  1509. copied_files = []
  1510. if os.path.exists(objc_files_dir):
  1511. copied_files += copy_objc_files(objc_files_dir, objc_base_path, module, True)
  1512. target_path = 'macosx' if args.target == 'osx' else module_source_map.get(args.target, args.target)
  1513. target_files_dir = os.path.join(misc_location, target_path)
  1514. if os.path.exists(target_files_dir):
  1515. copied_files += copy_objc_files(target_files_dir, objc_base_path, module, True)
  1516. objc_test_files_dir = os.path.join(misc_location, 'test')
  1517. if os.path.exists(objc_test_files_dir):
  1518. copy_objc_files(objc_test_files_dir, objc_test_base_path, 'test', False)
  1519. objc_test_resources_dir = os.path.join(objc_test_files_dir, 'resources')
  1520. if os.path.exists(objc_test_resources_dir):
  1521. copy_tree(objc_test_resources_dir, os.path.join(objc_test_base_path, 'test', 'resources'))
  1522. manual_classes = [x for x in [x[x.rfind('/')+1:-2] for x in [x for x in copied_files if x.endswith('.h')]] if x in type_dict]
  1523. if len(srcfiles) > 0:
  1524. generator.gen(srcfiles, module, dstdir, objc_base_path,
  1525. common_headers, manual_classes,
  1526. config.get("preprocessor_definitions"))
  1527. else:
  1528. logging.info("No generated code for module: %s", module)
  1529. generator.finalize(args.target, objc_base_path, objc_build_dir)
  1530. print('Generated files: %d (updated %d)' % (total_files, updated_files))