diff --git a/cmake/OpenCVModule.cmake b/cmake/OpenCVModule.cmake index 4c1eaf650c..17f5b3b7dd 100644 --- a/cmake/OpenCVModule.cmake +++ b/cmake/OpenCVModule.cmake @@ -104,7 +104,7 @@ macro(ocv_add_dependencies full_modname) list(FIND OPENCV_MODULE_${full_modname}_WRAPPERS "python" __python_idx) if (NOT __python_idx EQUAL -1) list(REMOVE_ITEM OPENCV_MODULE_${full_modname}_WRAPPERS "python") - list(APPEND OPENCV_MODULE_${full_modname}_WRAPPERS "python2" "python3") + list(APPEND OPENCV_MODULE_${full_modname}_WRAPPERS "python_bindings_generator" "python2" "python3") endif() unset(__python_idx) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 3d6e3950d1..aa049f9189 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -33,7 +33,7 @@ endif(HAVE_DOC_GENERATOR) if(BUILD_DOCS AND DOXYGEN_FOUND) # not documented modules list - list(APPEND blacklist "ts" "java" "python2" "python3" "js" "world") + list(APPEND blacklist "ts" "java" "python_bindings_generator" "python2" "python3" "js" "world") unset(CMAKE_DOXYGEN_TUTORIAL_CONTRIB_ROOT) unset(CMAKE_DOXYGEN_TUTORIAL_JS_ROOT) @@ -104,7 +104,6 @@ if(BUILD_DOCS AND DOXYGEN_FOUND) list(APPEND deps ${bib_file}) endif() # Reference entry - # set(one_ref "@ref ${m} | ${m}\n") set(one_ref "\t- ${m}. @ref ${m}\n") list(FIND EXTRA_MODULES ${m} _pos) if(${_pos} EQUAL -1) @@ -205,15 +204,49 @@ if(BUILD_DOCS AND DOXYGEN_FOUND) list(APPEND js_tutorials_assets_deps "${f}" "${opencv_tutorial_html_dir}/${fname}") endforeach() - add_custom_target(doxygen + add_custom_target( + doxygen_cpp COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} DEPENDS ${doxyfile} ${rootfile} ${bibfile} ${deps} ${js_tutorials_assets_deps} + COMMENT "Generate Doxygen documentation" ) + install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doxygen/html DESTINATION "${OPENCV_DOC_INSTALL_PATH}" COMPONENT "docs" OPTIONAL ) + if(NOT DEFINED HAVE_PYTHON_BS4 AND PYTHON_DEFAULT_EXECUTABLE) + # Documentation post-processing tool requires BuautifulSoup Python package + execute_process(COMMAND "${PYTHON_DEFAULT_EXECUTABLE}" -c "import bs4; from bs4 import BeautifulSoup; print(bs4.__version__)" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _bs4_version + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(NOT _result EQUAL 0) + set(HAVE_PYTHON_BS4 0 CACHE INTERNAL "") + else() + message(STATUS "Python BeautifulSoup (bs4) version: ${_bs4_version}") + set(HAVE_PYTHON_BS4 1 CACHE INTERNAL "") + endif() + endif() + + if(PYTHON_DEFAULT_EXECUTABLE AND HAVE_PYTHON_BS4 + AND OPENCV_PYTHON_SIGNATURES_FILE AND TARGET gen_opencv_python_source) + add_custom_target(doxygen_python + COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/tools/add_signatures.py" "${CMAKE_CURRENT_BINARY_DIR}/doxygen/html/" "${OPENCV_PYTHON_SIGNATURES_FILE}" "python" + DEPENDS doxygen_cpp gen_opencv_python_source + COMMENT "Inject Python signatures into documentation" + ) + add_custom_target(doxygen + DEPENDS doxygen_cpp doxygen_python + ) + else() + add_custom_target(doxygen + DEPENDS doxygen_cpp + ) + endif() + # Alias to build/install docs only add_custom_target(install_docs DEPENDS doxygen diff --git a/doc/tools/add_signatures.py b/doc/tools/add_signatures.py new file mode 100644 index 0000000000..778dd33570 --- /dev/null +++ b/doc/tools/add_signatures.py @@ -0,0 +1,99 @@ +""" +This code adds Python/Java signatures to the docs. + +TODO: Do the same thing for Java +* using javadoc/ get all the methods/classes/constants to a json file + +TODO: +* clarify when there are several C++ signatures corresponding to a single Python function. + i.e: calcHist(): + http://docs.opencv.org/3.2.0/d6/dc7/group__imgproc__hist.html#ga4b2b5fd75503ff9e6844cc4dcdaed35d +* clarify special case: + http://docs.opencv.org/3.2.0/db/de0/group__core__utils.html#ga4910d7f86336cd4eff9dd05575667e41 +""" +from __future__ import print_function +import sys +sys.dont_write_bytecode = True # Don't generate .pyc files / __pycache__ directories + +import os +from pprint import pprint +import re +import logging +import json + +import html_functions +import doxygen_scan + +loglevel=os.environ.get("LOGLEVEL", None) +if loglevel: + logging.basicConfig(level=loglevel) + +ROOT_DIR = sys.argv[1] +PYTHON_SIGNATURES_FILE = sys.argv[2] +JAVA_OR_PYTHON = sys.argv[3] + +ADD_JAVA = False +ADD_PYTHON = False +if JAVA_OR_PYTHON == "python": + ADD_PYTHON = True + +python_signatures = dict() +with open(PYTHON_SIGNATURES_FILE, "rt") as f: + python_signatures = json.load(f) + print("Loaded Python signatures: %d" % len(python_signatures)) + +import xml.etree.ElementTree as ET +root = ET.parse(ROOT_DIR + 'opencv.tag') +files_dict = {} + +# constants and function from opencv.tag +namespaces = root.findall("./compound[@kind='namespace']") +#print("Found {} namespaces".format(len(namespaces))) +for ns in namespaces: + ns_name = ns.find("./name").text + #print('NS: {}'.format(ns_name)) + doxygen_scan.scan_namespace_constants(ns, ns_name, files_dict) + doxygen_scan.scan_namespace_functions(ns, ns_name, files_dict) + +# class methods from opencv.tag +classes = root.findall("./compound[@kind='class']") +#print("Found {} classes".format(len(classes))) +for c in classes: + c_name = c.find("./name").text + file = c.find("./filename").text + #print('Class: {} => {}'.format(c_name, file)) + doxygen_scan.scan_class_methods(c, c_name, files_dict) + +print('Doxygen files to scan: %s' % len(files_dict)) + +files_processed = 0 +files_skipped = 0 +symbols_processed = 0 + +for file in files_dict: + #if file != "dd/d9e/classcv_1_1VideoWriter.html": + #if file != "d4/d86/group__imgproc__filter.html": + #if file != "df/dfb/group__imgproc__object.html": + # continue + #print('File: ' + file) + + anchor_list = files_dict[file] + active_anchors = [a for a in anchor_list if a.cppname in python_signatures] + if len(active_anchors) == 0: # no linked Python symbols + #print('Skip: ' + file) + files_skipped = files_skipped + 1 + continue + + active_anchors_dict = {a.anchor: a for a in active_anchors} + if len(active_anchors_dict) != len(active_anchors): + logging.info('Duplicate entries detected: %s -> %s (%s)' % (len(active_anchors), len(active_anchors_dict), file)) + + files_processed = files_processed + 1 + + #pprint(active_anchors) + symbols_processed = symbols_processed + len(active_anchors_dict) + + logging.info('File: %r' % file) + html_functions.insert_python_signatures(python_signatures, active_anchors_dict, ROOT_DIR + file) + +print('Done (processed files %d, symbols %d, skipped %d files)' % (files_processed, symbols_processed, files_skipped)) diff --git a/doc/tools/doxygen_scan.py b/doc/tools/doxygen_scan.py new file mode 100644 index 0000000000..a1ce60f618 --- /dev/null +++ b/doc/tools/doxygen_scan.py @@ -0,0 +1,48 @@ +import traceback + +class Symbol(object): + def __init__(self, anchor, type, cppname): + self.anchor = anchor + self.type = type + self.cppname = cppname + #if anchor == 'ga586ebfb0a7fb604b35a23d85391329be': + # print(repr(self)) + # traceback.print_stack() + + def __repr__(self): + return '%s:%s@%s' % (self.type, self.cppname, self.anchor) + +def add_to_file(files_dict, file, anchor): + anchors = files_dict.setdefault(file, []) + anchors.append(anchor) + + +def scan_namespace_constants(ns, ns_name, files_dict): + constants = ns.findall("./member[@kind='enumvalue']") + for c in constants: + c_name = c.find("./name").text + name = ns_name + '::' + c_name + file = c.find("./anchorfile").text + anchor = c.find("./anchor").text + #print(' CONST: {} => {}#{}'.format(name, file, anchor)) + add_to_file(files_dict, file, Symbol(anchor, "const", name)) + +def scan_namespace_functions(ns, ns_name, files_dict): + functions = ns.findall("./member[@kind='function']") + for f in functions: + f_name = f.find("./name").text + name = ns_name + '::' + f_name + file = f.find("./anchorfile").text + anchor = f.find("./anchor").text + #print(' FN: {} => {}#{}'.format(name, file, anchor)) + add_to_file(files_dict, file, Symbol(anchor, "fn", name)) + +def scan_class_methods(c, c_name, files_dict): + methods = c.findall("./member[@kind='function']") + for m in methods: + m_name = m.find("./name").text + name = c_name + '::' + m_name + file = m.find("./anchorfile").text + anchor = m.find("./anchor").text + #print(' Method: {} => {}#{}'.format(name, file, anchor)) + add_to_file(files_dict, file, Symbol(anchor, "method", name)) diff --git a/doc/tools/html_functions.py b/doc/tools/html_functions.py new file mode 100644 index 0000000000..c3fd3bd8e9 --- /dev/null +++ b/doc/tools/html_functions.py @@ -0,0 +1,125 @@ +from __future__ import print_function +import sys + +import logging +import os +from pprint import pprint +import traceback + +try: + import bs4 + from bs4 import BeautifulSoup +except ImportError: + raise ImportError('Error: ' + 'Install BeautifulSoup (bs4) for adding' + ' Python & Java signatures documentation') + +def load_html_file(file_dir): + """ Uses BeautifulSoup to load an html """ + with open(file_dir, 'rb') as fp: + soup = BeautifulSoup(fp, 'html.parser') + return soup + +def update_html(file, soup): + s = str(soup) + if os.name == 'nt' or sys.version_info[0] == 3: # if Windows + s = s.encode('utf-8', 'ignore') + with open(file, 'wb') as f: + f.write(s) + + +def insert_python_signatures(python_signatures, symbols_dict, filepath): + soup = load_html_file(filepath) + entries = soup.find_all(lambda tag: tag.name == "a" and tag.has_attr('id')) + for e in entries: + anchor = e['id'] + if anchor in symbols_dict: + s = symbols_dict[anchor] + logging.info('Process: %r' % s) + if s.type == 'fn' or s.type == 'method': + process_fn(soup, e, python_signatures[s.cppname], s) + elif s.type == 'const': + process_const(soup, e, python_signatures[s.cppname], s) + else: + logging.error('unsupported type: %s' % s); + + update_html(filepath, soup) + + +def process_fn(soup, anchor, python_signature, symbol): + try: + r = anchor.find_next_sibling(class_='memitem').find(class_='memproto').find('table') + insert_python_fn_signature(soup, r, python_signature, symbol) + except: + logging.error("Can't process: %s" % symbol) + traceback.print_exc() + pprint(anchor) + + +def process_const(soup, anchor, python_signature, symbol): + try: + #pprint(anchor.parent) + description = append(soup.new_tag('div', **{'class' : ['python_language']}), + 'Python: ' + python_signature[0]['name']) + old = anchor.find_next_sibling('div', class_='python_language') + if old is None: + anchor.parent.append(description) + else: + old.replace_with(description) + #pprint(anchor.parent) + except: + logging.error("Can't process: %s" % symbol) + traceback.print_exc() + pprint(anchor) + + +def insert_python_fn_signature(soup, table, variants, symbol): + description = create_python_fn_description(soup, variants) + description['class'] = 'python_language' + soup = insert_or_replace(table, description, 'table', 'python_language') + return soup + + +def create_python_fn_description(soup, variants): + language = 'Python:' + table = soup.new_tag('table') + heading_row = soup.new_tag('th') + table.append( + append(soup.new_tag('tr'), + append(soup.new_tag('th', colspan=999, style="text-align:left"), language))) + for v in variants: + #logging.debug(v) + add_signature_to_table(soup, table, v, language, type) + #print(table) + return table + + +def add_signature_to_table(soup, table, signature, language, type): + """ Add a signature to an html table""" + row = soup.new_tag('tr') + row.append(soup.new_tag('td', style='width: 20px;')) + + if 'ret' in signature: + row.append(append(soup.new_tag('td'), signature['ret'])) + row.append(append(soup.new_tag('td'), '=')) + else: + row.append(soup.new_tag('td')) # return values + row.append(soup.new_tag('td')) # '=' + + row.append(append(soup.new_tag('td'), signature['name'] + '(')) + row.append(append(soup.new_tag('td', **{'class': 'paramname'}), signature['arg'])) + row.append(append(soup.new_tag('td'), ')')) + table.append(row) + + +def append(target, obj): + target.append(obj) + return target + + +def insert_or_replace(element_before, new_element, tag, tag_class): + old = element_before.find_next_sibling(tag, class_=tag_class) + if old is None: + element_before.insert_after(new_element) + else: + old.replace_with(new_element) diff --git a/modules/python/CMakeLists.txt b/modules/python/CMakeLists.txt index d85a516791..dffe9a794e 100644 --- a/modules/python/CMakeLists.txt +++ b/modules/python/CMakeLists.txt @@ -2,6 +2,8 @@ # CMake file for python support # ---------------------------------------------------------------------------- +add_subdirectory(bindings) + if(ANDROID OR APPLE_FRAMEWORK OR WINRT) set(__disable_python2 ON) set(__disable_python3 ON) diff --git a/modules/python/bindings/CMakeLists.txt b/modules/python/bindings/CMakeLists.txt new file mode 100644 index 0000000000..e407ea9a11 --- /dev/null +++ b/modules/python/bindings/CMakeLists.txt @@ -0,0 +1,69 @@ +set(MODULE_NAME "python_bindings_generator") +ocv_add_module(${MODULE_NAME} INTERNAL) + +set(OPENCV_PYTHON_SIGNATURES_FILE "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_signatures.json" CACHE INTERNAL "") +set(OPENCV_PYTHON_BINDINGS_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE INTERNAL "") + +# This file is included from a subdirectory +set(PYTHON_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../") + +# get list of modules to wrap +set(OPENCV_PYTHON_MODULES) +foreach(m ${OPENCV_MODULES_BUILD}) + if (";${OPENCV_MODULE_${m}_WRAPPERS};" MATCHES ";${MODULE_NAME};" AND HAVE_${m}) + list(APPEND OPENCV_PYTHON_MODULES ${m}) + #message(STATUS "\t${m}") + endif() +endforeach() + +set(opencv_hdrs "") +set(opencv_userdef_hdrs "") +foreach(m ${OPENCV_PYTHON_MODULES}) + list(APPEND opencv_hdrs ${OPENCV_MODULE_${m}_HEADERS}) + file(GLOB userdef_hdrs ${OPENCV_MODULE_${m}_LOCATION}/misc/python/pyopencv*.hpp) + list(APPEND opencv_userdef_hdrs ${userdef_hdrs}) +endforeach(m) + +# header blacklist +ocv_list_filterout(opencv_hdrs "modules/.*\\\\.h$") +ocv_list_filterout(opencv_hdrs "modules/core/.*/cuda") +ocv_list_filterout(opencv_hdrs "modules/cuda.*") +ocv_list_filterout(opencv_hdrs "modules/cudev") +ocv_list_filterout(opencv_hdrs "modules/core/.*/hal/") +ocv_list_filterout(opencv_hdrs "modules/.+/utils/.*") +ocv_list_filterout(opencv_hdrs "modules/.*\\\\.inl\\\\.h*") +ocv_list_filterout(opencv_hdrs "modules/.*_inl\\\\.h*") +ocv_list_filterout(opencv_hdrs "modules/.*\\\\.details\\\\.h*") +ocv_list_filterout(opencv_hdrs "modules/.*/detection_based_tracker\\\\.hpp") # Conditional compilation + +set(cv2_generated_hdrs + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_include.h" + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_funcs.h" + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_types.h" + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_type_reg.h" + "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_ns_reg.h" +) + +set(cv2_generated_files ${cv2_generated_hdrs} + "${OPENCV_PYTHON_SIGNATURES_FILE}" +) + +string(REPLACE ";" "\n" opencv_hdrs_ "${opencv_hdrs}") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/headers.txt" "${opencv_hdrs_}") +add_custom_command( + OUTPUT ${cv2_generated_files} + COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${PYTHON_SOURCE_DIR}/src2/gen2.py" ${CMAKE_CURRENT_BINARY_DIR} "${CMAKE_CURRENT_BINARY_DIR}/headers.txt" + DEPENDS ${PYTHON_SOURCE_DIR}/src2/gen2.py + DEPENDS ${PYTHON_SOURCE_DIR}/src2/hdr_parser.py + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/headers.txt + DEPENDS ${opencv_hdrs} + COMMENT "Generate files for Python bindings and documentation" +) + +add_custom_target(gen_opencv_python_source DEPENDS ${cv2_generated_files}) + +set(cv2_custom_hdr "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_custom_headers.h") +file(WRITE ${cv2_custom_hdr} "//user-defined headers\n") +foreach(uh ${opencv_userdef_hdrs}) + file(APPEND ${cv2_custom_hdr} "#include \"${uh}\"\n") +endforeach(uh) diff --git a/modules/python/common.cmake b/modules/python/common.cmake index 9f87a043a3..fcccffe2ed 100644 --- a/modules/python/common.cmake +++ b/modules/python/common.cmake @@ -1,6 +1,17 @@ # This file is included from a subdirectory set(PYTHON_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../") +ocv_add_module(${MODULE_NAME} BINDINGS PRIVATE_REQUIRED opencv_python_bindings_generator) + +ocv_module_include_directories( + "${${PYTHON}_INCLUDE_PATH}" +) +include_directories( + ${${PYTHON}_NUMPY_INCLUDE_DIRS} + "${PYTHON_SOURCE_DIR}/src2" + "${OPENCV_PYTHON_BINDINGS_DIR}" +) + # try to use dynamic symbols linking with libpython.so set(OPENCV_FORCE_PYTHON_LIBS OFF CACHE BOOL "") string(REPLACE "-Wl,--no-undefined" "" CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS}") @@ -8,80 +19,8 @@ if(NOT WIN32 AND NOT APPLE AND NOT OPENCV_PYTHON_SKIP_LINKER_EXCLUDE_LIBS) set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,--exclude-libs=ALL") endif() -ocv_add_module(${MODULE_NAME} BINDINGS) - -ocv_module_include_directories( - "${${PYTHON}_INCLUDE_PATH}" - ${${PYTHON}_NUMPY_INCLUDE_DIRS} - "${PYTHON_SOURCE_DIR}/src2" - ) - -# get list of modules to wrap -# message(STATUS "Wrapped in ${MODULE_NAME}:") -set(OPENCV_PYTHON_MODULES) -foreach(m ${OPENCV_MODULES_BUILD}) - if (";${OPENCV_MODULE_${m}_WRAPPERS};" MATCHES ";${MODULE_NAME};" AND HAVE_${m}) - list(APPEND OPENCV_PYTHON_MODULES ${m}) - # message(STATUS "\t${m}") - endif() -endforeach() - -set(opencv_hdrs "") -set(opencv_userdef_hdrs "") -foreach(m ${OPENCV_PYTHON_MODULES}) - list(APPEND opencv_hdrs ${OPENCV_MODULE_${m}_HEADERS}) - file(GLOB userdef_hdrs ${OPENCV_MODULE_${m}_LOCATION}/misc/python/pyopencv*.hpp) - list(APPEND opencv_userdef_hdrs ${userdef_hdrs}) -endforeach(m) - -# header blacklist -ocv_list_filterout(opencv_hdrs "modules/.*\\\\.h$") -ocv_list_filterout(opencv_hdrs "modules/core/.*/cuda") -ocv_list_filterout(opencv_hdrs "modules/cuda.*") -ocv_list_filterout(opencv_hdrs "modules/cudev") -ocv_list_filterout(opencv_hdrs "modules/core/.*/hal/") -ocv_list_filterout(opencv_hdrs "modules/.+/utils/.*") -ocv_list_filterout(opencv_hdrs "modules/.*\\\\.inl\\\\.h*") -ocv_list_filterout(opencv_hdrs "modules/.*_inl\\\\.h*") -ocv_list_filterout(opencv_hdrs "modules/.*\\\\.details\\\\.h*") -ocv_list_filterout(opencv_hdrs "modules/.*/detection_based_tracker\\\\.hpp") # Conditional compilation - -set(cv2_generated_hdrs - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_include.h" - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_funcs.h" - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_types.h" - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_type_reg.h" - "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_generated_ns_reg.h" -) - -set(OPENCV_${PYTHON}_SIGNATURES_FILE "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_signatures.json" CACHE INTERNAL "") - -set(cv2_generated_files ${cv2_generated_hdrs} - "${OPENCV_${PYTHON}_SIGNATURES_FILE}" -) - -string(REPLACE ";" "\n" opencv_hdrs_ "${opencv_hdrs}") -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/headers.txt" "${opencv_hdrs_}") -add_custom_command( - OUTPUT ${cv2_generated_files} - COMMAND ${PYTHON_DEFAULT_EXECUTABLE} "${PYTHON_SOURCE_DIR}/src2/gen2.py" ${CMAKE_CURRENT_BINARY_DIR} "${CMAKE_CURRENT_BINARY_DIR}/headers.txt" "${PYTHON}" - DEPENDS ${PYTHON_SOURCE_DIR}/src2/gen2.py - DEPENDS ${PYTHON_SOURCE_DIR}/src2/hdr_parser.py - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/headers.txt - DEPENDS ${opencv_hdrs} - COMMENT "Generate files for ${the_module}" -) - -add_custom_target(gen_${the_module} DEPENDS ${cv2_generated_files}) - -set(cv2_custom_hdr "${CMAKE_CURRENT_BINARY_DIR}/pyopencv_custom_headers.h") -file(WRITE ${cv2_custom_hdr} "//user-defined headers\n") -foreach(uh ${opencv_userdef_hdrs}) - file(APPEND ${cv2_custom_hdr} "#include \"${uh}\"\n") -endforeach(uh) - ocv_add_library(${the_module} MODULE ${PYTHON_SOURCE_DIR}/src2/cv2.cpp ${cv2_generated_hdrs} ${opencv_userdef_hdrs} ${cv2_custom_hdr}) -add_dependencies(${the_module} gen_${the_module}) +add_dependencies(${the_module} gen_opencv_python_source) if(APPLE) set_target_properties(${the_module} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") @@ -92,7 +31,10 @@ elseif(WIN32 OR OPENCV_FORCE_PYTHON_LIBS) ocv_target_link_libraries(${the_module} LINK_PRIVATE ${${PYTHON}_LIBRARIES}) endif() endif() -ocv_target_link_libraries(${the_module} LINK_PRIVATE ${OPENCV_MODULE_${the_module}_DEPS}) + +set(deps ${OPENCV_MODULE_${the_module}_DEPS}) +list(REMOVE_ITEM deps opencv_python_bindings_generator) # don't add dummy module to target_link_libraries list +ocv_target_link_libraries(${the_module} LINK_PRIVATE ${deps}) if(DEFINED ${PYTHON}_CVPY_SUFFIX) set(CVPY_SUFFIX "${${PYTHON}_CVPY_SUFFIX}") diff --git a/modules/python/src2/cv2.cpp b/modules/python/src2/cv2.cpp index 30c84f46b9..8f1eb3a214 100644 --- a/modules/python/src2/cv2.cpp +++ b/modules/python/src2/cv2.cpp @@ -9,6 +9,12 @@ #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include +#if PY_MAJOR_VERSION >= 3 +# define CV_PYTHON_TYPE_HEAD_INIT() PyVarObject_HEAD_INIT(&PyType_Type, 0) +#else +# define CV_PYTHON_TYPE_HEAD_INIT() PyObject_HEAD_INIT(&PyType_Type) 0, +#endif + #include "pyopencv_generated_include.h" #include "opencv2/core/types_c.h" diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py index 4f15715ccc..27928bc485 100755 --- a/modules/python/src2/gen2.py +++ b/modules/python/src2/gen2.py @@ -46,20 +46,7 @@ gen_template_func_body = Template("""$code_decl } """) -py_major_version = sys.version_info[0] -if __name__ == "__main__": - if len(sys.argv) > 3: - if sys.argv[3] == 'PYTHON3': - py_major_version = 3 - elif sys.argv[3] == 'PYTHON2': - py_major_version = 2 - else: - raise Exception('Incorrect argument: expected PYTHON2 or PYTHON3, received: ' + sys.argv[3]) -if py_major_version >= 3: - head_init_str = "PyVarObject_HEAD_INIT(&PyType_Type, 0)" -else: - head_init_str = """PyObject_HEAD_INIT(&PyType_Type) -0,""" +head_init_str = "CV_PYTHON_TYPE_HEAD_INIT()" gen_template_simple_type_decl = Template(""" struct pyopencv_${name}_t