Packages
evision
0.1.34
1.0.1-rc.0
1.0.0
1.0.0-rc.0
0.2.17
0.2.17-rc2
0.2.17-rc1
0.2.16
0.2.16-pre.2
0.2.16-pre
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.2-rc2
0.2.1
0.2.0
0.1.39
0.1.38
0.1.37
0.1.36
0.1.35
0.1.34
0.1.33
0.1.32
retired
0.1.31
0.1.30
0.1.29
0.1.28
0.1.27
0.1.26
0.1.26-rc3
0.1.26-rc2
0.1.26-rc1
0.1.26-rc0
0.1.25
0.1.24
0.1.23
0.1.22
0.1.21
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
OpenCV-Erlang/Elixir binding.
Current section
Files
Jump to
Current section
Files
py_src/gen2.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import print_function
import ast
import sys
from pathlib import Path
import argparse
import hdr_parser
import re
from erl_enum_expression_generator import ErlEnumExpressionGenerator
import evision_templates as ET
import evision_structures as ES
from helper import *
from namespace import Namespace
from func_info import FuncInfo
from class_info import ClassInfo
from module_generator import ModuleGenerator
from fixes import evision_elixir_fixes, evision_erlang_fixes, evision_elixir_module_fixes, evision_erlang_module_fixes
from pathlib import Path
import os
from os import makedirs
from shutil import rmtree
if sys.version_info[0] >= 3:
from io import StringIO
else:
from cStringIO import StringIO
class BeamWrapperGenerator(object):
def __init__(self, enabled_modules, langs, win_dll):
self.clear()
self.argname_prefix_re = re.compile(r'^[_]*')
self.inline_docs_code_type_re = re.compile(r'@code{.(.*)}')
self.inline_docs_inline_math_re = re.compile(r'(?:.*?)\\\\f[$\[](.*?)\\\\f[$\]]', re.MULTILINE|re.DOTALL)
self.enabled_modules = enabled_modules
self.langs = langs
self.win_dll = win_dll
if self.win_dll is None:
self.win_dll = ''
if len(self.win_dll) > 4 and self.win_dll[-4:] == '/lib':
self.win_dll = f"{self.win_dll[:-4]}/bin"
def clear(self):
self.classes = {}
self.namespaces = {}
self.consts = {}
self.enums = {}
self.enum_names = {}
self.enum_names_io = StringIO()
self.enum_names_io_erlang = StringIO()
self.code_include = StringIO()
self.code_enums = StringIO()
self.code_types = StringIO()
self.code_funcs = StringIO()
self.code_ns_reg = StringIO()
# lib/generated/evision_nif.ex
self.evision_nif = StringIO()
# src/generated/evision_nif.erl
self.evision_nif_erlang = StringIO()
# lib/generated/evision.ex
self.evision_elixir = StringIO()
# lib/generated/evision_constant.ex
self.evision_constant_elixir = StringIO()
# src/generated/evision.erl
self.evision_erlang = StringIO()
# src/generated/evision_constant.erl
self.evision_constant_erlang = StringIO()
# src/evision.hrl
self.evision_erlang_hrl = StringIO()
self.evision_ex = ModuleGenerator("Evision")
self.evision_elixir.write('defmodule Evision do\n')
self.evision_elixir.write(' import Kernel, except: [apply: 2, apply: 3, min: 2, max: 2]\n\n')
self.evision_elixir.write(' @doc false\n')
self.evision_elixir.write(' def __to_struct__(any), do: Evision.Internal.Structurise.to_struct(any)\n\n')
self.evision_constant_elixir.write('defmodule Evision.Constant do\n')
self.evision_constant_elixir.write(' import Bitwise\n')
self.evision_constant_elixir.write(ET.gen_cv_types_elixir)
self.evision_erlang.write('-module(evision).\n-compile(nowarn_export_all).\n-compile([export_all]).\n-include("evision.hrl").\n\n')
self.evision_erlang.write('\'__to_struct__\'(Any) ->\n evision_internal_structurise:from_struct(Any).\n\n')
self.evision_constant_erlang.write('-module(evision_constant).\n-compile(nowarn_export_all).\n-compile([export_all]).\n\n')
self.evision_erlang_hrl.write(
"-record(evision_mat, {channels, dims, type, raw_type, shape, ref}).\n"
"-record(evision_videocapture, {fps, frame_count, frame_width, frame_height, isOpened, ref}).\n"
)
self.evision_modules = {}
self.code_type_publish = StringIO()
self.py_signatures = dict()
self.class_idx = 0
self.evision_nif_names = dict()
self.not_struct_types = dict()
def get_export_scope_name(self, original_scope_name):
# Outer classes should be registered before their content - inner classes in this case
class_scope = self.classes.get(normalize_class_name(original_scope_name), None)
if class_scope:
return class_scope.full_export_name
# Otherwise it is a namespace.
# If something is messed up at this point - it will be revelead during
# library import
return original_scope_name
def add_class(self, stype, name, decl):
classinfo = ClassInfo(name, decl, self)
classinfo.decl_idx = self.class_idx
self.class_idx += 1
if classinfo.name in self.classes:
print("Generator error: class %s (cname=%s) already exists" \
% (classinfo.name, classinfo.cname))
# sys.exit(-1)
return
self.classes[classinfo.name] = classinfo
# Add Class to json file.
namespace, classes, name = self.split_decl_name(name)
namespace = '.'.join(namespace)
name = '_'.join(classes+[name])
py_name = 'cv.' + classinfo.wname # use wrapper name
py_signatures = self.py_signatures.setdefault(classinfo.cname, [])
py_signatures.append(dict(name=py_name))
#print('class: ' + classinfo.cname + " => " + py_name)
def split_decl_name(self, name):
chunks = name.split('.')
namespace = chunks[:-1]
classes = []
while namespace and '.'.join(namespace) not in self.parser.namespaces:
classes.insert(0, namespace.pop())
return namespace, classes, chunks[-1]
def add_const(self, name, decl):
(module_name, erl_const_name) = name.split('.')[-2:]
val = decl[1]
skip_this = False
if val == "std::numeric_limits<uint8_t>::max()":
val_erlang = 255
else:
val_tree = ast.parse(val, mode='eval')
val_gen = ErlEnumExpressionGenerator()
val_gen.visit(val_tree)
skip_this = val_gen.skip_this
val = val_gen.expression
val_erlang = val_gen.expression_erlang
if not skip_this:
erl_const_name = map_argname('elixir', erl_const_name, ignore_upper_starting=True)
if self.enum_names.get(val, None) is not None:
val = f'cv_{val}()'
if self.enum_names.get(erl_const_name, None) is None:
self.enum_names[erl_const_name] = val
self.enum_names_io.write(f" def cv_{erl_const_name}, do: {val}\n")
self.enum_names_io_erlang.write(f"cv_{erl_const_name}() ->\n {val_erlang}.\n")
else:
if self.enum_names[erl_const_name] != val:
erl_const_name = map_argname('elixir', f'{module_name}_{erl_const_name}', ignore_upper_starting=True)
if self.enum_names.get(erl_const_name, None) is None:
self.enum_names[erl_const_name] = val
self.enum_names_io.write(f" def cv_{erl_const_name}, do: {val}\n")
self.enum_names_io_erlang.write(f"cv_{erl_const_name}() ->\n {val_erlang}.\n")
else:
raise "duplicated constant name"
cname = name.replace('.', '::')
namespace, classes, name = self.split_decl_name(name)
namespace = '.'.join(namespace)
name = '_'.join(classes+[name])
ns = self.namespaces.setdefault(namespace, Namespace())
if name in ns.consts:
print("Generator error: constant %s (cname=%s) already exists" \
% (name, cname))
return
ns.consts[name] = cname
value = decl[1]
py_name = '.'.join([namespace, name])
py_signatures = self.py_signatures.setdefault(cname, [])
py_signatures.append(dict(name=py_name, value=value))
#print(cname + ' => ' + str(py_name) + ' (value=' + value + ')')
def add_enum(self, name, decl):
wname = normalize_class_name(name)
if wname.endswith("<unnamed>"):
wname = None
else:
self.enums[wname] = name
const_decls = decl[3]
for decl in const_decls:
name = decl[0]
self.add_const(name.replace("const ", "").strip(), decl)
def add_func(self, decl):
namespace, classes, barename = self.split_decl_name(decl[0])
cname = "::".join(namespace+classes+[barename])
name = barename
classname = ''
bareclassname = ''
if classes:
classname = normalize_class_name('.'.join(namespace+classes))
bareclassname = classes[-1]
namespace_str = '.'.join(namespace)
isconstructor = name == bareclassname
is_static = False
isphantom = False
mappable = None
for m in decl[2]:
if m == "/S":
is_static = True
elif m == "/phantom":
isphantom = True
cname = cname.replace("::", "_")
elif m.startswith("="):
name = m[1:]
elif m.startswith("/mappable="):
mappable = m[10:]
self.classes[classname].mappables.append(mappable)
return
if isconstructor:
name = "_".join(classes[:-1]+[name])
if is_static:
# Add it as a method to the class
func_map = self.classes[classname].methods
func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace_str, is_static))
func.add_variant(decl, isphantom)
# Add it as global function
g_name = "_".join(classes+[name])
w_classes = []
for i in range(0, len(classes)):
classes_i = classes[:i+1]
classname_i = normalize_class_name('.'.join(namespace+classes_i))
w_classname = self.classes[classname_i].wname
namespace_prefix = normalize_class_name('.'.join(namespace)) + '_'
if w_classname.startswith(namespace_prefix):
w_classname = w_classname[len(namespace_prefix):]
w_classes.append(w_classname)
g_wname = "_".join(w_classes+[name])
func_map = self.namespaces.setdefault(namespace_str, Namespace()).funcs
func = func_map.setdefault(g_name, FuncInfo("", g_name, cname, isconstructor, namespace_str, False))
func.add_variant(decl, isphantom)
if g_wname != g_name: # TODO OpenCV 5.0
wfunc = func_map.setdefault(g_wname, FuncInfo("", g_wname, cname, isconstructor, namespace_str, False))
wfunc.add_variant(decl, isphantom)
else:
if classname and not isconstructor:
if not isphantom:
cname = barename
func_map = self.classes[classname].methods
else:
func_map = self.namespaces.setdefault(namespace_str, Namespace()).funcs
func = func_map.setdefault(name, FuncInfo(classname, name, cname, isconstructor, namespace_str, is_static))
func.add_variant(decl, isphantom)
if classname and isconstructor:
self.classes[classname].constructor = func
def handle_custom_file(self, module_text):
check_defs = None
with open(module_text, "rt") as f:
for line in f:
if line.startswith("// @evision enable_with: "):
with_module = line[len("// @evision enable_with: "):].strip()
if check_defs is None:
check_defs = with_module in self.enabled_modules
else:
check_defs = check_defs and (with_module in self.enabled_modules)
if check_defs is True or check_defs is None:
with open(module_text, "rt") as f:
for line in f:
line = line.strip()
if line.startswith("// @evision c: "):
parts = line[len("// @evision c: "):].split(',')
if len(parts) != 3:
raise Exception(f'Invalid comment: {line}')
erl_name = parts[0].strip()
func_name = parts[1].strip()
func_arity = parts[2].strip()
if func_name.endswith('_read') or func_name.endswith('_load_static') or \
func_name.endswith('_write') or func_name.endswith('_save') or \
func_name in io_bound_funcs():
self.code_ns_reg.write(f' F_IO({erl_name}, {func_name}, {func_arity}),\n')
else:
self.code_ns_reg.write(f' F_CPU({erl_name}, {func_name}, {func_arity}),\n')
if int(func_arity) > 0:
self.evision_nif_erlang.write(f'{erl_name}(_opts) ->\n not_loaded(?LINE).\n')
else:
self.evision_nif_erlang.write(f'{erl_name}() ->\n not_loaded(?LINE).\n')
elif line.startswith("// @evision nif: "):
line = line[len("// @evision nif: "):].strip()
self.evision_nif.write(f' {line}\n')
def gen_namespace(self):
for ns_name in self.namespaces:
ns = self.namespaces[ns_name]
wname = normalize_class_name(ns_name)
for name, func in sorted(ns.funcs.items()):
if wname == 'cv':
self.evision_ex.gen_ns_method(wname, name, func, namespace_list=None)
else:
module_file_generator, _ = self.get_module_writer(wname, wname=wname, name=name, is_ns=True)
module_file_generator.gen_ns_method(wname, name, func, namespace_list=None)
self.code_ns_reg.write(func.get_tab_entry())
modules_dir = Path(self.output_path) / 'modules'
for module_text in modules_dir.glob('*.h'):
self.handle_custom_file(module_text)
backend_dir = Path(self.output_path) / 'modules' / 'evision_backend'
for module_text in backend_dir.glob('*.h'):
self.handle_custom_file(module_text)
def gen_enum_reg(self, enum_name):
name_seg = enum_name.split(".")
if len(name_seg) >= 2 and name_seg[-1] == name_seg[-2]:
enum_name = ".".join(name_seg[:-1])
wname = normalize_class_name(enum_name)
cname = enum_name.replace(".", "::")
code = ""
if re.sub(r"^cv\.", "", enum_name) != wname:
code += "typedef {0} {1};\n".format(cname, wname)
code += "CV_ERL_FROM_ENUM({0});\nCV_ERL_TO_ENUM({0});\n\n".format(wname)
self.code_enums.write(code)
def save(self, path, name, buf):
outdir_path = path
outdir = Path(outdir_path)
if not outdir.exists():
outdir.mkdir(parents=True, exist_ok=True)
with open(f"{outdir_path}/{name}", "wt", encoding='utf-8') as f:
if name.endswith(".h"):
f.write("#include <erl_nif.h>\n")
f.write('#include "nif_utils.hpp"\n')
f.write('using namespace evision::nif;\n')
if type(buf) == str:
f.write(buf)
else:
f.write(buf.getvalue())
def save_json(self, path, name, value):
import json
with open(path + "/" + name, "wt", encoding='utf-8') as f:
json.dump(value, f)
def get_module_writer(self, module_name, wname, name, is_ns):
elixir_module_name = make_elixir_module_names(module_name=module_name)
inner_ns = []
if wname.startswith('cv::'):
wname = wname[4:]
inner_ns = wname.split('::')
elixir_module_name = make_elixir_module_names(separated_ns=inner_ns)
if module_name == 'ximgproc_segmentation':
elixir_module_name = "XImgProc"
inner_ns = ["Segmentation"]
elif module_name == 'utils_nested':
elixir_module_name = "Utils.Nested"
else:
elixir_module_name = elixir_module_name.replace('_', '').strip()
evision_module_filename = elixir_module_name.replace('.', '_')
if evision_module_filename in self.evision_modules:
return self.evision_modules[evision_module_filename], inner_ns
else:
module_file_generator = ModuleGenerator(elixir_module_name)
module_file_generator.write_elixir(f'defmodule Evision.{elixir_module_name} do\n')
if elixir_module_name not in ['Flann', 'Segmentation', 'ML']:
module_file_generator.write_elixir(' import Kernel, except: [apply: 2, apply: 3]\n\n')
if not evision_module_filename.startswith("evision_"):
module_file_generator.write_erlang(f'-module(evision_{evision_module_filename.lower()}).\n-compile(nowarn_export_all).\n-compile([export_all]).\n-include("evision.hrl").\n\n')
else:
module_file_generator.write_erlang(f'-module({evision_module_filename.lower()}).\n--compile(nowarn_export_all).\ncompile([export_all]).\n-include("evision.hrl").\n\n')
if ES.evision_structs.get(elixir_module_name, None) is not None:
module_file_generator.write_elixir(ES.evision_structs[elixir_module_name]["elixir"])
module_file_generator.write_elixir("\n")
module_file_generator.write_erlang(ES.evision_structs[elixir_module_name]["erlang"])
module_file_generator.write_erlang("\n")
if elixir_module_name not in evision_structrised_classes:
mapped_elixir_module_name = get_elixir_module_name(elixir_module_name)
atom_elixir_module_name = f"Evision.{mapped_elixir_module_name}"
atom_erlang_module_name = elixir_module_name
module_file_generator.write_elixir(
ES.generic_struct_template_elixir.substitute(
atom_elixir_module_name=atom_elixir_module_name,
elixir_module_name=mapped_elixir_module_name
)
)
atom_erlang_module_name = atom_erlang_module_name.replace("Evision.", "evision_").replace(".", "_").lower()
if not atom_erlang_module_name.startswith("evision_"):
atom_erlang_module_name = f"evision_{atom_erlang_module_name}"
module_file_generator.write_erlang(
ES.generic_struct_template_erlang.substitute(
atom_elixir_module_name=f"Elixir.{mapped_elixir_module_name}",
atom_erlang_module_name=atom_erlang_module_name
)
)
self.evision_erlang_hrl.write(
f"-record({atom_erlang_module_name}, "
"{ref}).\n"
)
self.evision_modules[evision_module_filename] = module_file_generator
return self.evision_modules[evision_module_filename], inner_ns
def gen_enabled_modules(self):
all_modules = {
# opencv/opencv_contrib
'opencv': [
'calib3d',
'core',
'dnn',
'features2d',
'flann',
'highgui',
'imgcodecs',
'imgproc',
'ml',
'photo',
'stitching',
'ts',
'video',
'videoio',
'gapi',
'world',
'python2',
'python3',
'java',
],
'opencv_contrib': [
'aruco',
'barcode',
'bgsegm',
'bioinspired',
'dnn_superres',
'face',
'hfs',
'img_hash',
'line_descriptor',
'mcc',
'plot',
'quality',
'rapid',
'reg',
'rgbd',
'saliency',
'shape',
'stereo',
'structured_light',
'surface_matching',
'text',
'tracking',
'wechat_qrcode',
'xfeatures2d',
'ximgproc',
'xphoto',
# no bindings yet
'datasets',
'dnn_objdetect',
'dpm',
'optflow',
'sfm',
'videostab',
'xobjdetect',
],
'cuda': [
'cudaarithm',
'cudabgsegm',
'cudacodec',
'cudafeatures2d',
'cudafilters',
'cudaimgproc',
'cudalegacy',
'cudaobjdetect',
'cudaoptflow',
'cudastereo',
'cudawarping',
'cudev',
]
}
num_total_modules = sum([len(components) for s, components in all_modules.items()])
self.code_funcs.write("static ERL_NIF_TERM evision_cv_enabled_modules(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]){\n")
self.code_funcs.write(f""" const size_t num_total_modules = {num_total_modules};
size_t index = 0;
ERL_NIF_TERM keys[num_total_modules];
ERL_NIF_TERM values[num_total_modules];
""")
for _, components in all_modules.items():
for component in components:
self.code_funcs.write(f""" keys[index] = evision::nif::atom(env, "{component}");
#ifdef HAVE_OPENCV_{component.upper()}
values[index] = evision::nif::atom(env, "true");
#else
values[index] = evision::nif::atom(env, "false");
#endif
index++;
""")
self.code_funcs.write(""" ERL_NIF_TERM map;
if (enif_make_map_from_arrays(env, keys, values, index, &map)) {
return map;
} else {
return evision::nif::error(env, "enif_make_map_from_arrays failed in evision_from_as_map");
}
""")
self.code_funcs.write("}\n")
self.code_ns_reg.write(f' F(enabled_modules, evision_cv_enabled_modules, 0),\n')
self.evision_nif.write(f' def enabled_modules, do: :erlang.nif_error("enabled_modules not loaded")\n')
self.evision_nif_erlang.write(f'enabled_modules() ->\n not_loaded(?LINE).\n')
def gen(self, srcfiles, output_path, erl_output_path, erlang_output_path):
self.output_path = output_path
self.clear()
self.parser = hdr_parser.CppHeaderParser(generate_umat_decls=True, generate_gpumat_decls=True)
self.evision_nif.write('defmodule :evision_nif do\n{}\n'.format(ET.gen_evision_nif_load_nif %(self.win_dll, )))
self.evision_nif_erlang.write('-module(evision_nif).\n-compile(nowarn_export_all).\n-compile([export_all]).\n\n{}\n{}\n'.format(ET.gen_evision_nif_load_nif_erlang % (self.win_dll,), ET.gen_cv_types_erlang))
self.code_ns_reg.write('static ErlNifFunc nif_functions[] = {\n')
self.gen_enabled_modules()
# step 1: scan the headers and build more descriptive maps of classes, consts, functions
for hdr in srcfiles:
decls = self.parser.parse(hdr)
if len(decls) == 0:
continue
if 'gapi' in hdr:
continue
if hdr.find('misc/python/shadow_') < 0: # Avoid including the "shadow_" files
if hdr.find('opencv2/') >= 0:
# put relative path
self.code_include.write('#include "{0}"\n'.format(hdr[hdr.rindex('opencv2/'):]))
else:
self.code_include.write('#include "{0}"\n'.format(hdr))
for decl in decls:
name = decl[0]
if name.startswith("struct") or name.startswith("class"):
# class/struct
p = name.find(" ")
stype = name[:p]
name = name[p+1:].strip()
self.add_class(stype, name, decl)
elif name.startswith("const"):
# constant
self.add_const(name.replace("const ", "").strip(), decl)
elif name.startswith("enum"):
# enum
self.add_enum(name.rsplit(" ", 1)[1], decl)
else:
# function
self.add_func(decl)
# step 1.5 check if all base classes exist
for name, classinfo in self.classes.items():
if classinfo.base:
chunks = classinfo.base.split('_')
base = '_'.join(chunks)
while base not in self.classes and len(chunks)>1:
del chunks[-2]
base = '_'.join(chunks)
if base not in self.classes:
print("Generator error: unable to resolve base %s for %s"
% (classinfo.base, classinfo.name))
sys.exit(-1)
base_instance = self.classes[base]
classinfo.base = base
classinfo.isalgorithm |= base_instance.isalgorithm # wrong processing of 'isalgorithm' flag:
# doesn't work for trees(graphs) with depth > 2
self.classes[name] = classinfo
# tree-based propagation of 'isalgorithm'
processed = dict()
def process_isalgorithm(classinfo):
if classinfo.isalgorithm or classinfo in processed:
return classinfo.isalgorithm
res = False
if classinfo.base:
res = process_isalgorithm(self.classes[classinfo.base])
#assert not (res == True or classinfo.isalgorithm is False), "Internal error: " + classinfo.name + " => " + classinfo.base
classinfo.isalgorithm |= res
res = classinfo.isalgorithm
processed[classinfo] = True
return res
for name, classinfo in self.classes.items():
process_isalgorithm(classinfo)
# step 2: generate code for the classes and their methods
classlist = list(self.classes.items())
classlist.sort()
for name, classinfo in classlist:
self.code_types.write("//{}\n".format(80*"="))
self.code_types.write("// {} ({})\n".format(name, 'Map' if classinfo.ismap else 'Generic'))
self.code_types.write("//{}\n".format(80*"="))
classinfo.gen_erl_func_list(self)
self.code_types.write(classinfo.gen_code(self))
if classinfo.ismap:
self.code_types.write(ET.gen_template_map_type_cvt.substitute(name=classinfo.name, cname=classinfo.cname))
else:
mappable_code = "\n".join([
ET.gen_template_mappable.substitute(cname=classinfo.cname, mappable=mappable)
for mappable in classinfo.mappables])
code = ET.gen_template_type_decl.substitute(
name=classinfo.name,
cname=classinfo.cname if classinfo.issimple else "Ptr<{}>".format(classinfo.cname),
mappable_code=mappable_code
)
self.code_types.write(code)
# register classes in the same order as they have been declared.
# this way, base classes will be registered in Python before their derivatives.
classlist1 = [(classinfo.decl_idx, name, classinfo) for name, classinfo in classlist]
classlist1.sort()
for decl_idx, name, classinfo in classlist1:
if classinfo.ismap:
continue
self.code_type_publish.write(classinfo.gen_def(self, self.evision_modules, self.evision_erlang_hrl))
# step 3: generate the code for all the global functions
for ns_name, ns in sorted(self.namespaces.items()):
if ns_name.split('.')[0] != 'cv':
continue
for name, func in sorted(ns.funcs.items()):
if func.isconstructor:
continue
code = func.gen_code(self)
self.code_funcs.write(code)
self.gen_namespace()
# step 4: generate the code for enum types
enumlist = list(self.enums.values())
enumlist.sort()
for name in enumlist:
self.gen_enum_reg(name)
# step 5: generate the code for constants
constlist = list(self.consts.items())
constlist.sort()
for name, constinfo in constlist:
self.gen_const_reg(constinfo)
# end 'evision.ex'
if 'elixir' in self.langs:
for fix in evision_elixir_fixes():
self.evision_elixir.write(fix)
self.evision_elixir.write("\n")
self.evision_elixir.write(ET.enabled_modules_code.substitute(
enabled_modules=",".join([f'\n "{m}"' for m in self.enabled_modules]))
)
# end 'evision.erl'
if 'erlang' in self.langs:
self.evision_ex.end('erlang')
self.evision_erlang.write(self.evision_ex.get_generated_code('erlang'))
for fix in evision_erlang_fixes():
self.evision_erlang.write(fix)
self.evision_erlang.write("\n")
self.evision_erlang.write(ET.enabled_modules_code_erlang.substitute(
enabled_modules=",".join([f'\'{m}\'' for m in self.enabled_modules]))
)
# That's it. Now save all the files
self.save(output_path, "evision_generated_include.h", self.code_include)
self.save(output_path, "evision_generated_funcs.h", self.code_funcs)
self.save(output_path, "evision_generated_enums.h", self.code_enums)
self.save(output_path, "evision_generated_types.h", self.code_type_publish)
self.save(output_path, "evision_generated_types_content.h", self.code_types)
# write all module files
for name in self.evision_modules:
module_file_generator = self.evision_modules[name]
if 'elixir' in self.langs:
if name in evision_elixir_module_fixes():
fixes = evision_elixir_module_fixes()[name]
for f,d in fixes:
module_file_generator.write_elixir(f)
self.evision_nif.write(d)
module_file_generator.end('elixir')
self.evision_nif.write(module_file_generator.get_nif_declaration('elixir'))
self.save(erl_output_path, f"evision_{name.lower()}.ex", module_file_generator.get_generated_code('elixir'))
if 'erlang' in self.langs:
if name in evision_erlang_module_fixes():
fixes = evision_erlang_module_fixes()[name]
for f,d in fixes:
module_file_generator.write_erlang(f)
self.evision_nif_erlang.write(d)
module_file_generator.end('erlang')
self.evision_nif_erlang.write(module_file_generator.get_nif_declaration('erlang'))
self.save(erlang_output_path, f"evision_{name.lower()}.erl", module_file_generator.get_generated_code('erlang'))
self.code_ns_reg.write(module_file_generator.get_erl_nif_func_entry())
if 'elixir' in self.langs:
# 'evision_nif.ex'
self.evision_nif.write(self.evision_ex.get_nif_declaration('elixir'))
self.evision_nif.write('\nend\n')
self.save(erl_output_path, "evision_nif.ex", self.evision_nif)
# 'evision_constant.ex'
self.evision_constant_elixir.write(self.enum_names_io.getvalue())
self.evision_constant_elixir.write('\nend\n')
self.save(erl_output_path, "evision_constant.ex", self.evision_constant_elixir)
# 'evision.ex'
self.evision_ex.end('elixir')
self.evision_elixir.write(self.evision_ex.get_generated_code('elixir'))
self.save(erl_output_path, "evision.ex", self.evision_elixir)
if 'erlang' in self.langs:
# 'evision_nif.erl'
self.evision_nif_erlang.write(self.evision_ex.get_nif_declaration('erlang'))
self.save(erlang_output_path, "evision_nif.erl", self.evision_nif_erlang)
# 'evision.erl'
self.evision_ex.end('erlang')
self.evision_elixir.write(self.evision_ex.get_generated_code('erlang'))
self.save(erlang_output_path, "evision.erl", self.evision_erlang)
# 'evision_constant.erl'
self.evision_constant_erlang.write(self.enum_names_io_erlang.getvalue())
self.evision_constant_erlang.write('\n')
self.save(erlang_output_path, "evision_constant.erl", self.evision_constant_erlang)
# 'evision.hrl'
self.save(erlang_output_path, "evision.hrl", self.evision_erlang_hrl)
self.code_ns_reg.write('\n};\n\n')
self.save(output_path, "evision_generated_modules_content.h", self.code_ns_reg)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--c_src", type=str, default="./c_src", help="Path to the c_src dir")
parser.add_argument("--elixir_gen", type=str, default="./lib", help="Path to the output dir of elixir binding files")
parser.add_argument("--erlang_gen", type=str, default="./src", help="Path to the output dir of erlang binding files")
parser.add_argument("--headers", help="Path to the headers.txt/header-contrib.txt in c_src")
parser.add_argument("--lang", type=str, help="Comma-seperated values. erlang,elixir")
parser.add_argument("--modules", type=str, default='', help="Comma-seperated values.")
parser.add_argument("--win_dll", type=str, default='', help="Path to OpenCV libs on Windows")
args = parser.parse_args()
srcfiles = hdr_parser.opencv_hdr_list
dstdir = args.c_src
elixir_dstdir = args.elixir_gen
erlang_dstdir = args.erlang_gen
if len(args.headers) > 4:
with open(args.headers, 'r') as f:
srcfiles = [l.strip() for l in f.readlines()]
lang = []
if len(args.lang) > 5:
lang = [l.lower().strip() for l in args.lang.split(",")]
if len(lang) == 0:
raise RuntimeError("env var EVISION_GENERATE_LANG is empty")
for l in lang:
if l not in ['elixir', 'erlang']:
raise RuntimeError(f"unknown value found in EVISION_GENERATE_LANG: `{l}`. Allowed values are `elixir`, `erlang`")
# default
enabled_modules = ['calib3d', 'core', 'features2d', 'flann', 'highgui', 'imgcodecs', 'imgproc', 'ml', 'photo',
'stitching', 'ts', 'video', 'videoio', 'dnn']
if len(args.modules) > 0:
enabled_modules = args.modules.split(",")
generator = BeamWrapperGenerator(enabled_modules, lang, args.win_dll)
rmtree(elixir_dstdir)
rmtree(erlang_dstdir)
makedirs(elixir_dstdir)
makedirs(erlang_dstdir)
generator.gen(srcfiles, dstdir, elixir_dstdir, erlang_dstdir)
# for n in generator.namespaces:
# print(f'"{n}": &(&1[:namespace] == :"{n}"),')