mirror of
https://github.com/opencv/opencv.git
synced 2024-12-15 09:49:13 +08:00
3d9cb5329c
Add experimental support for Apple VisionOS platform #24136 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch This is dependent on cmake support for VisionOs which is currently in progress. Creating PR now to test that there are no regressions in iOS and macOS builds
1721 lines
80 KiB
Python
Executable File
1721 lines
80 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
from __future__ import print_function, unicode_literals
|
|
import sys, re, os.path, errno, fnmatch
|
|
import json
|
|
import logging
|
|
import codecs
|
|
import io
|
|
from shutil import copyfile
|
|
from pprint import pformat
|
|
from string import Template
|
|
|
|
if sys.version_info >= (3, 8): # Python 3.8+
|
|
from shutil import copytree
|
|
def copy_tree(src, dst):
|
|
copytree(src, dst, dirs_exist_ok=True)
|
|
else:
|
|
from distutils.dir_util import copy_tree
|
|
|
|
try:
|
|
from io import StringIO # Python 3
|
|
except:
|
|
from io import BytesIO as StringIO
|
|
|
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
# list of modules
|
|
config = None
|
|
ROOT_DIR = None
|
|
|
|
total_files = 0
|
|
updated_files = 0
|
|
|
|
module_imports = []
|
|
|
|
# list of namespaces, which should be skipped by wrapper generator
|
|
# the list is loaded from misc/objc/gen_dict.json defined for the module only
|
|
namespace_ignore_list = []
|
|
|
|
# list of class names, which should be skipped by wrapper generator
|
|
# the list is loaded from misc/objc/gen_dict.json defined for the module and its dependencies
|
|
class_ignore_list = []
|
|
|
|
|
|
# list of enum names, which should be skipped by wrapper generator
|
|
enum_ignore_list = []
|
|
|
|
# list of constant names, which should be skipped by wrapper generator
|
|
# ignored constants can be defined using regular expressions
|
|
const_ignore_list = []
|
|
|
|
# list of private constants
|
|
const_private_list = []
|
|
|
|
# { Module : { public : [[name, val],...], private : [[]...] } }
|
|
missing_consts = {}
|
|
|
|
type_dict = {
|
|
"" : {"objc_type" : ""}, # c-tor ret_type
|
|
"void" : {"objc_type" : "void", "is_primitive" : True, "swift_type": "Void"},
|
|
"bool" : {"objc_type" : "BOOL", "is_primitive" : True, "to_cpp": "(bool)%(n)s", "swift_type": "Bool"},
|
|
"char" : {"objc_type" : "char", "is_primitive" : True, "swift_type": "Int8"},
|
|
"int" : {"objc_type" : "int", "is_primitive" : True, "out_type" : "int*", "out_type_ptr": "%(n)s", "out_type_ref": "*(int*)(%(n)s)", "swift_type": "Int32"},
|
|
"long" : {"objc_type" : "long", "is_primitive" : True, "swift_type": "Int"},
|
|
"float" : {"objc_type" : "float", "is_primitive" : True, "out_type" : "float*", "out_type_ptr": "%(n)s", "out_type_ref": "*(float*)(%(n)s)", "swift_type": "Float"},
|
|
"double" : {"objc_type" : "double", "is_primitive" : True, "out_type" : "double*", "out_type_ptr": "%(n)s", "out_type_ref": "*(double*)(%(n)s)", "swift_type": "Double"},
|
|
"size_t" : {"objc_type" : "size_t", "is_primitive" : True},
|
|
"int64" : {"objc_type" : "long", "is_primitive" : True, "swift_type": "Int"},
|
|
"string" : {"objc_type" : "NSString*", "is_primitive" : True, "from_cpp": "[NSString stringWithUTF8String:%(n)s.c_str()]", "cast_to": "std::string", "swift_type": "String"}
|
|
}
|
|
|
|
# Defines a rule to add extra prefixes for names from specific namespaces.
|
|
# In example, cv::fisheye::stereoRectify from namespace fisheye is wrapped as fisheye_stereoRectify
|
|
namespaces_dict = {}
|
|
|
|
# { module: { class | "*" : [ header ]} }
|
|
AdditionalImports = {}
|
|
|
|
# { class : { func : {declaration, implementation} } }
|
|
ManualFuncs = {}
|
|
|
|
# { class : { func : { arg_name : {"ctype" : ctype, "attrib" : [attrib]} } } }
|
|
func_arg_fix = {}
|
|
|
|
# { class : { func : { prolog : "", epilog : "" } } }
|
|
header_fix = {}
|
|
|
|
# { class : { enum: fixed_enum } }
|
|
enum_fix = {}
|
|
|
|
# { class : { enum: { const: fixed_const} } }
|
|
const_fix = {}
|
|
|
|
# { (class, func) : objc_signature }
|
|
method_dict = {
|
|
("Mat", "convertTo") : "-convertTo:rtype:alpha:beta:",
|
|
("Mat", "setTo") : "-setToScalar:mask:",
|
|
("Mat", "zeros") : "+zeros:cols:type:",
|
|
("Mat", "ones") : "+ones:cols:type:",
|
|
("Mat", "dot") : "-dot:"
|
|
}
|
|
|
|
modules = []
|
|
|
|
|
|
class SkipSymbolException(Exception):
|
|
def __init__(self, text):
|
|
self.t = text
|
|
def __str__(self):
|
|
return self.t
|
|
|
|
|
|
def read_contents(fname):
|
|
with open(fname, 'r') as f:
|
|
data = f.read()
|
|
return data
|
|
|
|
def mkdir_p(path):
|
|
''' mkdir -p '''
|
|
try:
|
|
os.makedirs(path)
|
|
except OSError as exc:
|
|
if exc.errno == errno.EEXIST and os.path.isdir(path):
|
|
pass
|
|
else:
|
|
raise
|
|
|
|
def header_import(hdr):
|
|
""" converts absolute header path to import parameter """
|
|
pos = hdr.find('/include/')
|
|
hdr = hdr[pos+9 if pos >= 0 else 0:]
|
|
#pos = hdr.find('opencv2/')
|
|
#hdr = hdr[pos+8 if pos >= 0 else 0:]
|
|
return hdr
|
|
|
|
def make_objcname(m):
|
|
return "Cv"+m if (m[0] in "0123456789") else m
|
|
|
|
def make_objcmodule(m):
|
|
return "cv"+m if (m[0] in "0123456789") else m
|
|
|
|
T_OBJC_CLASS_HEADER = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_class_header.template'))
|
|
T_OBJC_CLASS_BODY = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_class_body.template'))
|
|
T_OBJC_MODULE_HEADER = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_module_header.template'))
|
|
T_OBJC_MODULE_BODY = read_contents(os.path.join(SCRIPT_DIR, 'templates/objc_module_body.template'))
|
|
|
|
class GeneralInfo():
|
|
def __init__(self, type, decl, namespaces):
|
|
self.symbol_id, self.namespace, self.classpath, self.classname, self.name = self.parseName(decl[0], namespaces)
|
|
|
|
for ns_ignore in namespace_ignore_list:
|
|
if self.symbol_id.startswith(ns_ignore + '.'):
|
|
raise SkipSymbolException('ignored namespace ({}): {}'.format(ns_ignore, self.symbol_id))
|
|
|
|
# parse doxygen comments
|
|
self.params={}
|
|
|
|
self.deprecated = False
|
|
if type == "class":
|
|
docstring = "// C++: class " + self.name + "\n"
|
|
else:
|
|
docstring=""
|
|
|
|
if len(decl)>5 and decl[5]:
|
|
doc = decl[5]
|
|
|
|
if re.search("(@|\\\\)deprecated", doc):
|
|
self.deprecated = True
|
|
|
|
docstring += sanitize_documentation_string(doc, type)
|
|
elif type == "class":
|
|
docstring += "/**\n * The " + self.name + " module\n */\n"
|
|
|
|
self.docstring = docstring
|
|
|
|
def parseName(self, name, namespaces):
|
|
'''
|
|
input: full name and available namespaces
|
|
returns: (namespace, classpath, classname, name)
|
|
'''
|
|
name = name[name.find(" ")+1:].strip() # remove struct/class/const prefix
|
|
spaceName = ""
|
|
localName = name # <classes>.<name>
|
|
for namespace in sorted(namespaces, key=len, reverse=True):
|
|
if name.startswith(namespace + "."):
|
|
spaceName = namespace
|
|
localName = name.replace(namespace + ".", "")
|
|
break
|
|
pieces = localName.split(".")
|
|
if len(pieces) > 2: # <class>.<class>.<class>.<name>
|
|
return name, spaceName, ".".join(pieces[:-1]), pieces[-2], pieces[-1]
|
|
elif len(pieces) == 2: # <class>.<name>
|
|
return name, spaceName, pieces[0], pieces[0], pieces[1]
|
|
elif len(pieces) == 1: # <name>
|
|
return name, spaceName, "", "", pieces[0]
|
|
else:
|
|
return name, spaceName, "", "" # error?!
|
|
|
|
def fullName(self, isCPP=False):
|
|
result = ".".join([self.fullClass(), self.name])
|
|
return result if not isCPP else get_cname(result)
|
|
|
|
def fullClass(self, isCPP=False):
|
|
result = ".".join([f for f in [self.namespace] + self.classpath.split(".") if len(f)>0])
|
|
return result if not isCPP else get_cname(result)
|
|
|
|
class ConstInfo(GeneralInfo):
|
|
def __init__(self, decl, addedManually=False, namespaces=[], enumType=None):
|
|
GeneralInfo.__init__(self, "const", decl, namespaces)
|
|
self.cname = get_cname(self.name)
|
|
self.swift_name = None
|
|
self.value = decl[1]
|
|
self.enumType = enumType
|
|
self.addedManually = addedManually
|
|
if self.namespace in namespaces_dict:
|
|
self.name = '%s_%s' % (namespaces_dict[self.namespace], self.name)
|
|
|
|
def __repr__(self):
|
|
return Template("CONST $name=$value$manual").substitute(name=self.name,
|
|
value=self.value,
|
|
manual="(manual)" if self.addedManually else "")
|
|
|
|
def isIgnored(self):
|
|
for c in const_ignore_list:
|
|
if re.match(c, self.name):
|
|
return True
|
|
return False
|
|
|
|
def normalize_field_name(name):
|
|
return name.replace(".","_").replace("[","").replace("]","").replace("_getNativeObjAddr()","_nativeObj")
|
|
|
|
def normalize_class_name(name):
|
|
return re.sub(r"^cv\.", "", name).replace(".", "_")
|
|
|
|
def get_cname(name):
|
|
return name.replace(".", "::")
|
|
|
|
def cast_from(t):
|
|
if t in type_dict and "cast_from" in type_dict[t]:
|
|
return type_dict[t]["cast_from"]
|
|
return t
|
|
|
|
def cast_to(t):
|
|
if t in type_dict and "cast_to" in type_dict[t]:
|
|
return type_dict[t]["cast_to"]
|
|
return t
|
|
|
|
def gen_class_doc(docstring, module, members, enums):
|
|
lines = docstring.splitlines()
|
|
lines.insert(len(lines)-1, " *")
|
|
if len(members) > 0:
|
|
lines.insert(len(lines)-1, " * Member classes: " + ", ".join([("`" + m + "`") for m in members]))
|
|
lines.insert(len(lines)-1, " *")
|
|
else:
|
|
lines.insert(len(lines)-1, " * Member of `" + module + "`")
|
|
if len(enums) > 0:
|
|
lines.insert(len(lines)-1, " * Member enums: " + ", ".join([("`" + m + "`") for m in enums]))
|
|
|
|
return "\n".join(lines)
|
|
|
|
class ClassPropInfo():
|
|
def __init__(self, decl): # [f_ctype, f_name, '', '/RW']
|
|
self.ctype = decl[0]
|
|
self.name = decl[1]
|
|
self.rw = "/RW" in decl[3]
|
|
|
|
def __repr__(self):
|
|
return Template("PROP $ctype $name").substitute(ctype=self.ctype, name=self.name)
|
|
|
|
class ClassInfo(GeneralInfo):
|
|
def __init__(self, decl, namespaces=[]): # [ 'class/struct cname', ': base', [modlist] ]
|
|
GeneralInfo.__init__(self, "class", decl, namespaces)
|
|
self.cname = self.name if not self.classname else self.classname + "_" + self.name
|
|
self.real_cname = self.name if not self.classname else self.classname + "::" + self.name
|
|
self.methods = []
|
|
self.methods_suffixes = {}
|
|
self.consts = [] # using a list to save the occurrence order
|
|
self.private_consts = []
|
|
self.imports = set()
|
|
self.props= []
|
|
self.objc_name = self.name if not self.classname else self.classname + self.name
|
|
self.smart = None # True if class stores Ptr<T>* instead of T* in nativeObj field
|
|
self.additionalImports = None # additional import files
|
|
self.enum_declarations = None # Objective-C enum declarations stream
|
|
self.method_declarations = None # Objective-C method declarations stream
|
|
self.method_implementations = None # Objective-C method implementations stream
|
|
self.objc_header_template = None # Objective-C header code
|
|
self.objc_body_template = None # Objective-C body code
|
|
for m in decl[2]:
|
|
if m.startswith("="):
|
|
self.objc_name = m[1:]
|
|
self.base = ''
|
|
self.is_base_class = True
|
|
self.native_ptr_name = "nativePtr"
|
|
self.member_classes = [] # Only relevant for modules
|
|
self.member_enums = [] # Only relevant for modules
|
|
if decl[1]:
|
|
self.base = re.sub(r"^.*:", "", decl[1].split(",")[0]).strip()
|
|
if self.base:
|
|
self.is_base_class = False
|
|
self.native_ptr_name = "nativePtr" + self.objc_name
|
|
|
|
def __repr__(self):
|
|
return Template("CLASS $namespace::$classpath.$name : $base").substitute(**self.__dict__)
|
|
|
|
def getImports(self, module):
|
|
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])]
|
|
|
|
def isEnum(self, c):
|
|
return c in type_dict and type_dict[c].get("is_enum", False)
|
|
|
|
def getForwardDeclarations(self, module):
|
|
enum_decl = [x for x in self.imports if self.isEnum(x) and type_dict[x]["import_module"] != module]
|
|
enum_imports = sorted(list(set([type_dict[m]["import_module"] for m in enum_decl])))
|
|
class_decl = [x for x in self.imports if not self.isEnum(x)]
|
|
return ["#import \"%s.h\"" % make_objcname(c) for c in enum_imports] + [""] + ["@class %s;" % c for c in sorted(class_decl)]
|
|
|
|
def addImports(self, ctype, is_out_type):
|
|
if ctype == self.cname:
|
|
return
|
|
if ctype in type_dict:
|
|
objc_import = None
|
|
if "v_type" in type_dict[ctype]:
|
|
objc_import = type_dict[type_dict[ctype]["v_type"]]["objc_type"]
|
|
elif "v_v_type" in type_dict[ctype]:
|
|
objc_import = type_dict[type_dict[ctype]["v_v_type"]]["objc_type"]
|
|
elif not type_dict[ctype].get("is_primitive", False):
|
|
objc_import = type_dict[ctype]["objc_type"]
|
|
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)):
|
|
objc_import = objc_import[:-1] if objc_import[-1] == "*" else objc_import # remove trailing "*"
|
|
if objc_import != self.cname:
|
|
self.imports.add(objc_import) # remove trailing "*"
|
|
|
|
def getAllMethods(self):
|
|
result = []
|
|
result += [fi for fi in self.methods if fi.isconstructor]
|
|
result += [fi for fi in self.methods if not fi.isconstructor]
|
|
return result
|
|
|
|
def addMethod(self, fi):
|
|
self.methods.append(fi)
|
|
|
|
def getConst(self, name):
|
|
for cand in self.consts + self.private_consts:
|
|
if cand.name == name:
|
|
return cand
|
|
return None
|
|
|
|
def addConst(self, constinfo):
|
|
# choose right list (public or private)
|
|
consts = self.consts
|
|
for c in const_private_list:
|
|
if re.match(c, constinfo.name):
|
|
consts = self.private_consts
|
|
break
|
|
consts.append(constinfo)
|
|
|
|
def initCodeStreams(self, Module):
|
|
self.additionalImports = StringIO()
|
|
self.enum_declarations = StringIO()
|
|
self.method_declarations = StringIO()
|
|
self.method_implementations = StringIO()
|
|
if self.base:
|
|
self.objc_header_template = T_OBJC_CLASS_HEADER
|
|
self.objc_body_template = T_OBJC_CLASS_BODY
|
|
else:
|
|
self.base = "NSObject"
|
|
if self.name != Module:
|
|
self.objc_header_template = T_OBJC_CLASS_HEADER
|
|
self.objc_body_template = T_OBJC_CLASS_BODY
|
|
else:
|
|
self.objc_header_template = T_OBJC_MODULE_HEADER
|
|
self.objc_body_template = T_OBJC_MODULE_BODY
|
|
# misc handling
|
|
if self.name == Module:
|
|
for i in module_imports or []:
|
|
self.imports.add(i)
|
|
|
|
def cleanupCodeStreams(self):
|
|
self.additionalImports.close()
|
|
self.enum_declarations.close()
|
|
self.method_declarations.close()
|
|
self.method_implementations.close()
|
|
|
|
def generateObjcHeaderCode(self, m, M, objcM):
|
|
return Template(self.objc_header_template + "\n\n").substitute(
|
|
module = M,
|
|
additionalImports = self.additionalImports.getvalue(),
|
|
importBaseClass = '#import "' + make_objcname(self.base) + '.h"' if not self.is_base_class else "",
|
|
forwardDeclarations = "\n".join([_f for _f in self.getForwardDeclarations(objcM) if _f]),
|
|
enumDeclarations = self.enum_declarations.getvalue(),
|
|
nativePointerHandling = Template(
|
|
"""
|
|
#ifdef __cplusplus
|
|
@property(readonly)cv::Ptr<$cName> $native_ptr_name;
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
- (instancetype)initWithNativePtr:(cv::Ptr<$cName>)nativePtr;
|
|
+ (instancetype)fromNative:(cv::Ptr<$cName>)nativePtr;
|
|
#endif
|
|
"""
|
|
).substitute(
|
|
cName = self.fullName(isCPP=True),
|
|
native_ptr_name = self.native_ptr_name
|
|
),
|
|
manualMethodDeclations = "",
|
|
methodDeclarations = self.method_declarations.getvalue(),
|
|
name = self.name,
|
|
objcName = make_objcname(self.objc_name),
|
|
cName = self.cname,
|
|
imports = "\n".join(self.getImports(M)),
|
|
docs = gen_class_doc(self.docstring, M, self.member_classes, self.member_enums),
|
|
base = self.base)
|
|
|
|
def generateObjcBodyCode(self, m, M):
|
|
return Template(self.objc_body_template + "\n\n").substitute(
|
|
module = M,
|
|
nativePointerHandling=Template(
|
|
"""
|
|
- (instancetype)initWithNativePtr:(cv::Ptr<$cName>)nativePtr {
|
|
self = [super $init_call];
|
|
if (self) {
|
|
_$native_ptr_name = nativePtr;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
+ (instancetype)fromNative:(cv::Ptr<$cName>)nativePtr {
|
|
return [[$objcName alloc] initWithNativePtr:nativePtr];
|
|
}
|
|
"""
|
|
).substitute(
|
|
cName = self.fullName(isCPP=True),
|
|
objcName = self.objc_name,
|
|
native_ptr_name = self.native_ptr_name,
|
|
init_call = "init" if self.is_base_class else "initWithNativePtr:nativePtr"
|
|
),
|
|
manualMethodDeclations = "",
|
|
methodImplementations = self.method_implementations.getvalue(),
|
|
name = self.name,
|
|
objcName = self.objc_name,
|
|
cName = self.cname,
|
|
imports = "\n".join(self.getImports(M)),
|
|
docs = gen_class_doc(self.docstring, M, self.member_classes, self.member_enums),
|
|
base = self.base)
|
|
|
|
class ArgInfo():
|
|
def __init__(self, arg_tuple): # [ ctype, name, def val, [mod], argno ]
|
|
self.pointer = False
|
|
ctype = arg_tuple[0]
|
|
if ctype.endswith("*"):
|
|
ctype = ctype[:-1]
|
|
self.pointer = True
|
|
self.ctype = ctype
|
|
self.name = arg_tuple[1]
|
|
self.defval = arg_tuple[2]
|
|
self.out = ""
|
|
if "/O" in arg_tuple[3]:
|
|
self.out = "O"
|
|
if "/IO" in arg_tuple[3]:
|
|
self.out = "IO"
|
|
|
|
def __repr__(self):
|
|
return Template("ARG $ctype$p $name=$defval").substitute(ctype=self.ctype,
|
|
p=" *" if self.pointer else "",
|
|
name=self.name,
|
|
defval=self.defval)
|
|
|
|
class FuncInfo(GeneralInfo):
|
|
def __init__(self, decl, module, namespaces=[]): # [ funcname, return_ctype, [modifiers], [args] ]
|
|
GeneralInfo.__init__(self, "func", decl, namespaces)
|
|
self.cname = get_cname(decl[0])
|
|
nested_type = self.classpath.find(".") != -1
|
|
self.objc_name = self.name if not nested_type else self.classpath.replace(".", "")
|
|
self.classname = self.classname if not nested_type else self.classpath.replace(".", "_")
|
|
self.swift_name = self.name
|
|
self.cv_name = self.fullName(isCPP=True)
|
|
self.isconstructor = self.name == self.classname
|
|
if "[" in self.name:
|
|
self.objc_name = "getelem"
|
|
if self.namespace in namespaces_dict:
|
|
self.objc_name = '%s_%s' % (namespaces_dict[self.namespace], self.objc_name)
|
|
for m in decl[2]:
|
|
if m.startswith("="):
|
|
self.objc_name = m[1:]
|
|
self.static = ["","static"][ "/S" in decl[2] ]
|
|
self.ctype = re.sub(r"^CvTermCriteria", "TermCriteria", decl[1] or "")
|
|
self.args = []
|
|
func_fix_map = func_arg_fix.get(self.classname or module, {}).get(self.objc_name, {})
|
|
header_fixes = header_fix.get(self.classname or module, {}).get(self.objc_name, {})
|
|
self.prolog = header_fixes.get('prolog', None)
|
|
self.epilog = header_fixes.get('epilog', None)
|
|
for a in decl[3]:
|
|
arg = a[:]
|
|
arg_fix_map = func_fix_map.get(arg[1], {})
|
|
arg[0] = arg_fix_map.get('ctype', arg[0]) #fixing arg type
|
|
arg[2] = arg_fix_map.get('defval', arg[2]) #fixing arg defval
|
|
arg[3] = arg_fix_map.get('attrib', arg[3]) #fixing arg attrib
|
|
self.args.append(ArgInfo(arg))
|
|
|
|
if type_complete(self.args, self.ctype):
|
|
func_fix_map = func_arg_fix.get(self.classname or module, {}).get(self.signature(self.args), {})
|
|
name_fix_map = func_fix_map.get(self.name, {})
|
|
self.objc_name = name_fix_map.get('name', self.objc_name)
|
|
self.swift_name = name_fix_map.get('swift_name', self.swift_name)
|
|
for arg in self.args:
|
|
arg_fix_map = func_fix_map.get(arg.name, {})
|
|
arg.ctype = arg_fix_map.get('ctype', arg.ctype) #fixing arg type
|
|
arg.defval = arg_fix_map.get('defval', arg.defval) #fixing arg type
|
|
arg.name = arg_fix_map.get('name', arg.name) #fixing arg name
|
|
|
|
def __repr__(self):
|
|
return Template("FUNC <$ctype $namespace.$classpath.$name $args>").substitute(**self.__dict__)
|
|
|
|
def __lt__(self, other):
|
|
return self.__repr__() < other.__repr__()
|
|
|
|
def signature(self, args):
|
|
objc_args = build_objc_args(args)
|
|
return "(" + type_dict[self.ctype]["objc_type"] + ")" + self.objc_name + " ".join(objc_args)
|
|
|
|
def type_complete(args, ctype):
|
|
for a in args:
|
|
if a.ctype not in type_dict:
|
|
if not a.defval and a.ctype.endswith("*"):
|
|
a.defval = 0
|
|
if a.defval:
|
|
a.ctype = ''
|
|
continue
|
|
return False
|
|
if ctype not in type_dict:
|
|
return False
|
|
return True
|
|
|
|
def build_objc_args(args):
|
|
objc_args = []
|
|
for a in args:
|
|
if a.ctype not in type_dict:
|
|
if not a.defval and a.ctype.endswith("*"):
|
|
a.defval = 0
|
|
if a.defval:
|
|
a.ctype = ''
|
|
continue
|
|
if not a.ctype: # hidden
|
|
continue
|
|
objc_type = type_dict[a.ctype]["objc_type"]
|
|
if "v_type" in type_dict[a.ctype]:
|
|
if "O" in a.out:
|
|
objc_type = "NSMutableArray<" + objc_type + ">*"
|
|
else:
|
|
objc_type = "NSArray<" + objc_type + ">*"
|
|
elif "v_v_type" in type_dict[a.ctype]:
|
|
if "O" in a.out:
|
|
objc_type = "NSMutableArray<NSMutableArray<" + objc_type + ">*>*"
|
|
else:
|
|
objc_type = "NSArray<NSArray<" + objc_type + ">*>*"
|
|
|
|
if a.out and type_dict[a.ctype].get("out_type", ""):
|
|
objc_type = type_dict[a.ctype]["out_type"]
|
|
objc_args.append((a.name if len(objc_args) > 0 else '') + ':(' + objc_type + ')' + a.name)
|
|
return objc_args
|
|
|
|
def build_objc_method_name(args):
|
|
objc_method_name = ""
|
|
for a in args[1:]:
|
|
if a.ctype not in type_dict:
|
|
if not a.defval and a.ctype.endswith("*"):
|
|
a.defval = 0
|
|
if a.defval:
|
|
a.ctype = ''
|
|
continue
|
|
if not a.ctype: # hidden
|
|
continue
|
|
objc_method_name += a.name + ":"
|
|
return objc_method_name
|
|
|
|
def get_swift_type(ctype):
|
|
has_swift_type = "swift_type" in type_dict[ctype]
|
|
swift_type = type_dict[ctype]["swift_type"] if has_swift_type else type_dict[ctype]["objc_type"]
|
|
if swift_type[-1:] == "*":
|
|
swift_type = swift_type[:-1]
|
|
if not has_swift_type:
|
|
if "v_type" in type_dict[ctype]:
|
|
swift_type = "[" + swift_type + "]"
|
|
elif "v_v_type" in type_dict[ctype]:
|
|
swift_type = "[[" + swift_type + "]]"
|
|
return swift_type
|
|
|
|
def build_swift_extension_decl(name, args, constructor, static, ret_type):
|
|
extension_decl = "@nonobjc " + ("class " if static else "") + (("func " + name) if not constructor else "convenience init") + "("
|
|
swift_args = []
|
|
for a in args:
|
|
if a.ctype not in type_dict:
|
|
if not a.defval and a.ctype.endswith("*"):
|
|
a.defval = 0
|
|
if a.defval:
|
|
a.ctype = ''
|
|
continue
|
|
if not a.ctype: # hidden
|
|
continue
|
|
swift_type = get_swift_type(a.ctype)
|
|
|
|
if "O" in a.out:
|
|
if type_dict[a.ctype].get("primitive_type", False):
|
|
swift_type = "UnsafeMutablePointer<" + swift_type + ">"
|
|
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):
|
|
swift_type = "inout " + swift_type
|
|
|
|
swift_args.append(a.name + ': ' + swift_type)
|
|
|
|
extension_decl += ", ".join(swift_args) + ")"
|
|
if ret_type:
|
|
extension_decl += " -> " + get_swift_type(ret_type)
|
|
return extension_decl
|
|
|
|
def extension_arg(a):
|
|
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))
|
|
|
|
def extension_tmp_arg(a):
|
|
if a.ctype in type_dict:
|
|
if type_dict[a.ctype].get("primitive_vector", False) or type_dict[a.ctype].get("primitive_vector_vector", False):
|
|
return a.name + "Vector"
|
|
elif ("v_type" in type_dict[a.ctype] or "v_v_type" in type_dict[a.ctype]) and "O" in a.out:
|
|
return a.name + "Array"
|
|
return a.name
|
|
|
|
def make_swift_extension(args):
|
|
for a in args:
|
|
if extension_arg(a):
|
|
return True
|
|
return False
|
|
|
|
def build_swift_signature(args):
|
|
swift_signature = ""
|
|
for a in args:
|
|
if a.ctype not in type_dict:
|
|
if not a.defval and a.ctype.endswith("*"):
|
|
a.defval = 0
|
|
if a.defval:
|
|
a.ctype = ''
|
|
continue
|
|
if not a.ctype: # hidden
|
|
continue
|
|
swift_signature += a.name + ":"
|
|
return swift_signature
|
|
|
|
def build_unrefined_call(name, args, constructor, static, classname, has_ret):
|
|
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")
|
|
call_args = []
|
|
for a in args:
|
|
if a.ctype not in type_dict:
|
|
if not a.defval and a.ctype.endswith("*"):
|
|
a.defval = 0
|
|
if a.defval:
|
|
a.ctype = ''
|
|
continue
|
|
if not a.ctype: # hidden
|
|
continue
|
|
call_args.append(a.name + ": " + extension_tmp_arg(a))
|
|
swift_refine_call += "(" + ", ".join(call_args) + ")"
|
|
return swift_refine_call
|
|
|
|
def build_swift_logues(args):
|
|
prologue = []
|
|
epilogue = []
|
|
for a in args:
|
|
if a.ctype not in type_dict:
|
|
if not a.defval and a.ctype.endswith("*"):
|
|
a.defval = 0
|
|
if a.defval:
|
|
a.ctype = ''
|
|
continue
|
|
if not a.ctype: # hidden
|
|
continue
|
|
if a.ctype in type_dict:
|
|
if type_dict[a.ctype].get("primitive_vector", False):
|
|
prologue.append("let " + extension_tmp_arg(a) + " = " + type_dict[a.ctype]["objc_type"][:-1] + "(" + a.name + ")")
|
|
if "O" in a.out:
|
|
unsigned = type_dict[a.ctype].get("unsigned", False)
|
|
array_prop = "array" if not unsigned else "unsignedArray"
|
|
epilogue.append(a.name + ".removeAll()")
|
|
epilogue.append(a.name + ".append(contentsOf: " + extension_tmp_arg(a) + "." + array_prop + ")")
|
|
elif type_dict[a.ctype].get("primitive_vector_vector", False):
|
|
if not "O" in a.out:
|
|
prologue.append("let " + extension_tmp_arg(a) + " = " + a.name + ".map {" + type_dict[a.ctype]["objc_type"][:-1] + "($0) }")
|
|
else:
|
|
prologue.append("let " + extension_tmp_arg(a) + " = NSMutableArray(array: " + a.name + ".map {" + type_dict[a.ctype]["objc_type"][:-1] + "($0) })")
|
|
epilogue.append(a.name + ".removeAll()")
|
|
epilogue.append(a.name + ".append(contentsOf: " + extension_tmp_arg(a) + ".map { ($.0 as! " + type_dict[a.ctype]["objc_type"][:-1] + ").array })")
|
|
elif ("v_type" in type_dict[a.ctype] or "v_v_type" in type_dict[a.ctype]) and "O" in a.out:
|
|
prologue.append("let " + extension_tmp_arg(a) + " = NSMutableArray(array: " + a.name + ")")
|
|
epilogue.append(a.name + ".removeAll()")
|
|
epilogue.append(a.name + ".append(contentsOf: " + extension_tmp_arg(a) + " as! " + get_swift_type(a.ctype) + ")")
|
|
return prologue, epilogue
|
|
|
|
def add_method_to_dict(class_name, fi):
|
|
static = fi.static if fi.classname else True
|
|
if (class_name, fi.objc_name) not in method_dict:
|
|
objc_method_name = ("+" if static else "-") + fi.objc_name + ":" + build_objc_method_name(fi.args)
|
|
method_dict[(class_name, fi.objc_name)] = objc_method_name
|
|
|
|
def see_lookup(objc_class, see):
|
|
semi_colon = see.find("::")
|
|
see_class = see[:semi_colon] if semi_colon > 0 else objc_class
|
|
see_method = see[(semi_colon + 2):] if semi_colon != -1 else see
|
|
if (see_class, see_method) in method_dict:
|
|
method = method_dict[(see_class, see_method)]
|
|
if see_class == objc_class:
|
|
return method
|
|
else:
|
|
return ("-" if method[0] == "-" else "") + "[" + see_class + " " + method[1:] + "]"
|
|
else:
|
|
return see
|
|
|
|
|
|
class ObjectiveCWrapperGenerator(object):
|
|
def __init__(self):
|
|
self.header_files = []
|
|
self.clear()
|
|
|
|
def clear(self):
|
|
self.namespaces = ["cv"]
|
|
mat_class_info = ClassInfo([ 'class Mat', '', [], [] ], self.namespaces)
|
|
mat_class_info.namespace = "cv"
|
|
self.classes = { "Mat" : mat_class_info }
|
|
self.classes["Mat"].namespace = "cv"
|
|
self.module = ""
|
|
self.Module = ""
|
|
self.extension_implementations = None # Swift extensions implementations stream
|
|
self.ported_func_list = []
|
|
self.skipped_func_list = []
|
|
self.def_args_hist = {} # { def_args_cnt : funcs_cnt }
|
|
|
|
def add_class(self, decl):
|
|
classinfo = ClassInfo(decl, namespaces=self.namespaces)
|
|
if classinfo.name in class_ignore_list:
|
|
logging.info('ignored: %s', classinfo)
|
|
return None
|
|
if classinfo.name != self.Module:
|
|
self.classes[self.Module].member_classes.append(classinfo.objc_name)
|
|
name = classinfo.cname
|
|
if self.isWrapped(name) and not classinfo.base:
|
|
logging.warning('duplicated: %s', classinfo)
|
|
return None
|
|
if name in self.classes: # TODO implement inner namespaces
|
|
if self.classes[name].symbol_id != classinfo.symbol_id:
|
|
logging.warning('duplicated under new id: {} (was {})'.format(classinfo.symbol_id, self.classes[name].symbol_id))
|
|
return None
|
|
self.classes[name] = classinfo
|
|
if name in type_dict and not classinfo.base:
|
|
logging.warning('duplicated: %s', classinfo)
|
|
return None
|
|
if name != self.Module:
|
|
type_dict.setdefault(name, {}).update(
|
|
{ "objc_type" : classinfo.objc_name + "*",
|
|
"from_cpp" : "[" + classinfo.objc_name + " fromNative:%(n)s]",
|
|
"to_cpp" : "*(%(n)s." + classinfo.native_ptr_name + ")" }
|
|
)
|
|
|
|
# missing_consts { Module : { public : [[name, val],...], private : [[]...] } }
|
|
if name in missing_consts:
|
|
if 'public' in missing_consts[name]:
|
|
for (n, val) in missing_consts[name]['public']:
|
|
classinfo.consts.append( ConstInfo([n, val], addedManually=True) )
|
|
|
|
# class props
|
|
for p in decl[3]:
|
|
classinfo.props.append( ClassPropInfo(p) )
|
|
|
|
if name != self.Module:
|
|
type_dict.setdefault("Ptr_"+name, {}).update(
|
|
{ "objc_type" : classinfo.objc_name + "*",
|
|
"c_type" : name,
|
|
"real_c_type" : classinfo.real_cname,
|
|
"to_cpp": "%(n)s." + classinfo.native_ptr_name,
|
|
"from_cpp": "[" + name + " fromNative:%(n)s]"}
|
|
)
|
|
|
|
logging.info('ok: class %s, name: %s, base: %s', classinfo, name, classinfo.base)
|
|
return classinfo
|
|
|
|
def add_const(self, decl, scope=None, enumType=None): # [ "const cname", val, [], [] ]
|
|
constinfo = ConstInfo(decl, namespaces=self.namespaces, enumType=enumType)
|
|
if constinfo.isIgnored():
|
|
logging.info('ignored: %s', constinfo)
|
|
else:
|
|
objc_type = enumType.rsplit(".", 1)[-1] if enumType else ""
|
|
if constinfo.enumType and constinfo.classpath:
|
|
new_name = constinfo.classname + '_' + constinfo.name
|
|
const_fix.setdefault(constinfo.classpath, {}).setdefault(objc_type, {})[constinfo.name] = new_name
|
|
constinfo.swift_name = constinfo.name
|
|
constinfo.name = new_name
|
|
logging.info('use outer class prefix: %s', constinfo)
|
|
|
|
if constinfo.classpath in const_fix and objc_type in const_fix[constinfo.classpath]:
|
|
fixed_consts = const_fix[constinfo.classpath][objc_type]
|
|
if constinfo.name in fixed_consts:
|
|
fixed_const = fixed_consts[constinfo.name]
|
|
constinfo.name = fixed_const
|
|
constinfo.cname = fixed_const
|
|
if constinfo.value in fixed_consts:
|
|
constinfo.value = fixed_consts[constinfo.value]
|
|
|
|
if not self.isWrapped(constinfo.classname):
|
|
logging.info('class not found: %s', constinfo)
|
|
if not constinfo.name.startswith(constinfo.classname + "_"):
|
|
constinfo.swift_name = constinfo.name
|
|
constinfo.name = constinfo.classname + '_' + constinfo.name
|
|
constinfo.classname = ''
|
|
|
|
ci = self.getClass(constinfo.classname)
|
|
duplicate = ci.getConst(constinfo.name)
|
|
if duplicate:
|
|
if duplicate.addedManually:
|
|
logging.info('manual: %s', constinfo)
|
|
else:
|
|
logging.warning('duplicated: %s', constinfo)
|
|
else:
|
|
ci.addConst(constinfo)
|
|
logging.info('ok: %s', constinfo)
|
|
|
|
def add_enum(self, decl): # [ "enum cname", "", [], [] ]
|
|
enumType = decl[0].rsplit(" ", 1)[1]
|
|
if enumType.endswith("<unnamed>"):
|
|
enumType = None
|
|
else:
|
|
ctype = normalize_class_name(enumType)
|
|
constinfo = ConstInfo(decl[3][0], namespaces=self.namespaces, enumType=enumType)
|
|
objc_type = enumType.rsplit(".", 1)[-1]
|
|
if objc_type in enum_ignore_list:
|
|
return
|
|
if constinfo.classname in enum_fix:
|
|
objc_type = enum_fix[constinfo.classname].get(objc_type, objc_type)
|
|
import_module = constinfo.classname if constinfo.classname and constinfo.classname != objc_type else self.Module
|
|
type_dict[ctype] = { "cast_from" : "int",
|
|
"cast_to" : get_cname(enumType),
|
|
"objc_type" : objc_type,
|
|
"is_enum" : True,
|
|
"import_module" : import_module,
|
|
"from_cpp" : "(" + objc_type + ")%(n)s"}
|
|
type_dict[objc_type] = { "cast_to" : get_cname(enumType),
|
|
"objc_type": objc_type,
|
|
"is_enum": True,
|
|
"import_module": import_module,
|
|
"from_cpp": "(" + objc_type + ")%(n)s"}
|
|
self.classes[self.Module].member_enums.append(objc_type)
|
|
|
|
const_decls = decl[3]
|
|
|
|
for decl in const_decls:
|
|
self.add_const(decl, self.Module, enumType)
|
|
|
|
def add_func(self, decl):
|
|
fi = FuncInfo(decl, self.Module, namespaces=self.namespaces)
|
|
classname = fi.classname or self.Module
|
|
if classname in class_ignore_list:
|
|
logging.info('ignored: %s', fi)
|
|
elif classname in ManualFuncs and fi.objc_name in ManualFuncs[classname]:
|
|
logging.info('manual: %s', fi)
|
|
if "objc_method_name" in ManualFuncs[classname][fi.objc_name]:
|
|
method_dict[(classname, fi.objc_name)] = ManualFuncs[classname][fi.objc_name]["objc_method_name"]
|
|
elif not self.isWrapped(classname):
|
|
logging.warning('not found: %s', fi)
|
|
else:
|
|
ci = self.getClass(classname)
|
|
if ci.symbol_id != fi.symbol_id[0:fi.symbol_id.rfind('.')] and ci.symbol_id != self.Module:
|
|
# TODO fix this (inner namepaces)
|
|
logging.warning('SKIP: mismatched class: {} (class: {})'.format(fi.symbol_id, ci.symbol_id))
|
|
return
|
|
ci.addMethod(fi)
|
|
logging.info('ok: %s', fi)
|
|
# calc args with def val
|
|
cnt = len([a for a in fi.args if a.defval])
|
|
self.def_args_hist[cnt] = self.def_args_hist.get(cnt, 0) + 1
|
|
add_method_to_dict(classname, fi)
|
|
|
|
def save(self, path, buf):
|
|
global total_files, updated_files
|
|
if len(buf) == 0:
|
|
return
|
|
total_files += 1
|
|
if os.path.exists(path):
|
|
with open(path, "rt") as f:
|
|
content = f.read()
|
|
if content == buf:
|
|
return
|
|
with codecs.open(path, "w", "utf-8") as f:
|
|
f.write(buf)
|
|
updated_files += 1
|
|
|
|
def get_namespace_prefix(self, cname):
|
|
namespace = self.classes[cname].namespace if cname in self.classes else "cv"
|
|
return namespace.replace(".", "::") + "::"
|
|
|
|
def gen(self, srcfiles, module, output_path, output_objc_path, common_headers, manual_classes):
|
|
self.clear()
|
|
self.module = module
|
|
self.objcmodule = make_objcmodule(module)
|
|
self.Module = module.capitalize()
|
|
extension_implementations = StringIO() # Swift extensions implementations stream
|
|
extension_signatures = []
|
|
|
|
# TODO: support UMat versions of declarations (implement UMat-wrapper for Java)
|
|
parser = hdr_parser.CppHeaderParser(generate_umat_decls=False)
|
|
|
|
module_ci = self.add_class( ['class ' + self.Module, '', [], []]) # [ 'class/struct cname', ':bases', [modlist] [props] ]
|
|
module_ci.header_import = module + '.hpp'
|
|
|
|
# scan the headers and build more descriptive maps of classes, consts, functions
|
|
includes = []
|
|
for hdr in common_headers:
|
|
logging.info("\n===== Common header : %s =====", hdr)
|
|
includes.append(header_import(hdr))
|
|
for hdr in srcfiles:
|
|
decls = parser.parse(hdr)
|
|
self.namespaces = sorted(parser.namespaces)
|
|
logging.info("\n\n===== Header: %s =====", hdr)
|
|
logging.info("Namespaces: %s", sorted(parser.namespaces))
|
|
if decls:
|
|
includes.append(header_import(hdr))
|
|
else:
|
|
logging.info("Ignore header: %s", hdr)
|
|
for decl in decls:
|
|
logging.info("\n--- Incoming ---\n%s", pformat(decl[:5], 4)) # without docstring
|
|
name = decl[0]
|
|
try:
|
|
if name.startswith("struct") or name.startswith("class"):
|
|
ci = self.add_class(decl)
|
|
if ci:
|
|
ci.header_import = header_import(hdr)
|
|
elif name.startswith("const"):
|
|
self.add_const(decl)
|
|
elif name.startswith("enum"):
|
|
# enum
|
|
self.add_enum(decl)
|
|
else: # function
|
|
self.add_func(decl)
|
|
except SkipSymbolException as e:
|
|
logging.info('SKIP: {} due to {}'.format(name, e))
|
|
self.classes[self.Module].member_classes += manual_classes
|
|
|
|
logging.info("\n\n===== Generating... =====")
|
|
package_path = os.path.join(output_objc_path, self.objcmodule)
|
|
mkdir_p(package_path)
|
|
extension_file = "%s/%sExt.swift" % (package_path, make_objcname(self.Module))
|
|
|
|
for ci in sorted(self.classes.values(), key=lambda x: x.symbol_id):
|
|
if ci.name == "Mat":
|
|
continue
|
|
ci.initCodeStreams(self.Module)
|
|
self.gen_class(ci, self.module, extension_implementations, extension_signatures)
|
|
classObjcHeaderCode = ci.generateObjcHeaderCode(self.module, self.Module, ci.objc_name)
|
|
objc_mangled_name = make_objcname(ci.objc_name)
|
|
header_file = "%s/%s.h" % (package_path, objc_mangled_name)
|
|
self.save(header_file, classObjcHeaderCode)
|
|
self.header_files.append(header_file)
|
|
classObjcBodyCode = ci.generateObjcBodyCode(self.module, self.Module)
|
|
self.save("%s/%s.mm" % (package_path, objc_mangled_name), classObjcBodyCode)
|
|
ci.cleanupCodeStreams()
|
|
self.save(extension_file, extension_implementations.getvalue())
|
|
extension_implementations.close()
|
|
self.save(os.path.join(output_path, self.objcmodule+".txt"), self.makeReport())
|
|
|
|
def makeReport(self):
|
|
'''
|
|
Returns string with generator report
|
|
'''
|
|
report = StringIO()
|
|
total_count = len(self.ported_func_list)+ len(self.skipped_func_list)
|
|
report.write("PORTED FUNCs LIST (%i of %i):\n\n" % (len(self.ported_func_list), total_count))
|
|
report.write("\n".join(self.ported_func_list))
|
|
report.write("\n\nSKIPPED FUNCs LIST (%i of %i):\n\n" % (len(self.skipped_func_list), total_count))
|
|
report.write("".join(self.skipped_func_list))
|
|
for i in sorted(self.def_args_hist.keys()):
|
|
report.write("\n%i def args - %i funcs" % (i, self.def_args_hist[i]))
|
|
return report.getvalue()
|
|
|
|
def fullTypeName(self, t):
|
|
if not type_dict[t].get("is_primitive", False) or "cast_to" in type_dict[t]:
|
|
if "cast_to" in type_dict[t]:
|
|
return type_dict[t]["cast_to"]
|
|
else:
|
|
namespace_prefix = self.get_namespace_prefix(t)
|
|
return namespace_prefix + t
|
|
else:
|
|
return t
|
|
|
|
def build_objc2cv_prologue(self, prologue, vector_type, vector_full_type, objc_type, vector_name, array_name):
|
|
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"):
|
|
prologue.append("OBJC2CV(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ");")
|
|
else:
|
|
conv_macro = "CONV_" + array_name
|
|
prologue.append("#define " + conv_macro + "(e) " + type_dict[vector_type]["to_cpp"] % {"n": "e"})
|
|
prologue.append("OBJC2CV_CUSTOM(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ", " + conv_macro + ");")
|
|
prologue.append("#undef " + conv_macro)
|
|
|
|
def build_cv2objc_epilogue(self, epilogue, vector_type, vector_full_type, objc_type, vector_name, array_name):
|
|
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]")):
|
|
epilogue.append("CV2OBJC(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ");")
|
|
else:
|
|
unconv_macro = "UNCONV_" + array_name
|
|
epilogue.append("#define " + unconv_macro + "(e) " + type_dict[vector_type]["from_cpp"] % {"n": "e"})
|
|
epilogue.append("CV2OBJC_CUSTOM(" + vector_full_type + ", " + objc_type[:-1] + ", " + vector_name + ", " + array_name + ", " + unconv_macro + ");")
|
|
epilogue.append("#undef " + unconv_macro)
|
|
|
|
def gen_func(self, ci, fi, extension_implementations, extension_signatures):
|
|
logging.info("%s", fi)
|
|
method_declarations = ci.method_declarations
|
|
method_implementations = ci.method_implementations
|
|
|
|
decl_args = []
|
|
for a in fi.args:
|
|
s = a.ctype or ' _hidden_ '
|
|
if a.pointer:
|
|
s += "*"
|
|
elif a.out:
|
|
s += "&"
|
|
s += " " + a.name
|
|
if a.defval:
|
|
s += " = " + str(a.defval)
|
|
decl_args.append(s)
|
|
c_decl = "%s %s %s(%s)" % ( fi.static, fi.ctype, fi.cname, ", ".join(decl_args) )
|
|
|
|
# comment
|
|
method_declarations.write( "\n//\n// %s\n//\n" % c_decl )
|
|
method_implementations.write( "\n//\n// %s\n//\n" % c_decl )
|
|
# check if we 'know' all the types
|
|
if fi.ctype not in type_dict: # unsupported ret type
|
|
msg = "// Return type '%s' is not supported, skipping the function\n\n" % fi.ctype
|
|
self.skipped_func_list.append(c_decl + "\n" + msg)
|
|
method_declarations.write( " "*4 + msg )
|
|
logging.warning("SKIP:" + c_decl.strip() + "\t due to RET type " + fi.ctype)
|
|
return
|
|
for a in fi.args:
|
|
if a.ctype not in type_dict:
|
|
if not a.defval and a.ctype.endswith("*"):
|
|
a.defval = 0
|
|
if a.defval:
|
|
a.ctype = ''
|
|
continue
|
|
msg = "// Unknown type '%s' (%s), skipping the function\n\n" % (a.ctype, a.out or "I")
|
|
self.skipped_func_list.append(c_decl + "\n" + msg)
|
|
method_declarations.write( msg )
|
|
logging.warning("SKIP:" + c_decl.strip() + "\t due to ARG type " + a.ctype + "/" + (a.out or "I"))
|
|
return
|
|
|
|
self.ported_func_list.append(c_decl)
|
|
|
|
# args
|
|
args = fi.args[:] # copy
|
|
objc_signatures=[]
|
|
while True:
|
|
# method args
|
|
cv_args = []
|
|
prologue = []
|
|
epilogue = []
|
|
if fi.ctype:
|
|
ci.addImports(fi.ctype, False)
|
|
for a in args:
|
|
if not "v_type" in type_dict[a.ctype] and not "v_v_type" in type_dict[a.ctype]:
|
|
cast = ("(" + type_dict[a.ctype]["cast_to"] + ")") if "cast_to" in type_dict[a.ctype] else ""
|
|
cv_name = type_dict[a.ctype].get("to_cpp", cast + "%(n)s") if a.ctype else a.defval
|
|
if a.pointer and not cv_name == "0":
|
|
cv_name = "&(" + cv_name + ")"
|
|
if "O" in a.out and type_dict[a.ctype].get("out_type", ""):
|
|
cv_name = type_dict[a.ctype].get("out_type_ptr" if a.pointer else "out_type_ref", "%(n)s")
|
|
cv_args.append(type_dict[a.ctype].get("cv_name", cv_name) % {"n": a.name})
|
|
if not a.ctype: # hidden
|
|
continue
|
|
ci.addImports(a.ctype, "O" in a.out)
|
|
if "v_type" in type_dict[a.ctype]: # pass as vector
|
|
vector_cpp_type = type_dict[a.ctype]["v_type"]
|
|
objc_type = type_dict[a.ctype]["objc_type"]
|
|
has_namespace = vector_cpp_type.find("::") != -1
|
|
ci.addImports(a.ctype, False)
|
|
vector_full_cpp_type = self.fullTypeName(vector_cpp_type) if not has_namespace else vector_cpp_type
|
|
vector_cpp_name = a.name + "Vector"
|
|
cv_args.append(vector_cpp_name)
|
|
self.build_objc2cv_prologue(prologue, vector_cpp_type, vector_full_cpp_type, objc_type, vector_cpp_name, a.name)
|
|
if "O" in a.out:
|
|
self.build_cv2objc_epilogue(epilogue, vector_cpp_type, vector_full_cpp_type, objc_type, vector_cpp_name, a.name)
|
|
|
|
if "v_v_type" in type_dict[a.ctype]: # pass as vector of vector
|
|
vector_cpp_type = type_dict[a.ctype]["v_v_type"]
|
|
objc_type = type_dict[a.ctype]["objc_type"]
|
|
ci.addImports(a.ctype, False)
|
|
vector_full_cpp_type = self.fullTypeName(vector_cpp_type)
|
|
vector_cpp_name = a.name + "Vector2"
|
|
cv_args.append(vector_cpp_name)
|
|
prologue.append("OBJC2CV2(" + vector_full_cpp_type + ", " + objc_type[:-1] + ", " + vector_cpp_name + ", " + a.name + ");")
|
|
if "O" in a.out:
|
|
epilogue.append(
|
|
"CV2OBJC2(" + vector_full_cpp_type + ", " + objc_type[:-1] + ", " + vector_cpp_name + ", " + a.name + ");")
|
|
|
|
# calculate method signature to check for uniqueness
|
|
objc_args = build_objc_args(args)
|
|
objc_signature = fi.signature(args)
|
|
swift_ext = make_swift_extension(args)
|
|
logging.info("Objective-C: " + objc_signature)
|
|
|
|
if objc_signature in objc_signatures:
|
|
if args:
|
|
args.pop()
|
|
continue
|
|
else:
|
|
break
|
|
|
|
# doc comment
|
|
if fi.docstring:
|
|
lines = fi.docstring.splitlines()
|
|
toWrite = []
|
|
for index, line in enumerate(lines):
|
|
p0 = line.find("@param")
|
|
if p0 != -1:
|
|
p0 += 7 # len("@param" + 1)
|
|
p1 = line.find(' ', p0)
|
|
p1 = len(line) if p1 == -1 else p1
|
|
name = line[p0:p1]
|
|
for arg in args:
|
|
if arg.name == name:
|
|
toWrite.append(re.sub('\*\s*@param ', '* @param ', line))
|
|
break
|
|
else:
|
|
s0 = line.find("@see")
|
|
if s0 != -1:
|
|
sees = line[(s0 + 5):].split(",")
|
|
toWrite.append(line[:(s0 + 5)] + ", ".join(["`" + see_lookup(ci.objc_name, see.strip()) + "`" for see in sees]))
|
|
else:
|
|
toWrite.append(line)
|
|
|
|
for line in toWrite:
|
|
method_declarations.write(line + "\n")
|
|
|
|
# public wrapper method impl (calling native one above)
|
|
# e.g.
|
|
# public static void add( Mat src1, Mat src2, Mat dst, Mat mask, int dtype )
|
|
# { add_0( src1.nativeObj, src2.nativeObj, dst.nativeObj, mask.nativeObj, dtype ); }
|
|
ret_type = fi.ctype
|
|
if fi.ctype.endswith('*'):
|
|
ret_type = ret_type[:-1]
|
|
ret_val = self.fullTypeName(fi.ctype) + " retVal = "
|
|
ret = "return retVal;"
|
|
tail = ""
|
|
constructor = False
|
|
if "v_type" in type_dict[ret_type]:
|
|
objc_type = type_dict[ret_type]["objc_type"]
|
|
vector_type = type_dict[ret_type]["v_type"]
|
|
full_cpp_type = (self.get_namespace_prefix(vector_type) if (vector_type.find("::") == -1) else "") + vector_type
|
|
prologue.append("NSMutableArray<" + objc_type + ">* retVal = [NSMutableArray new];")
|
|
ret_val = "std::vector<" + full_cpp_type + "> retValVector = "
|
|
self.build_cv2objc_epilogue(epilogue, vector_type, full_cpp_type, objc_type, "retValVector", "retVal")
|
|
elif "v_v_type" in type_dict[ret_type]:
|
|
objc_type = type_dict[ret_type]["objc_type"]
|
|
cpp_type = type_dict[ret_type]["v_v_type"]
|
|
if cpp_type.find("::") == -1:
|
|
cpp_type = self.get_namespace_prefix(cpp_type) + cpp_type
|
|
prologue.append("NSMutableArray<NSMutableArray<" + objc_type + ">*>* retVal = [NSMutableArray new];")
|
|
ret_val = "std::vector< std::vector<" + cpp_type + "> > retValVector = "
|
|
epilogue.append("CV2OBJC2(" + cpp_type + ", " + objc_type[:-1] + ", retValVector, retVal);")
|
|
elif ret_type.startswith("Ptr_"):
|
|
cpp_type = type_dict[ret_type]["c_type"]
|
|
real_cpp_type = type_dict[ret_type].get("real_c_type", cpp_type)
|
|
namespace_prefix = self.get_namespace_prefix(cpp_type)
|
|
ret_val = "cv::Ptr<" + namespace_prefix + real_cpp_type + "> retVal = "
|
|
ret = "return [" + type_dict[ret_type]["objc_type"][:-1] + " fromNative:retVal];"
|
|
elif ret_type == "void":
|
|
ret_val = ""
|
|
ret = ""
|
|
elif ret_type == "": # c-tor
|
|
constructor = True
|
|
ret_val = "return [self initWithNativePtr:cv::Ptr<" + fi.fullClass(isCPP=True) + ">(new "
|
|
tail = ")]"
|
|
ret = ""
|
|
elif self.isWrapped(ret_type): # wrapped class
|
|
namespace_prefix = self.get_namespace_prefix(ret_type)
|
|
ret_val = "cv::Ptr<" + namespace_prefix + ret_type + "> retVal = new " + namespace_prefix + ret_type + "("
|
|
tail = ")"
|
|
ret_type_dict = type_dict[ret_type]
|
|
from_cpp = ret_type_dict["from_cpp_ptr"] if "from_cpp_ptr" in ret_type_dict else ret_type_dict["from_cpp"]
|
|
ret = "return " + (from_cpp % { "n" : "retVal" }) + ";"
|
|
elif "from_cpp" in type_dict[ret_type]:
|
|
ret = "return " + (type_dict[ret_type]["from_cpp"] % { "n" : "retVal" }) + ";"
|
|
|
|
static = fi.static if fi.classname else True
|
|
|
|
objc_ret_type = type_dict[fi.ctype]["objc_type"] if type_dict[fi.ctype]["objc_type"] else "void" if not constructor else "instancetype"
|
|
if "v_type" in type_dict[ret_type]:
|
|
objc_ret_type = "NSArray<" + objc_ret_type + ">*"
|
|
elif "v_v_type" in type_dict[ret_type]:
|
|
objc_ret_type = "NSArray<NSArray<" + objc_ret_type + ">*>*"
|
|
|
|
prototype = Template("$static ($objc_ret_type)$objc_name$objc_args").substitute(
|
|
static = "+" if static else "-",
|
|
objc_ret_type = objc_ret_type,
|
|
objc_args = " ".join(objc_args),
|
|
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 ""))
|
|
)
|
|
|
|
if fi.prolog is not None:
|
|
method_declarations.write("\n%s\n\n" % fi.prolog)
|
|
|
|
method_declarations.write( Template(
|
|
"""$prototype$swift_name$deprecation_decl;
|
|
|
|
"""
|
|
).substitute(
|
|
prototype = prototype,
|
|
swift_name = " NS_SWIFT_NAME(" + fi.swift_name + "(" + build_swift_signature(args) + "))" if not constructor else "",
|
|
deprecation_decl = " DEPRECATED_ATTRIBUTE" if fi.deprecated else ""
|
|
)
|
|
)
|
|
|
|
if fi.epilog is not None:
|
|
method_declarations.write("%s\n\n" % fi.epilog)
|
|
|
|
method_implementations.write( Template(
|
|
"""$prototype {$prologue
|
|
$ret_val$obj_deref$cv_name($cv_args)$tail;$epilogue$ret
|
|
}
|
|
|
|
"""
|
|
).substitute(
|
|
prototype = prototype,
|
|
ret = "\n " + ret if ret else "",
|
|
ret_val = ret_val,
|
|
prologue = "\n " + "\n ".join(prologue) if prologue else "",
|
|
epilogue = "\n " + "\n ".join(epilogue) if epilogue else "",
|
|
static = "+" if static else "-",
|
|
obj_deref = ("self." + ci.native_ptr_name + "->") if not static and not constructor else "",
|
|
cv_name = fi.cv_name if static else fi.fullClass(isCPP=True) if constructor else fi.name,
|
|
cv_args = ", ".join(cv_args),
|
|
tail = tail
|
|
)
|
|
)
|
|
|
|
if swift_ext:
|
|
prototype = build_swift_extension_decl(fi.swift_name, args, constructor, static, ret_type)
|
|
if not (ci.name, prototype) in extension_signatures and not (ci.base, prototype) in extension_signatures:
|
|
(pro, epi) = build_swift_logues(args)
|
|
extension_implementations.write( Template(
|
|
"""public extension $classname {
|
|
$deprecation_decl$prototype {
|
|
$prologue
|
|
$unrefined_call$epilogue$ret
|
|
}
|
|
}
|
|
|
|
"""
|
|
).substitute(
|
|
classname = make_objcname(ci.name),
|
|
deprecation_decl = "@available(*, deprecated)\n " if fi.deprecated else "",
|
|
prototype = prototype,
|
|
prologue = " " + "\n ".join(pro),
|
|
unrefined_call = " " + build_unrefined_call(fi.swift_name, args, constructor, static, ci.name, ret_type is not None and ret_type != "void"),
|
|
epilogue = "\n " + "\n ".join(epi) if len(epi) > 0 else "",
|
|
ret = "\n return ret" if ret_type is not None and ret_type != "void" and not constructor else ""
|
|
)
|
|
)
|
|
extension_signatures.append((ci.name, prototype))
|
|
|
|
# adding method signature to dictionary
|
|
objc_signatures.append(objc_signature)
|
|
|
|
# processing args with default values
|
|
if args and args[-1].defval:
|
|
args.pop()
|
|
else:
|
|
break
|
|
|
|
def gen_class(self, ci, module, extension_implementations, extension_signatures):
|
|
logging.info("%s", ci)
|
|
additional_imports = []
|
|
if module in AdditionalImports:
|
|
if "*" in AdditionalImports[module]:
|
|
additional_imports += AdditionalImports[module]["*"]
|
|
if ci.name in AdditionalImports[module]:
|
|
additional_imports += AdditionalImports[module][ci.name]
|
|
if hasattr(ci, 'header_import'):
|
|
h = '"{}"'.format(ci.header_import)
|
|
if not h in additional_imports:
|
|
additional_imports.append(h)
|
|
|
|
h = '"{}.hpp"'.format(module)
|
|
if h in additional_imports:
|
|
additional_imports.remove(h)
|
|
h = '"opencv2/{}.hpp"'.format(module)
|
|
if not h in additional_imports:
|
|
additional_imports.insert(0, h)
|
|
|
|
if additional_imports:
|
|
ci.additionalImports.write('\n'.join(['#import %s' % make_objcname(h) for h in additional_imports]))
|
|
|
|
# constants
|
|
wrote_consts_pragma = False
|
|
consts_map = {c.name: c for c in ci.private_consts}
|
|
consts_map.update({c.name: c for c in ci.consts})
|
|
def const_value(v):
|
|
if v in consts_map:
|
|
target = consts_map[v]
|
|
assert target.value != v
|
|
return const_value(target.value)
|
|
return v
|
|
if ci.consts:
|
|
enumTypes = set([c.enumType for c in ci.consts])
|
|
grouped_consts = {enumType: [c for c in ci.consts if c.enumType == enumType] for enumType in enumTypes}
|
|
for typeName in sorted(grouped_consts.keys(), key=lambda x: str(x) if x is not None else ""):
|
|
consts = grouped_consts[typeName]
|
|
logging.info("%s", consts)
|
|
if typeName:
|
|
typeNameShort = typeName.rsplit(".", 1)[-1]
|
|
if ci.cname in enum_fix:
|
|
typeNameShort = enum_fix[ci.cname].get(typeNameShort, typeNameShort)
|
|
|
|
ci.enum_declarations.write("""
|
|
// C++: enum {1} ({2})
|
|
typedef NS_ENUM(int, {1}) {{
|
|
{0}\n}};\n\n""".format(
|
|
",\n ".join(["%s = %s" % (c.name + (" NS_SWIFT_NAME(" + c.swift_name + ")" if c.swift_name else ""), c.value) for c in consts]),
|
|
typeNameShort, typeName)
|
|
)
|
|
else:
|
|
if not wrote_consts_pragma:
|
|
ci.method_declarations.write("#pragma mark - Class Constants\n\n")
|
|
wrote_consts_pragma = True
|
|
ci.method_declarations.write("""
|
|
{0}\n\n""".format("\n".join(["@property (class, readonly) int %s NS_SWIFT_NAME(%s);" % (c.name, c.name) for c in consts]))
|
|
)
|
|
declared_consts = []
|
|
match_alphabet = re.compile("[a-zA-Z]")
|
|
for c in consts:
|
|
value = str(c.value)
|
|
if match_alphabet.search(value):
|
|
for declared_const in sorted(declared_consts, key=len, reverse=True):
|
|
regex = re.compile("(?<!" + ci.cname + ".)" + declared_const)
|
|
value = regex.sub(ci.cname + "." + declared_const, value)
|
|
ci.method_implementations.write("+ (int)%s {\n return %s;\n}\n\n" % (c.name, value))
|
|
declared_consts.append(c.name)
|
|
|
|
ci.method_declarations.write("#pragma mark - Methods\n\n")
|
|
|
|
# methods
|
|
for fi in ci.getAllMethods():
|
|
self.gen_func(ci, fi, extension_implementations, extension_signatures)
|
|
# props
|
|
for pi in ci.props:
|
|
ci.method_declarations.write("\n //\n // C++: %s %s::%s\n //\n\n" % (pi.ctype, ci.fullName(isCPP=True), pi.name))
|
|
type_data = type_dict[pi.ctype] if pi.ctype != "uchar" else {"objc_type" : "unsigned char", "is_primitive" : True}
|
|
objc_type = type_data.get("objc_type", pi.ctype)
|
|
ci.addImports(pi.ctype, False)
|
|
ci.method_declarations.write("@property " + ("(readonly) " if not pi.rw else "") + objc_type + " " + pi.name + ";\n")
|
|
ptr_ref = "self." + ci.native_ptr_name + "->" if not ci.is_base_class else "self.nativePtr->"
|
|
if "v_type" in type_data:
|
|
vector_cpp_type = type_data["v_type"]
|
|
has_namespace = vector_cpp_type.find("::") != -1
|
|
vector_full_cpp_type = self.fullTypeName(vector_cpp_type) if not has_namespace else vector_cpp_type
|
|
ret_val = "std::vector<" + vector_full_cpp_type + "> retValVector = "
|
|
ci.method_implementations.write("-(NSArray<" + objc_type + ">*)" + pi.name + " {\n")
|
|
ci.method_implementations.write("\tNSMutableArray<" + objc_type + ">* retVal = [NSMutableArray new];\n")
|
|
ci.method_implementations.write("\t" + ret_val + ptr_ref + pi.name + ";\n")
|
|
epilogue = []
|
|
self.build_cv2objc_epilogue(epilogue, vector_cpp_type, vector_full_cpp_type, objc_type, "retValVector", "retVal")
|
|
ci.method_implementations.write("\t" + ("\n\t".join(epilogue)) + "\n")
|
|
ci.method_implementations.write("\treturn retVal;\n}\n\n")
|
|
elif "v_v_type" in type_data:
|
|
vector_cpp_type = type_data["v_v_type"]
|
|
has_namespace = vector_cpp_type.find("::") != -1
|
|
vector_full_cpp_type = self.fullTypeName(vector_cpp_type) if not has_namespace else vector_cpp_type
|
|
ret_val = "std::vector<std::vector<" + vector_full_cpp_type + ">> retValVectorVector = "
|
|
ci.method_implementations.write("-(NSArray<NSArray<" + objc_type + ">*>*)" + pi.name + " {\n")
|
|
ci.method_implementations.write("\tNSMutableArray<NSMutableArray<" + objc_type + ">*>* retVal = [NSMutableArray new];\n")
|
|
ci.method_implementations.write("\t" + ret_val + ptr_ref + pi.name + ";\n")
|
|
ci.method_implementations.write("\tCV2OBJC2(" + vector_full_cpp_type + ", " + objc_type[:-1] + ", retValVectorVector, retVal);\n")
|
|
ci.method_implementations.write("\treturn retVal;\n}\n\n")
|
|
elif self.isWrapped(pi.ctype): # wrapped class
|
|
namespace_prefix = self.get_namespace_prefix(pi.ctype)
|
|
ci.method_implementations.write("-(" + objc_type + ")" + pi.name + " {\n")
|
|
ci.method_implementations.write("\tcv::Ptr<" + namespace_prefix + pi.ctype + "> retVal = new " + namespace_prefix + pi.ctype + "(" + ptr_ref + pi.name + ");\n")
|
|
from_cpp = type_data["from_cpp_ptr"] if "from_cpp_ptr" in type_data else type_data["from_cpp"]
|
|
ci.method_implementations.write("\treturn " + (from_cpp % {"n": "retVal"}) + ";\n}\n\n")
|
|
else:
|
|
from_cpp = type_data.get("from_cpp", "%(n)s")
|
|
retVal = from_cpp % {"n": (ptr_ref + pi.name)}
|
|
ci.method_implementations.write("-(" + objc_type + ")" + pi.name + " {\n\treturn " + retVal + ";\n}\n\n")
|
|
if pi.rw:
|
|
if "v_type" in type_data:
|
|
vector_cpp_type = type_data["v_type"]
|
|
has_namespace = vector_cpp_type.find("::") != -1
|
|
vector_full_cpp_type = self.fullTypeName(vector_cpp_type) if not has_namespace else vector_cpp_type
|
|
ci.method_implementations.write("-(void)set" + pi.name[0].upper() + pi.name[1:] + ":(NSArray<" + objc_type + ">*)" + pi.name + "{\n")
|
|
prologue = []
|
|
self.build_objc2cv_prologue(prologue, vector_cpp_type, vector_full_cpp_type, objc_type, "valVector", pi.name)
|
|
ci.method_implementations.write("\t" + ("\n\t".join(prologue)) + "\n")
|
|
ci.method_implementations.write("\t" + ptr_ref + pi.name + " = valVector;\n}\n\n")
|
|
else:
|
|
to_cpp = type_data.get("to_cpp", ("(" + type_data.get("cast_to") + ")%(n)s") if "cast_to" in type_data else "%(n)s")
|
|
val = to_cpp % {"n": pi.name}
|
|
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")
|
|
|
|
# manual ports
|
|
if ci.name in ManualFuncs:
|
|
for func in sorted(ManualFuncs[ci.name].keys()):
|
|
logging.info("manual function: %s", func)
|
|
fn = ManualFuncs[ci.name][func]
|
|
ci.method_declarations.write( "\n".join(fn["declaration"]) )
|
|
ci.method_implementations.write( "\n".join(fn["implementation"]) )
|
|
|
|
def getClass(self, classname):
|
|
return self.classes[classname or self.Module]
|
|
|
|
def isWrapped(self, classname):
|
|
name = classname or self.Module
|
|
return name in self.classes
|
|
|
|
def isSmartClass(self, ci):
|
|
'''
|
|
Check if class stores Ptr<T>* instead of T* in nativeObj field
|
|
'''
|
|
if ci.smart != None:
|
|
return ci.smart
|
|
|
|
# if parents are smart (we hope) then children are!
|
|
# if not we believe the class is smart if it has "create" method
|
|
ci.smart = False
|
|
if ci.base or ci.name == 'Algorithm':
|
|
ci.smart = True
|
|
else:
|
|
for fi in ci.methods:
|
|
if fi.name == "create":
|
|
ci.smart = True
|
|
break
|
|
|
|
return ci.smart
|
|
|
|
def smartWrap(self, ci, fullname):
|
|
'''
|
|
Wraps fullname with Ptr<> if needed
|
|
'''
|
|
if self.isSmartClass(ci):
|
|
return "Ptr<" + fullname + ">"
|
|
return fullname
|
|
|
|
def finalize(self, objc_target, output_objc_path, output_objc_build_path):
|
|
opencv_header_file = os.path.join(output_objc_path, framework_name + ".h")
|
|
opencv_header = "#import <Foundation/Foundation.h>\n\n"
|
|
opencv_header += "// ! Project version number\nFOUNDATION_EXPORT double " + framework_name + "VersionNumber;\n\n"
|
|
opencv_header += "// ! Project version string\nFOUNDATION_EXPORT const unsigned char " + framework_name + "VersionString[];\n\n"
|
|
opencv_header += "\n".join(["#define AVAILABLE_" + m['name'].upper() for m in config['modules']])
|
|
opencv_header += "\n\n"
|
|
opencv_header += "\n".join(["#import <" + framework_name + "/%s>" % os.path.basename(f) for f in self.header_files])
|
|
self.save(opencv_header_file, opencv_header)
|
|
opencv_modulemap_file = os.path.join(output_objc_path, framework_name + ".modulemap")
|
|
opencv_modulemap = "framework module " + framework_name + " {\n"
|
|
opencv_modulemap += " umbrella header \"" + framework_name + ".h\"\n"
|
|
opencv_modulemap += "\n".join([" header \"%s\"" % os.path.basename(f) for f in self.header_files])
|
|
opencv_modulemap += "\n export *\n module * {export *}\n}\n"
|
|
self.save(opencv_modulemap_file, opencv_modulemap)
|
|
available_modules = " ".join(["-DAVAILABLE_" + m['name'].upper() for m in config['modules']])
|
|
cmakelist_template = read_contents(os.path.join(SCRIPT_DIR, 'templates/cmakelists.template'))
|
|
cmakelist = Template(cmakelist_template).substitute(modules = ";".join(modules), framework = framework_name, objc_target=objc_target, module_availability_defines=available_modules)
|
|
self.save(os.path.join(dstdir, "CMakeLists.txt"), cmakelist)
|
|
mkdir_p(os.path.join(output_objc_build_path, "framework_build"))
|
|
mkdir_p(os.path.join(output_objc_build_path, "test_build"))
|
|
mkdir_p(os.path.join(output_objc_build_path, "doc_build"))
|
|
with open(os.path.join(SCRIPT_DIR, '../doc/README.md')) as readme_in:
|
|
readme_body = readme_in.read()
|
|
readme_body += "\n\n\n##Modules\n\n" + ", ".join(["`" + m.capitalize() + "`" for m in modules])
|
|
with open(os.path.join(output_objc_build_path, "doc_build/README.md"), "w") as readme_out:
|
|
readme_out.write(readme_body)
|
|
if framework_name != "OpenCV":
|
|
for dirname, dirs, files in os.walk(os.path.join(testdir, "test")):
|
|
if dirname.endswith('/resources'):
|
|
continue # don't touch resource binary files
|
|
for filename in files:
|
|
filepath = os.path.join(dirname, filename)
|
|
with io.open(filepath, encoding="utf-8", errors="ignore") as file:
|
|
body = file.read()
|
|
body = body.replace("import OpenCV", "import " + framework_name)
|
|
body = body.replace("#import <OpenCV/OpenCV.h>", "#import <" + framework_name + "/" + framework_name + ".h>")
|
|
with codecs.open(filepath, "w", "utf-8") as file:
|
|
file.write(body)
|
|
|
|
|
|
def copy_objc_files(objc_files_dir, objc_base_path, module_path, include = False):
|
|
global total_files, updated_files
|
|
objc_files = []
|
|
re_filter = re.compile(r'^.+\.(h|m|mm|swift)$')
|
|
for root, dirnames, filenames in os.walk(objc_files_dir):
|
|
objc_files += [os.path.join(root, filename) for filename in filenames if re_filter.match(filename)]
|
|
objc_files = [f.replace('\\', '/') for f in objc_files]
|
|
|
|
re_prefix = re.compile(r'^.+/(.+)\.(h|m|mm|swift)$')
|
|
for objc_file in objc_files:
|
|
src = objc_file
|
|
m = re_prefix.match(objc_file)
|
|
target_fname = (m.group(1) + '.' + m.group(2)) if m else os.path.basename(objc_file)
|
|
dest = os.path.join(objc_base_path, os.path.join(module_path, target_fname))
|
|
mkdir_p(os.path.dirname(dest))
|
|
total_files += 1
|
|
if include and m.group(2) == 'h':
|
|
generator.header_files.append(dest)
|
|
if (not os.path.exists(dest)) or (os.stat(src).st_mtime - os.stat(dest).st_mtime > 1):
|
|
copyfile(src, dest)
|
|
updated_files += 1
|
|
return objc_files
|
|
|
|
def unescape(str):
|
|
return str.replace("<", "<").replace(">", ">").replace("&", "&")
|
|
|
|
def escape_underscore(str):
|
|
return str.replace('_', '\\_')
|
|
|
|
def escape_texttt(str):
|
|
return re.sub(re.compile('texttt{(.*?)\}', re.DOTALL), lambda x: 'texttt{' + escape_underscore(x.group(1)) + '}', str)
|
|
|
|
def get_macros(tex):
|
|
out = ""
|
|
if re.search("\\\\fork\s*{", tex):
|
|
out += "\\newcommand{\\fork}[4]{ \\left\\{ \\begin{array}{l l} #1 & \\text{#2}\\\\\\\\ #3 & \\text{#4}\\\\\\\\ \\end{array} \\right.} "
|
|
if re.search("\\\\vecthreethree\s*{", tex):
|
|
out += "\\newcommand{\\vecthreethree}[9]{ \\begin{bmatrix} #1 & #2 & #3\\\\\\\\ #4 & #5 & #6\\\\\\\\ #7 & #8 & #9 \\end{bmatrix} } "
|
|
return out
|
|
|
|
def fix_tex(tex):
|
|
macros = get_macros(tex)
|
|
fix_escaping = escape_texttt(unescape(tex))
|
|
return macros + fix_escaping
|
|
|
|
def sanitize_documentation_string(doc, type):
|
|
if type == "class":
|
|
doc = doc.replace("@param ", "")
|
|
|
|
doc = re.sub(re.compile('`\\$\\$(.*?)\\$\\$`', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
|
|
doc = re.sub(re.compile('\\\\f\\{align\\*\\}\\{?(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$\\begin{aligned} ' + fix_tex(x.group(1)) + ' \\end{aligned}$$`', doc)
|
|
doc = re.sub(re.compile('\\\\f\\{equation\\*\\}\\{(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$\\begin{aligned} ' + fix_tex(x.group(1)) + ' \\end{aligned}$$`', doc)
|
|
doc = re.sub(re.compile('\\\\f\\$(.*?)\\\\f\\$', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
|
|
doc = re.sub(re.compile('\\\\f\\[(.*?)\\\\f\\]', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
|
|
doc = re.sub(re.compile('\\\\f\\{(.*?)\\\\f\\}', re.DOTALL), lambda x: '`$$' + fix_tex(x.group(1)) + '$$`', doc)
|
|
|
|
doc = doc.replace("@anchor", "") \
|
|
.replace("@brief ", "").replace("\\brief ", "") \
|
|
.replace("@cite", "CITE:") \
|
|
.replace("@code{.cpp}", "<code>") \
|
|
.replace("@code{.txt}", "<code>") \
|
|
.replace("@code", "<code>") \
|
|
.replace("@copydoc", "") \
|
|
.replace("@copybrief", "") \
|
|
.replace("@date", "") \
|
|
.replace("@defgroup", "") \
|
|
.replace("@details ", "") \
|
|
.replace("@endcode", "</code>") \
|
|
.replace("@endinternal", "") \
|
|
.replace("@file", "") \
|
|
.replace("@include", "INCLUDE:") \
|
|
.replace("@ingroup", "") \
|
|
.replace("@internal", "") \
|
|
.replace("@overload", "") \
|
|
.replace("@param[in]", "@param") \
|
|
.replace("@param[out]", "@param") \
|
|
.replace("@ref", "REF:") \
|
|
.replace("@note", "NOTE:") \
|
|
.replace("@returns", "@return") \
|
|
.replace("@sa ", "@see ") \
|
|
.replace("@snippet", "SNIPPET:") \
|
|
.replace("@todo", "TODO:") \
|
|
|
|
lines = doc.splitlines()
|
|
|
|
in_code = False
|
|
for i,line in enumerate(lines):
|
|
if line.find("</code>") != -1:
|
|
in_code = False
|
|
lines[i] = line.replace("</code>", "")
|
|
if in_code:
|
|
lines[i] = unescape(line)
|
|
if line.find("<code>") != -1:
|
|
in_code = True
|
|
lines[i] = line.replace("<code>", "")
|
|
|
|
lines = list([x[x.find('*'):].strip() if x.lstrip().startswith("*") else x for x in lines])
|
|
lines = list(["* " + x[1:].strip() if x.startswith("*") and x != "*" else x for x in lines])
|
|
lines = list([x if x.startswith("*") else "* " + x if x and x != "*" else "*" for x in lines])
|
|
|
|
hasValues = False
|
|
for line in lines:
|
|
if line != "*":
|
|
hasValues = True
|
|
break
|
|
return "/**\n " + "\n ".join(lines) + "\n */" if hasValues else ""
|
|
|
|
if __name__ == "__main__":
|
|
# initialize logger
|
|
logging.basicConfig(filename='gen_objc.log', format=None, filemode='w', level=logging.INFO)
|
|
handler = logging.StreamHandler()
|
|
handler.setLevel(os.environ.get('LOG_LEVEL', logging.WARNING))
|
|
logging.getLogger().addHandler(handler)
|
|
|
|
# parse command line parameters
|
|
import argparse
|
|
arg_parser = argparse.ArgumentParser(description='OpenCV Objective-C Wrapper Generator')
|
|
arg_parser.add_argument('-p', '--parser', required=True, help='OpenCV header parser')
|
|
arg_parser.add_argument('-c', '--config', required=True, help='OpenCV modules config')
|
|
arg_parser.add_argument('-t', '--target', required=True, help='Target (either ios or osx or visionos)')
|
|
arg_parser.add_argument('-f', '--framework', required=True, help='Framework name')
|
|
|
|
args=arg_parser.parse_args()
|
|
|
|
# import header parser
|
|
hdr_parser_path = os.path.abspath(args.parser)
|
|
if hdr_parser_path.endswith(".py"):
|
|
hdr_parser_path = os.path.dirname(hdr_parser_path)
|
|
sys.path.append(hdr_parser_path)
|
|
import hdr_parser
|
|
|
|
with open(args.config) as f:
|
|
config = json.load(f)
|
|
|
|
ROOT_DIR = config['rootdir']; assert os.path.exists(ROOT_DIR)
|
|
if 'objc_build_dir' in config:
|
|
objc_build_dir = config['objc_build_dir']
|
|
assert os.path.exists(objc_build_dir), objc_build_dir
|
|
else:
|
|
objc_build_dir = os.getcwd()
|
|
|
|
dstdir = "./gen"
|
|
testdir = "./test"
|
|
objc_base_path = os.path.join(dstdir, 'objc'); mkdir_p(objc_base_path)
|
|
objc_test_base_path = testdir; mkdir_p(objc_test_base_path)
|
|
copy_objc_files(os.path.join(SCRIPT_DIR, '../test/test'), objc_test_base_path, 'test', False)
|
|
copy_objc_files(os.path.join(SCRIPT_DIR, '../test/dummy'), objc_test_base_path, 'dummy', False)
|
|
copyfile(os.path.join(SCRIPT_DIR, '../test/cmakelists.template'), os.path.join(objc_test_base_path, 'CMakeLists.txt'))
|
|
|
|
# launch Objective-C Wrapper generator
|
|
generator = ObjectiveCWrapperGenerator()
|
|
|
|
gen_dict_files = []
|
|
framework_name = args.framework
|
|
|
|
print("Objective-C: Processing OpenCV modules: %d" % len(config['modules']))
|
|
for e in config['modules']:
|
|
(module, module_location) = (e['name'], os.path.join(ROOT_DIR, e['location']))
|
|
logging.info("\n=== MODULE: %s (%s) ===\n" % (module, module_location))
|
|
modules.append(module)
|
|
|
|
module_imports = []
|
|
srcfiles = []
|
|
common_headers = []
|
|
|
|
misc_location = os.path.join(module_location, 'misc/objc')
|
|
|
|
srcfiles_fname = os.path.join(misc_location, 'filelist')
|
|
if os.path.exists(srcfiles_fname):
|
|
with open(srcfiles_fname) as f:
|
|
srcfiles = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()]
|
|
else:
|
|
re_bad = re.compile(r'(private|.inl.hpp$|_inl.hpp$|.detail.hpp$|.details.hpp$|_winrt.hpp$|/cuda/|/legacy/)')
|
|
# .h files before .hpp
|
|
h_files = []
|
|
hpp_files = []
|
|
for root, dirnames, filenames in os.walk(os.path.join(module_location, 'include')):
|
|
h_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.h')]
|
|
hpp_files += [os.path.join(root, filename) for filename in fnmatch.filter(filenames, '*.hpp')]
|
|
srcfiles = h_files + hpp_files
|
|
srcfiles = [f for f in srcfiles if not re_bad.search(f.replace('\\', '/'))]
|
|
logging.info("\nFiles (%d):\n%s", len(srcfiles), pformat(srcfiles))
|
|
|
|
common_headers_fname = os.path.join(misc_location, 'filelist_common')
|
|
if os.path.exists(common_headers_fname):
|
|
with open(common_headers_fname) as f:
|
|
common_headers = [os.path.join(module_location, str(l).strip()) for l in f.readlines() if str(l).strip()]
|
|
logging.info("\nCommon headers (%d):\n%s", len(common_headers), pformat(common_headers))
|
|
|
|
gendict_fname = os.path.join(misc_location, 'gen_dict.json')
|
|
module_source_map = {}
|
|
if os.path.exists(gendict_fname):
|
|
with open(gendict_fname) as f:
|
|
gen_type_dict = json.load(f)
|
|
namespace_ignore_list = gen_type_dict.get("namespace_ignore_list", [])
|
|
class_ignore_list += gen_type_dict.get("class_ignore_list", [])
|
|
enum_ignore_list += gen_type_dict.get("enum_ignore_list", [])
|
|
const_ignore_list += gen_type_dict.get("const_ignore_list", [])
|
|
const_private_list += gen_type_dict.get("const_private_list", [])
|
|
missing_consts.update(gen_type_dict.get("missing_consts", {}))
|
|
type_dict.update(gen_type_dict.get("type_dict", {}))
|
|
AdditionalImports[module] = gen_type_dict.get("AdditionalImports", {})
|
|
ManualFuncs.update(gen_type_dict.get("ManualFuncs", {}))
|
|
func_arg_fix.update(gen_type_dict.get("func_arg_fix", {}))
|
|
header_fix.update(gen_type_dict.get("header_fix", {}))
|
|
enum_fix.update(gen_type_dict.get("enum_fix", {}))
|
|
const_fix.update(gen_type_dict.get("const_fix", {}))
|
|
module_source_map = gen_type_dict.get("SourceMap", {})
|
|
namespaces_dict.update(gen_type_dict.get("namespaces_dict", {}))
|
|
module_imports += gen_type_dict.get("module_imports", [])
|
|
|
|
objc_files_dir = os.path.join(misc_location, 'common')
|
|
copied_files = []
|
|
if os.path.exists(objc_files_dir):
|
|
copied_files += copy_objc_files(objc_files_dir, objc_base_path, module, True)
|
|
|
|
target_path = 'macosx' if args.target == 'osx' else module_source_map.get(args.target, args.target)
|
|
target_files_dir = os.path.join(misc_location, target_path)
|
|
if os.path.exists(target_files_dir):
|
|
copied_files += copy_objc_files(target_files_dir, objc_base_path, module, True)
|
|
|
|
objc_test_files_dir = os.path.join(misc_location, 'test')
|
|
if os.path.exists(objc_test_files_dir):
|
|
copy_objc_files(objc_test_files_dir, objc_test_base_path, 'test', False)
|
|
objc_test_resources_dir = os.path.join(objc_test_files_dir, 'resources')
|
|
if os.path.exists(objc_test_resources_dir):
|
|
copy_tree(objc_test_resources_dir, os.path.join(objc_test_base_path, 'test', 'resources'))
|
|
|
|
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]
|
|
|
|
if len(srcfiles) > 0:
|
|
generator.gen(srcfiles, module, dstdir, objc_base_path, common_headers, manual_classes)
|
|
else:
|
|
logging.info("No generated code for module: %s", module)
|
|
generator.finalize(args.target, objc_base_path, objc_build_dir)
|
|
|
|
print('Generated files: %d (updated %d)' % (total_files, updated_files))
|