gen_objc.py 78 KB

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