Repository: opencv/opencv
Pull request #27325: feat: add conditional inclusion support to header parser
Author: login: VadimLevin
Labels: name: bug
name: category: python bindings
Created: 2025-05-16T10:13:56Z | Merged: 2025-05-20T15:11:12Z

Brings support for conditional inclusion directives evaluation and proper `#if`/`#else`/`#elif` branches support.

Generated diffs are [attached](https://github.com/user-attachments/files/20241712/Archive.zip):

`cv::Feature2D` has a valid base class now, because `#else` is not discarded

```cpp
#ifdef __EMSCRIPTEN__
class CV_EXPORTS_W Feature2D : public Algorithm
#else
class CV_EXPORTS_W Feature2D : public virtual Algorithm
#endif
```

Build warning is resolved:
```
Note: Class Feature2D has more than 1 base class (not supported by Python C extensions)
      Bases:  cv::Algorithm, cv::class, cv::Feature2D, cv::Algorithm
      Only the first base class will be used
```

Related diff in `pyopencv_generated_types.h`
```cpp
# Before
CVPY_TYPE(Feature2D, Feature2D, Ptr<cv::Feature2D>, Ptr, NoBase, 0, "")
# After
CVPY_TYPE(Feature2D, Feature2D, Ptr<cv::Feature2D>, Ptr, Algorithm, 0, "")
```

Duplicate for `cv::RotationWarper::warpPointBackward` is removed from function dispatch.
```cpp
#if CV_VERSION_MAJOR == 4
    virtual Point2f warpPointBackward(const Point2f& pt, InputArray K, InputArray R)
    {
        CV_UNUSED(pt); CV_UNUSED(K); CV_UNUSED(R);
        CV_Error(Error::StsNotImplemented, "");
    }
#else
    virtual Point2f warpPointBackward(const Point2f& pt, InputArray K, InputArray R) = 0;
#endif

```

Related diff in `pyopencv_generated_types_content.h`

<details>
  <summary>Before</summary>

```cpp
static PyObject* pyopencv_cv_PyRotationWarper_warpPointBackward(PyObject* self, PyObject* py_args, PyObject* kw)
{
    using namespace cv;


    Ptr<cv::PyRotationWarper> * self1 = 0;
    if (!pyopencv_PyRotationWarper_getp(self, self1))
        return failmsgp("Incorrect type of self (must be 'PyRotationWarper' or its derivative)");
    Ptr<cv::PyRotationWarper> _self_ = *(self1);
    pyPrepareArgumentConversionErrorsStorage(4);

    {
    PyObject* pyobj_pt = NULL;
    Point2f pt;
    PyObject* pyobj_K = NULL;
    Mat K;
    PyObject* pyobj_R = NULL;
    Mat R;
    Point2f retval;

    const char* keywords[] = { "pt", "K", "R", NULL };
    if( PyArg_ParseTupleAndKeywords(py_args, kw, "OOO:PyRotationWarper.warpPointBackward", (char**)keywords, &pyobj_pt, &pyobj_K, &pyobj_R) &&
        pyopencv_to_safe(pyobj_pt, pt, ArgInfo("pt", 0)) &&
        pyopencv_to_safe(pyobj_K, K, ArgInfo("K", 0)) &&
        pyopencv_to_safe(pyobj_R, R, ArgInfo("R", 0)) )
    {
        ERRWRAP2(retval = _self_->warpPointBackward(pt, K, R));
        return pyopencv_from(retval);
    }


        pyPopulateArgumentConversionErrors();
    }
    

    {
    PyObject* pyobj_pt = NULL;
    Point2f pt;
    PyObject* pyobj_K = NULL;
    Mat K;
    PyObject* pyobj_R = NULL;
    Mat R;
    Point2f retval;

    const char* keywords[] = { "pt", "K", "R", NULL };
    if( PyArg_ParseTupleAndKeywords(py_args, kw, "OOO:PyRotationWarper.warpPointBackward", (char**)keywords, &pyobj_pt, &pyobj_K, &pyobj_R) &&
        pyopencv_to_safe(pyobj_pt, pt, ArgInfo("pt", 0)) &&
        pyopencv_to_safe(pyobj_K, K, ArgInfo("K", 0)) &&
        pyopencv_to_safe(pyobj_R, R, ArgInfo("R", 0)) )
    {
        ERRWRAP2(retval = _self_->warpPointBackward(pt, K, R));
        return pyopencv_from(retval);
    }


        pyPopulateArgumentConversionErrors();
    }
    

    {
    PyObject* pyobj_pt = NULL;
    Point2f pt;
    PyObject* pyobj_K = NULL;
    UMat K;
    PyObject* pyobj_R = NULL;
    UMat R;
    Point2f retval;

    const char* keywords[] = { "pt", "K", "R", NULL };
    if( PyArg_ParseTupleAndKeywords(py_args, kw, "OOO:PyRotationWarper.warpPointBackward", (char**)keywords, &pyobj_pt, &pyobj_K, &pyobj_R) &&
        pyopencv_to_safe(pyobj_pt, pt, ArgInfo("pt", 0)) &&
        pyopencv_to_safe(pyobj_K, K, ArgInfo("K", 0)) &&
        pyopencv_to_safe(pyobj_R, R, ArgInfo("R", 0)) )
    {
        ERRWRAP2(retval = _self_->warpPointBackward(pt, K, R));
        return pyopencv_from(retval);
    }


        pyPopulateArgumentConversionErrors();
    }
    

    {
    PyObject* pyobj_pt = NULL;
    Point2f pt;
    PyObject* pyobj_K = NULL;
    UMat K;
    PyObject* pyobj_R = NULL;
    UMat R;
    Point2f retval;

    const char* keywords[] = { "pt", "K", "R", NULL };
    if( PyArg_ParseTupleAndKeywords(py_args, kw, "OOO:PyRotationWarper.warpPointBackward", (char**)keywords, &pyobj_pt, &pyobj_K, &pyobj_R) &&
        pyopencv_to_safe(pyobj_pt, pt, ArgInfo("pt", 0)) &&
        pyopencv_to_safe(pyobj_K, K, ArgInfo("K", 0)) &&
        pyopencv_to_safe(pyobj_R, R, ArgInfo("R", 0)) )
    {
        ERRWRAP2(retval = _self_->warpPointBackward(pt, K, R));
        return pyopencv_from(retval);
    }


        pyPopulateArgumentConversionErrors();
    }
    pyRaiseCVOverloadException("warpPointBackward");

    return NULL;
}
```
</details>

<details>
  <summary>After</summary>

```cpp
static PyObject* pyopencv_cv_PyRotationWarper_warpPointBackward(PyObject* self, PyObject* py_args, PyObject* kw)
{
    using namespace cv;


    Ptr<cv::PyRotationWarper> * self1 = 0;
    if (!pyopencv_PyRotationWarper_getp(self, self1))
        return failmsgp("Incorrect type of self (must be 'PyRotationWarper' or its derivative)");
    Ptr<cv::PyRotationWarper> _self_ = *(self1);
    pyPrepareArgumentConversionErrorsStorage(2);

    {
    PyObject* pyobj_pt = NULL;
    Point2f pt;
    PyObject* pyobj_K = NULL;
    Mat K;
    PyObject* pyobj_R = NULL;
    Mat R;
    Point2f retval;

    const char* keywords[] = { "pt", "K", "R", NULL };
    if( PyArg_ParseTupleAndKeywords(py_args, kw, "OOO:PyRotationWarper.warpPointBackward", (char**)keywords, &pyobj_pt, &pyobj_K, &pyobj_R) &&
        pyopencv_to_safe(pyobj_pt, pt, ArgInfo("pt", 0)) &&
        pyopencv_to_safe(pyobj_K, K, ArgInfo("K", 0)) &&
        pyopencv_to_safe(pyobj_R, R, ArgInfo("R", 0)) )
    {
        ERRWRAP2(retval = _self_->warpPointBackward(pt, K, R));
        return pyopencv_from(retval);
    }


        pyPopulateArgumentConversionErrors();
    }
    

    {
    PyObject* pyobj_pt = NULL;
    Point2f pt;
    PyObject* pyobj_K = NULL;
    UMat K;
    PyObject* pyobj_R = NULL;
    UMat R;
    Point2f retval;

    const char* keywords[] = { "pt", "K", "R", NULL };
    if( PyArg_ParseTupleAndKeywords(py_args, kw, "OOO:PyRotationWarper.warpPointBackward", (char**)keywords, &pyobj_pt, &pyobj_K, &pyobj_R) &&
        pyopencv_to_safe(pyobj_pt, pt, ArgInfo("pt", 0)) &&
        pyopencv_to_safe(pyobj_K, K, ArgInfo("K", 0)) &&
        pyopencv_to_safe(pyobj_R, R, ArgInfo("R", 0)) )
    {
        ERRWRAP2(retval = _self_->warpPointBackward(pt, K, R));
        return pyopencv_from(retval);
    }


        pyPopulateArgumentConversionErrors();
    }
    pyRaiseCVOverloadException("warpPointBackward");

    return NULL;
}
```
</details

Closes #27250
Closes #27064 
Closes #10175 
Closes #12972 

### 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
- [x] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake


Linked issue #27250: Optional DNN dependency causes Python bindings to fail
### System Information

OpenCV version: 5.0.0-pre
Operating System / Platform: Ubuntu 24.04
Compiler & compiler version: GCC 14.2.0

### Detailed description

When a module optionally depends on the opencv_dnn module, and CV_WRAP functions are conditionally defined using #ifdef HAVE_OPENCV_DNN, the Python binding generator ignores these conditionals and generates bindings for all CV_WRAP functions regardless of the presence of the DNN module. This leads to build errors when the DNN module is not available.

**Example:Module definition:**

```
ocv_define_module(objdetect
    opencv_core
    opencv_imgproc
    opencv_3d
    OPTIONAL
        opencv_dnn
    WRAP
        python
        java
        objc
        js
)
```
**Conditional function definition:**
```
#ifdef HAVE_OPENCV_DNN
    /**
     * @brief Set the net which will be used to find the approximate
     * bounding boxes for the color charts.
     * @param net the neural network.
     */
    CV_WRAP static Ptr<CCheckerDetector> create(const dnn::Net &net);
#endif
```

 **Current Behavior:** 

- Python binding generator ignores #ifdef HAVE_OPENCV_DNN.
- Generates bindings for the CV_WRAP function unconditionally.
- Build fails due to missing opencv_dnn.

**Expected Behavior:** 

- Python binding generator should respect conditional compilation (#ifdef HAVE_OPENCV_DNN) and only generate bindings if the opencv_dnn module is present.

### Steps to reproduce

Build the objdetect module with Python bindings solely (without DNN) in branch 5.x

### Issue submission checklist

- [x] I report the issue, it's not a question
- [x] I checked the problem with documentation, FAQ, open issues, forum.opencv.org, Stack Overflow, etc and have not found any solution
- [x] I updated to the latest OpenCV version and the issue is still there
- [x] There is reproducer code and related data files (videos, images, onnx, etc)
Linked issue comments: user: login: asmorkalov
created_at: 2025-04-22T07:59:56Z
body: Duplicates https://github.com/opencv/opencv/issues/27064. @VadimLevin Works on the solution for header parser.
author_association: CONTRIBUTOR

Diff:
diff --git a/cmake/OpenCVBindingsPreprocessorDefinitions.cmake b/cmake/OpenCVBindingsPreprocessorDefinitions.cmake
new file mode 100644
index 000000000000..2828e638a7aa
--- /dev/null
+++ b/cmake/OpenCVBindingsPreprocessorDefinitions.cmake
@@ -0,0 +1,63 @@
+function(ocv_bindings_generator_populate_preprocessor_definitions
+         opencv_modules
+         output_variable)
+  set(defs "\"CV_VERSION_MAJOR\": ${OPENCV_VERSION_MAJOR}")
+
+  macro(ocv_add_definition name value)
+    set(defs "${defs},\n\"${name}\": ${value}")
+  endmacro()
+
+  ocv_add_definition(CV_VERSION_MINOR ${OPENCV_VERSION_MINOR})
+  ocv_add_definition(CV_VERSION_PATCH ${OPENCV_VERSION_PATCH})
+  ocv_add_definition(OPENCV_ABI_COMPATIBILITY "${OPENCV_VERSION_MAJOR}00")
+
+  foreach(module IN LISTS ${opencv_modules})
+    if(HAVE_${module})
+        string(TOUPPER "${module}" module)
+        ocv_add_definition("HAVE_${module}" 1)
+    endif()
+  endforeach()
+  if(HAVE_EIGEN)
+    ocv_add_definition(HAVE_EIGEN 1)
+    ocv_add_definition(EIGEN_WORLD_VERSION ${EIGEN_WORLD_VERSION})
+    ocv_add_definition(EIGEN_MAJOR_VERSION ${EIGEN_MAJOR_VERSION})
+    ocv_add_definition(EIGEN_MINOR_VERSION ${EIGEN_MINOR_VERSION})
+  else()
+    # Some checks in parsed headers might not be protected with HAVE_EIGEN check
+    ocv_add_definition(EIGEN_WORLD_VERSION 0)
+    ocv_add_definition(EIGEN_MAJOR_VERSION 0)
+    ocv_add_definition(EIGEN_MINOR_VERSION 0)
+  endif()
+  if(HAVE_LAPACK)
+    ocv_add_definition(HAVE_LAPACK 1)
+  endif()
+
+  if(OPENCV_DISABLE_FILESYSTEM_SUPPORT)
+    ocv_add_definition(OPENCV_HAVE_FILESYSTEM_SUPPORT 0)
+  else()
+    ocv_add_definition(OPENCV_HAVE_FILESYSTEM_SUPPORT 1)
+  endif()
+
+  ocv_add_definition(OPENCV_BINDINGS_PARSER 1)
+
+  # Implementation details definitions, having no impact on how bindings are
+  # generated, so their real values can be safely ignored
+  ocv_add_definition(CV_ENABLE_UNROLLED 0)
+  ocv_add_definition(CV__EXCEPTION_PTR 0)
+  ocv_add_definition(CV_NEON 0)
+  ocv_add_definition(TBB_INTERFACE_VERSION 0)
+  ocv_add_definition(CV_SSE2 0)
+  ocv_add_definition(CV_VSX 0)
+  ocv_add_definition(OPENCV_SUPPORTS_FP_DENORMALS_HINT 0)
+  ocv_add_definition(CV_LOG_STRIP_LEVEL 0)
+  ocv_add_definition(CV_LOG_LEVEL_SILENT 0)
+  ocv_add_definition(CV_LOG_LEVEL_FATAL 1)
+  ocv_add_definition(CV_LOG_LEVEL_ERROR 2)
+  ocv_add_definition(CV_LOG_LEVEL_WARN 3)
+  ocv_add_definition(CV_LOG_LEVEL_INFO 4)
+  ocv_add_definition(CV_LOG_LEVEL_DEBUG 5)
+  ocv_add_definition(CV_LOG_LEVEL_VERBOSE 6)
+  ocv_add_definition(CERES_FOUND 0)
+
+  set(${output_variable} ${defs} PARENT_SCOPE)
+endfunction()
diff --git a/modules/java/generator/CMakeLists.txt b/modules/java/generator/CMakeLists.txt
index b8ae34023be0..130e6d5fec25 100644
--- a/modules/java/generator/CMakeLists.txt
+++ b/modules/java/generator/CMakeLists.txt
@@ -56,6 +56,12 @@ foreach(m ${OPENCV_JAVA_MODULES})
   ocv_remap_files(misc_files)
 endforeach(m)
 
+include("${OpenCV_SOURCE_DIR}/cmake/OpenCVBindingsPreprocessorDefinitions.cmake")
+ocv_bindings_generator_populate_preprocessor_definitions(
+  OPENCV_MODULES_BUILD
+  opencv_preprocessor_defs
+)
+
 set(CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/gen_java.json")
 set(__config_str
 "{
@@ -63,6 +69,9 @@ set(__config_str
   \"modules\": [
 ${__modules_config}
   ],
+  \"preprocessor_definitions\": {
+${opencv_preprocessor_defs}
+  },
   \"files_remap\": [
 ${__remap_config}
   ]
diff --git a/modules/java/generator/gen_java.py b/modules/java/generator/gen_java.py
index a77ca199f34f..0ffa5bd6ae72 100755
--- a/modules/java/generator/gen_java.py
+++ b/modules/java/generator/gen_java.py
@@ -591,12 +591,16 @@ def save(self, path, buf):
             f.write(buf)
         updated_files += 1
 
-    def gen(self, srcfiles, module, output_path, output_jni_path, output_java_path, common_headers):
+    def gen(self, srcfiles, module, output_path, output_jni_path, output_java_path, common_headers,
+            preprocessor_definitions=None):
         self.clear()
         self.module = module
         self.Module = module.capitalize()
         # TODO: support UMat versions of declarations (implement UMat-wrapper for Java)
-        parser = hdr_parser.CppHeaderParser(generate_umat_decls=False)
+        parser = hdr_parser.CppHeaderParser(
+            generate_umat_decls=False,
+            preprocessor_definitions=preprocessor_definitions
+        )
 
         self.add_class( ['class cv.' + self.Module, '', [], []] ) # [ 'class/struct cname', ':bases', [modlist] [props] ]
 
@@ -1444,6 +1448,7 @@ def sanitize_java_documentation_string(doc, type):
     gen_dict_files = []
 
     print("JAVA: Processing OpenCV modules: %d" % len(config['modules']))
+    preprocessor_definitions = config.get('preprocessor_definitions', None)
     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))
@@ -1508,7 +1513,8 @@ def sanitize_java_documentation_string(doc, type):
             copy_java_files(java_test_files_dir, java_test_base_path, 'org/opencv/test/' + module)
 
         if len(srcfiles) > 0:
-            generator.gen(srcfiles, module, dstdir, jni_path, java_path, common_headers)
+            generator.gen(srcfiles, module, dstdir, jni_path, java_path, common_headers,
+                          preprocessor_definitions)
         else:
             logging.info("No generated code for module: %s", module)
     generator.finalize(jni_path)
diff --git a/modules/js/generator/CMakeLists.txt b/modules/js/generator/CMakeLists.txt
index c66608e91703..d135b151eb28 100644
--- a/modules/js/generator/CMakeLists.txt
+++ b/modules/js/generator/CMakeLists.txt
@@ -30,7 +30,14 @@ ocv_list_filterout(opencv_hdrs "modules/core/include/opencv2/core/utils/*.privat
 ocv_list_filterout(opencv_hdrs "modules/core/include/opencv2/core/utils/instrumentation.hpp")
 ocv_list_filterout(opencv_hdrs "modules/core/include/opencv2/core/utils/trace*")
 
-ocv_update_file("${CMAKE_CURRENT_BINARY_DIR}/headers.txt" "${opencv_hdrs}")
+set(config_json_headers_list "")
+foreach(header IN LISTS opencv_hdrs)
+  if(NOT config_json_headers_list STREQUAL "")
+    set(config_json_headers_list "${config_json_headers_list},\n\"${header}\"")
+  else()
+    set(config_json_headers_list "\"${header}\"")
+  endif()
+endforeach()
 
 set(bindings_cpp "${OPENCV_JS_BINDINGS_DIR}/gen/bindings.cpp")
 
@@ -55,16 +62,42 @@ else()
   message(STATUS "Use autogenerated whitelist ${OPENCV_JS_WHITELIST_FILE}")
 endif()
 
+include("${OpenCV_SOURCE_DIR}/cmake/OpenCVBindingsPreprocessorDefinitions.cmake")
+ocv_bindings_generator_populate_preprocessor_definitions(
+  OPENCV_MODULES_BUILD
+  opencv_preprocessor_defs
+)
+
+set(__config_str
+"{
+  \"headers\": [
+${config_json_headers_list}
+  ],
+  \"preprocessor_definitions\": {
+${opencv_preprocessor_defs}
+  },
+  \"core_bindings_file_path\": \"${JS_SOURCE_DIR}/src/core_bindings.cpp\"
+}")
+set(JSON_CONFIG_FILE_PATH "${CMAKE_CURRENT_BINARY_DIR}/gen_js_config.json")
+if(EXISTS "${JSON_CONFIG_FILE_PATH}")
+  file(READ "${JSON_CONFIG_FILE_PATH}" __content)
+else()
+  set(__content "")
+endif()
+if(NOT "${__content}" STREQUAL "${__config_str}")
+  file(WRITE "${JSON_CONFIG_FILE_PATH}" "${__config_str}")
+endif()
+unset(__config_str)
+
 add_custom_command(
   OUTPUT ${bindings_cpp} "${OPENCV_DEPHELPER}/gen_opencv_js_source"
   COMMAND
       ${PYTHON_DEFAULT_EXECUTABLE}
       "${CMAKE_CURRENT_SOURCE_DIR}/embindgen.py"
-      "${scripts_hdr_parser}"
-      "${bindings_cpp}"
-      "${CMAKE_CURRENT_BINARY_DIR}/headers.txt"
-      "${JS_SOURCE_DIR}/src/core_bindings.cpp"
-      "${OPENCV_JS_WHITELIST_FILE}"
+      --parser "${scripts_hdr_parser}"
+      --output_file "${bindings_cpp}"
+      --config "${JSON_CONFIG_FILE_PATH}"
+      --whitelist "${OPENCV_JS_WHITELIST_FILE}"
   COMMAND
       ${CMAKE_COMMAND} -E touch "${OPENCV_DEPHELPER}/gen_opencv_js_source"
   WORKING_DIRECTORY
@@ -73,6 +106,7 @@ add_custom_command(
       ${JS_SOURCE_DIR}/src/core_bindings.cpp
       ${CMAKE_CURRENT_SOURCE_DIR}/embindgen.py
       ${CMAKE_CURRENT_SOURCE_DIR}/templates.py
+      ${JSON_CONFIG_FILE_PATH}
       "${OPENCV_JS_WHITELIST_FILE}"
       ${scripts_hdr_parser}
       #(not needed - generated by CMake) ${CMAKE_CURRENT_BINARY_DIR}/headers.txt
diff --git a/modules/js/generator/embindgen.py b/modules/js/generator/embindgen.py
index 835289313326..d5d600e83faf 100644
--- a/modules/js/generator/embindgen.py
+++ b/modules/js/generator/embindgen.py
@@ -319,7 +319,7 @@ def __init__(self):
 
 
 class JSWrapperGenerator(object):
-    def __init__(self):
+    def __init__(self, preprocessor_definitions=None):
 
         self.bindings = []
         self.wrapper_funcs = []
@@ -328,7 +328,9 @@ def __init__(self):
         self.namespaces = {}
         self.enums = {}  # FIXIT 'enums' should belong to 'namespaces'
 
-        self.parser = hdr_parser.CppHeaderParser()
+        self.parser = hdr_parser.CppHeaderParser(
+            preprocessor_definitions=preprocessor_definitions
+        )
         self.class_idx = 0
 
     def add_class(self, stype, name, decl):
@@ -962,41 +964,69 @@ def gen(self, dst_file, src_files, core_bindings):
 
 
 if __name__ == "__main__":
-    if len(sys.argv) < 5:
-        print("Usage:\n", \
-            os.path.basename(sys.argv[0]), \
-            "<full path to hdr_parser.py> <bindings.cpp> <headers.txt> <core_bindings.cpp> <whitelist.json or opencv_js.config.py>")
-        print("Current args are: ", ", ".join(["'"+a+"'" for a in sys.argv]))
-        exit(1)
-
-    dstdir = "."
-    hdr_parser_path = os.path.abspath(sys.argv[1])
+    import argparse
+
+    arg_parser = argparse.ArgumentParser(
+        description="OpenCV JavaScript bindings generator"
+    )
+    arg_parser.add_argument(
+        "-p", "--parser",
+        required=True,
+        help="Full path to OpenCV header parser `hdr_parser.py`"
+    )
+    arg_parser.add_argument(
+        "-o", "--output_file",
+        dest="output_file_path",
+        required=True,
+        help="Path to output file containing js bindings"
+    )
+    arg_parser.add_argument(
+        "-c", "--config",
+        dest="config_json_path",
+        required=True,
+        help="Path to generator configuration file in .json format"
+    )
+    arg_parser.add_argument(
+        "--whitelist",
+        dest="whitelist_file_path",
+        required=True,
+        help="Path to whitelist.js or opencv_js.config.py"
+    )
+    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
 
-    bindingsCpp = sys.argv[2]
-    headers = open(sys.argv[3], 'r').read().split(';')
-    coreBindings = sys.argv[4]
-    whiteListFile = sys.argv[5]
+    with open(args.config_json_path, "r") as fh:
+        config_json = json.load(fh)
+    headers = config_json.get("headers", ())
 
-    if whiteListFile.endswith(".json") or whiteListFile.endswith(".JSON"):
-        with open(whiteListFile) as f:
+    bindings_cpp = args.output_file_path
+    core_bindings_path = config_json["core_bindings_file_path"]
+    whitelist_file_path = args.whitelist_file_path
+
+    if whitelist_file_path.endswith(".json") or whitelist_file_path.endswith(".JSON"):
+        with open(whitelist_file_path) as f:
             gen_dict = json.load(f)
-        f.close()
         white_list = makeWhiteListJson(gen_dict)
         namespace_prefix_override = makeNamespacePrefixOverride(gen_dict)
-    elif whiteListFile.endswith(".py") or whiteListFile.endswith(".PY"):
-        exec(open(whiteListFile).read())
-        assert(white_list)
+    elif whitelist_file_path.endswith(".py") or whitelist_file_path.endswith(".PY"):
+        with open(whitelist_file_path) as fh:
+            exec(fh.read())
+        assert white_list
         namespace_prefix_override = {
             'dnn' : '',
             'aruco' : '',
         }
     else:
-        print("Unexpected format of OpenCV config file", whiteListFile)
+        print("Unexpected format of OpenCV config file", whitelist_file_path)
         exit(1)
 
-    generator = JSWrapperGenerator()
-    generator.gen(bindingsCpp, headers, coreBindings)
+    generator = JSWrapperGenerator(
+        preprocessor_definitions=config_json.get("preprocessor_definitions", None)
+    )
+    generator.gen(bindings_cpp, headers, core_bindings_path)
diff --git a/modules/objc/generator/CMakeLists.txt b/modules/objc/generator/CMakeLists.txt
index bd8f8325b3e7..1d7580f412c8 100644
--- a/modules/objc/generator/CMakeLists.txt
+++ b/modules/objc/generator/CMakeLists.txt
@@ -38,6 +38,13 @@ if(HAVE_opencv_objc)
   set(__objc_build_dir "\"objc_build_dir\": \"${CMAKE_CURRENT_BINARY_DIR}/../objc\",")
 endif()
 
+include("${OpenCV_SOURCE_DIR}/cmake/OpenCVBindingsPreprocessorDefinitions.cmake")
+
+ocv_bindings_generator_populate_preprocessor_definitions(
+  OPENCV_MODULES_BUILD
+  opencv_preprocessor_defs
+)
+
 set(CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/gen_objc.json")
 set(__config_str
 "{
@@ -45,7 +52,10 @@ set(__config_str
   ${__objc_build_dir}
   \"modules\": [
 ${__modules_config}
-  ]
+  ],
+  \"preprocessor_definitions\": {
+${opencv_preprocessor_defs}
+  }
 }
 ")
 #TODO: ocv_update_file("${CONFIG_FILE}" "${__config_str}" ON_CHANGE_REMOVE "${OPENCV_DEPHELPER}/gen_opencv_objc_source")
diff --git a/modules/objc/generator/gen_objc.py b/modules/objc/generator/gen_objc.py
index 74c370e8bfa3..2f6f974a021a 100755
--- a/modules/objc/generator/gen_objc.py
+++ b/modules/objc/generator/gen_objc.py
@@ -894,7 +894,8 @@ 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):
+    def gen(self, srcfiles, module, output_path, output_objc_path,
+            common_headers, manual_classes, preprocessor_definitions=None):
         self.clear()
         self.module = module
         self.objcmodule = make_objcmodule(module)
@@ -903,7 +904,10 @@ def gen(self, srcfiles, module, output_path, output_objc_path, common_headers, m
         extension_signatures = []
 
         # TODO: support UMat versions of declarations (implement UMat-wrapper for Java)
-        parser = hdr_parser.CppHeaderParser(generate_umat_decls=False)
+        parser = hdr_parser.CppHeaderParser(
+            generate_umat_decls=False,
+            preprocessor_definitions=preprocessor_definitions
+        )
 
         module_ci = self.add_class( ['class ' + self.Module, '', [], []]) # [ 'class/struct cname', ':bases', [modlist] [props] ]
         module_ci.header_import = module + '.hpp'
@@ -1715,7 +1719,9 @@ def sanitize_documentation_string(doc, type):
         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)
+            generator.gen(srcfiles, module, dstdir, objc_base_path,
+                          common_headers, manual_classes,
+                          config.get("preprocessor_definitions"))
         else:
             logging.info("No generated code for module: %s", module)
     generator.finalize(args.target, objc_base_path, objc_build_dir)
diff --git a/modules/python/bindings/CMakeLists.txt b/modules/python/bindings/CMakeLists.txt
index 918411864c63..c6db97bf2803 100644
--- a/modules/python/bindings/CMakeLists.txt
+++ b/modules/python/bindings/CMakeLists.txt
@@ -74,12 +74,50 @@ set(cv2_generated_files
     "${OPENCV_PYTHON_SIGNATURES_FILE}"
 )
 
-string(REPLACE ";" "\n" opencv_hdrs_ "${opencv_hdrs}")
-file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/headers.txt" "${opencv_hdrs_}")
+
+set(config_json_headers_list "")
+foreach(header IN LISTS opencv_hdrs)
+  if(NOT config_json_headers_list STREQUAL "")
+    set(config_json_headers_list "${config_json_headers_list},\n\"${header}\"")
+  else()
+    set(config_json_headers_list "\"${header}\"")
+  endif()
+endforeach()
+
+include("${OpenCV_SOURCE_DIR}/cmake/OpenCVBindingsPreprocessorDefinitions.cmake")
+
+ocv_bindings_generator_populate_preprocessor_definitions(
+  OPENCV_MODULES_BUILD
+  opencv_preprocessor_defs
+)
+
+set(__config_str
+"{
+  \"headers\": [
+${config_json_headers_list}
+  ],
+  \"preprocessor_definitions\": {
+${opencv_preprocessor_defs}
+  }
+}")
+
+set(JSON_CONFIG_FILE_PATH "${CMAKE_CURRENT_BINARY_DIR}/gen_python_config.json")
+if(EXISTS "${JSON_CONFIG_FILE_PATH}")
+  file(READ "${JSON_CONFIG_FILE_PATH}" __content)
+else()
+  set(__content "")
+endif()
+if(NOT "${__content}" STREQUAL "${__config_str}")
+  file(WRITE "${JSON_CONFIG_FILE_PATH}" "${__config_str}")
+endif()
+unset(__config_str)
+
 file(GLOB_RECURSE typing_stubs_generation_files "${PYTHON_SOURCE_DIR}/src2/typing_stubs_generation/*.py")
 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"
+    COMMAND "${PYTHON_DEFAULT_EXECUTABLE}" "${PYTHON_SOURCE_DIR}/src2/gen2.py"
+        "--config" "${JSON_CONFIG_FILE_PATH}"
+        "--output_dir" "${CMAKE_CURRENT_BINARY_DIR}"
     DEPENDS "${PYTHON_SOURCE_DIR}/src2/gen2.py"
             "${PYTHON_SOURCE_DIR}/src2/hdr_parser.py"
             "${typing_stubs_generation_files}"
diff --git a/modules/python/src2/gen2.py b/modules/python/src2/gen2.py
index af187e5d3fbe..7d9f75063cec 100755
--- a/modules/python/src2/gen2.py
+++ b/modules/python/src2/gen2.py
@@ -2,6 +2,7 @@
 
 from __future__ import print_function
 import hdr_parser, sys, re
+import json
 from string import Template
 from collections import namedtuple
 from itertools import chain
@@ -1108,6 +1109,18 @@ def __init__(self):
 
 
 class PythonWrapperGenerator(object):
+    class Config:
+        def __init__(self, headers, preprocessor_definitions = None):
+            self.headers = headers
+            if preprocessor_definitions is None:
+                preprocessor_definitions = {}
+            elif not isinstance(preprocessor_definitions, dict):
+                raise TypeError(
+                    "preprocessor_definitions should rather dictionary or None. "
+                    "Got: {}".format(type(preprocessor_definitions).__name__)
+                )
+            self.preprocessor_definitions = preprocessor_definitions
+
     def __init__(self):
         self.clear()
 
@@ -1324,13 +1337,16 @@ def save(self, path, name, buf):
             f.write(buf.getvalue())
 
     def save_json(self, path, name, value):
-        import json
         with open(path + "/" + name, "wt") as f:
             json.dump(value, f)
 
-    def gen(self, srcfiles, output_path):
+    def gen(self, srcfiles, output_path, preprocessor_definitions = None):
         self.clear()
-        self.parser = hdr_parser.CppHeaderParser(generate_umat_decls=True, generate_gpumat_decls=True)
+        self.parser = hdr_parser.CppHeaderParser(
+            generate_umat_decls=True,
+            generate_gpumat_decls=True,
+            preprocessor_definitions=preprocessor_definitions
+        )
 
 
         # step 1: scan the headers and build more descriptive maps of classes, consts, functions
@@ -1502,12 +1518,36 @@ def _registerType(classinfo):
 
 
 if __name__ == "__main__":
-    srcfiles = hdr_parser.opencv_hdr_list
-    dstdir = "/Users/vp/tmp"
-    if len(sys.argv) > 1:
-        dstdir = sys.argv[1]
-    if len(sys.argv) > 2:
-        with open(sys.argv[2], 'r') as f:
-            srcfiles = [l.strip() for l in f.readlines()]
+    import argparse
+    import tempfile
+
+    arg_parser = argparse.ArgumentParser(
+        description="OpenCV Python bindings generator"
+    )
+    arg_parser.add_argument(
+        "-c", "--config",
+        dest="config_json_path",
+        required=False,
+        help="Generator configuration file in .json format"
+        "Refer to PythonWrapperGenerator.Config for available "
+        "configuration keys"
+    )
+    arg_parser.add_argument(
+        "-o", "--output_dir",
+        dest="output_dir",
+        default=tempfile.gettempdir(),
+        help="Generated bindings output directory"
+    )
+    args = arg_parser.parse_args()
+    if args.config_json_path is not None:
+        with open(args.config_json_path, "r") as fh:
+            config_json = json.load(fh)
+        config = PythonWrapperGenerator.Config(**config_json)
+    else:
+        config = PythonWrapperGenerator.Config(
+            headers=hdr_parser.opencv_hdr_list
+        )
+
     generator = PythonWrapperGenerator()
-    generator.gen(srcfiles, dstdir)
+
+    generator.gen(config.headers, args.output_dir, config.preprocessor_definitions)
diff --git a/modules/python/src2/hdr_parser.py b/modules/python/src2/hdr_parser.py
index afcc390dee5a..b5faa61289a5 100755
--- a/modules/python/src2/hdr_parser.py
+++ b/modules/python/src2/hdr_parser.py
@@ -31,11 +31,160 @@
 original_return_type is None if the original_return_type is the same as return_value_type
 """
 
+def evaluate_conditional_inclusion_directive(directive, preprocessor_definitions):
+    """Evaluates C++ conditional inclusion directive.
+    Reference: https://en.cppreference.com/w/cpp/preprocessor/conditional
+
+    Args:
+        directive(str): input C++ conditional directive.
+        preprocessor_definitions(dict[str, int]): defined preprocessor identifiers.
+
+    Returns:
+        bool: True, if directive is evaluated to 1, False otherwise.
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef    A", {"A": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"B": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef    A", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {"A": 1})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 0", {})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 1", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if    VAR", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if  VAR  ", {"VAR": 1})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR)", {"VAR": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if !defined(VAR)", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR_1)", {"VAR_2": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined(VAR) && VAR", {"VAR": 0}
+    ... )
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if VAR_1 || VAR_2", {"VAR_1": 1, "VAR_2": 0}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined VAR && defined   (VAR)", {"VAR": 1}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if strangedefinedvar", {}
+    ... )
+    Traceback (most recent call last):
+        ...
+    ValueError: Failed to evaluate '#if strangedefinedvar' directive, stripped down to 'strangedefinedvar'
+    """
+    OPERATORS = { "!": "not ", "&&": "and", "&": "and", "||": "or", "|": "or" }
+
+    input_directive = directive
+
+    # Ignore all directives if they contain __cplusplus check
+    if "__cplusplus" in directive:
+        return True
+
+    directive = directive.strip()
+    if directive.startswith("#ifdef "):
+        var = directive[len("#ifdef "):].strip()
+        return var in preprocessor_definitions
+    if directive.startswith("#ifndef "):
+        var = directive[len("#ifndef "):].strip()
+        return var not in preprocessor_definitions
+
+    if directive.startswith("#if "):
+        directive = directive[len("#if "):].strip()
+    elif directive.startswith("#elif "):
+        directive = directive[len("#elif "):].strip()
+    else:
+        raise ValueError("{} is not known conditional directive".format(directive))
+
+    if directive.isdigit():
+        return int(directive) != 0
+
+    if directive in preprocessor_definitions:
+        return bool(preprocessor_definitions[directive])
+
+    # Converting all `defined` directives to their boolean representations
+    # they have 2 forms: `defined identifier` and `defined(identifier)`
+    directive = re.sub(
+        r"\bdefined\s*(\w+|\(\w+\))",
+        lambda m: "True" if m.group(1).strip("() ") in preprocessor_definitions else "False",
+        directive
+    )
+
+    for src_op, dst_op in OPERATORS.items():
+        directive = directive.replace(src_op, dst_op)
+
+    try:
+        if sys.version_info >= (3, 13):
+            eval_directive = eval(directive,
+                                  globals={"__builtins__": {}},
+                                  locals=preprocessor_definitions)
+        else:
+            eval_directive = eval(directive,
+                                  {"__builtins__": {}},
+                                  preprocessor_definitions)
+    except Exception as e:
+        raise ValueError(
+            "Failed to evaluate '{}' directive, stripped down to '{}'".format(
+                input_directive, directive
+            )
+        ) from e
+
+    if not isinstance(eval_directive, (bool, int)):
+        raise TypeError(
+            "'{}' directive is evaluated to unexpected type: {}".format(
+                input_directive, type(eval_directive).__name__
+            )
+        )
+    if isinstance(eval_directive, bool):
+        return eval_directive
+
+    return eval_directive != 0
+
+
 class CppHeaderParser(object):
 
-    def __init__(self, generate_umat_decls=False, generate_gpumat_decls=False):
+    def __init__(self, generate_umat_decls = False, generate_gpumat_decls = False,
+                 preprocessor_definitions = None):
         self._generate_umat_decls = generate_umat_decls
         self._generate_gpumat_decls = generate_gpumat_decls
+        if preprocessor_definitions is None:
+            preprocessor_definitions = {}
+        elif not isinstance(preprocessor_definitions, dict):
+            raise TypeError(
+                "preprocessor_definitions should rather dictionary or None. "
+                "Got: {}".format(type(preprocessor_definitions).__name__)
+            )
+        self.preprocessor_definitions = preprocessor_definitions
+        if "__OPENCV_BUILD" not in self.preprocessor_definitions:
+            self.preprocessor_definitions["__OPENCV_BUILD"] = 0
+        if "OPENCV_BINDING_PARSER" not in self.preprocessor_definitions:
+            self.preprocessor_definitions["OPENCV_BINDING_PARSER"] = 1
+        if "OPENCV_BINDINGS_PARSER" not in self.preprocessor_definitions:
+            self.preprocessor_definitions["OPENCV_BINDINGS_PARSER"] = 1
 
         self.BLOCK_TYPE = 0
         self.BLOCK_NAME = 1
@@ -839,9 +988,8 @@ def parse(self, hname, wmode=True):
         """
         self.hname = hname
         decls = []
-        f = io.open(hname, 'rt', encoding='utf-8')
-        linelist = list(f.readlines())
-        f.close()
+        with io.open(hname, 'rt', encoding='utf-8') as f:
+            linelist = list(f.readlines())
 
         # states:
         SCAN = 0 # outside of a comment or preprocessor directive
@@ -859,7 +1007,6 @@ def parse(self, hname, wmode=True):
         self.wrap_mode = wmode
 
         depth_if_0 = 0
-
         for l0 in linelist:
             self.lineno += 1
             #print(state, self.lineno, l0)
@@ -886,22 +1033,38 @@ def parse(self, hname, wmode=True):
                     continue
                 state = SCAN
                 l = re.sub(r'//(.+)?', '', l).strip()  # drop // comment
-                if l in [
-                    '#if 0',
-                    '#if defined(__OPENCV_BUILD)', '#ifdef __OPENCV_BUILD',
-                    '#if !defined(OPENCV_BINDING_PARSER)', '#ifndef OPENCV_BINDING_PARSER',
-                ]:
+                if l.startswith("#if") or l.startswith("#elif"):
+                    if not evaluate_conditional_inclusion_directive(
+                        l, self.preprocessor_definitions
+                    ):
+                        # Condition evaluated to false
+                        state = DIRECTIVE_IF_0
+                        depth_if_0 = 1
+                elif l.startswith("#else"):
+                    # else in state == DIRECTIVE may occur only if previous
+                    # conditional inclusion directive was evaluated to True
                     state = DIRECTIVE_IF_0
                     depth_if_0 = 1
                 continue
 
             if state == DIRECTIVE_IF_0:
-                if l.startswith('#'):
-                    l = l[1:].strip()
-                    if l.startswith("if"):
+                if l.startswith("#"):
+                    if l.startswith("#if"):
                         depth_if_0 += 1
                         continue
-                    if l.startswith("endif"):
+                    elif l.startswith("#else") and depth_if_0 == 1:
+                        depth_if_0 = 0
+                        state = SCAN
+                    elif l.startswith("#elif") and depth_if_0 == 1:
+                        if evaluate_conditional_inclusion_directive(
+                            l, self.preprocessor_definitions
+                        ):
+                            depth_if_0 = 0
+                            state = SCAN
+                        else:
+                            depth_if_0 += 1
+                            continue
+                    elif l.startswith("#endif"):
                         depth_if_0 -= 1
                         if depth_if_0 == 0:
                             state = SCAN
@@ -1075,6 +1238,9 @@ def print_decls(self, decls):
                     print()
 
 if __name__ == '__main__':
+    import doctest
+    doctest.testmod()
+
     parser = CppHeaderParser(generate_umat_decls=True, generate_gpumat_decls=True)
     decls = []
     for hname in opencv_hdr_list:


Reviews:
user: login: asmorkalov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T12:31:41Z
user: login: VadimLevin
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T12:34:46Z
user: login: VadimLevin
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T12:37:28Z
user: login: asmorkalov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T13:03:18Z
user: login: asmorkalov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T13:03:53Z
user: login: asmorkalov
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T07:51:32Z
user: login: dkurt
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-21T06:51:28Z
user: login: asmorkalov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T06:59:50Z

Review comments:
diff_hunk: @@ -31,11 +31,157 @@
 original_return_type is None if the original_return_type is the same as return_value_type
 """
 
+def evaluate_conditional_inclusion_directive(directive, preprocessor_definitions):
+    """Evaluates C++ conditional inclusion directive.
+    Reference: https://en.cppreference.com/w/cpp/preprocessor/conditional
+
+    Args:
+        directive(str): input C++ conditional directive.
+        preprocessor_definitions(dict[str, int]): defined preprocessor identifiers.
+
+    Returns:
+        bool: True, if directive is evaluated to 1, False otherwise.
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"A": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"B": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {"A": 1})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 0", {})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 1", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if VAR", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR)", {"VAR": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if !defined(VAR)", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR_1)", {"VAR_2": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined(VAR) && VAR", {"VAR": 0}
+    ... )
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if VAR_1 || VAR_2", {"VAR_1": 1, "VAR_2": 0}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined VAR && defined   (VAR)", {"VAR": 1}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if strangedefinedvar", {}
+    ... )
+    Traceback (most recent call last):
+        ...
+    ValueError: Failed to evaluate '#if strangedefinedvar' directive, stripped down to 'strangedefinedvar'
+    """
+    OPERATORS = { "!": "not ", "&&": "and", "&": "and", "||": "or", "|": "or" }
+
+    input_directive = directive
+
+    # Ignore all directives if they contain __cplusplus check
+    if "__cplusplus" in directive:
+        return True
+
+    directive = directive.strip()
+    if directive.startswith("#ifdef "):
+        var = directive[len("#ifdef "):]
path: modules/python/src2/hdr_parser.py
user: login: asmorkalov
body: var.strip()?
created_at: 2025-05-16T12:20:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,11 +31,157 @@
 original_return_type is None if the original_return_type is the same as return_value_type
 """
 
+def evaluate_conditional_inclusion_directive(directive, preprocessor_definitions):
+    """Evaluates C++ conditional inclusion directive.
+    Reference: https://en.cppreference.com/w/cpp/preprocessor/conditional
+
+    Args:
+        directive(str): input C++ conditional directive.
+        preprocessor_definitions(dict[str, int]): defined preprocessor identifiers.
+
+    Returns:
+        bool: True, if directive is evaluated to 1, False otherwise.
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"A": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"B": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {"A": 1})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 0", {})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 1", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if VAR", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR)", {"VAR": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if !defined(VAR)", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR_1)", {"VAR_2": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined(VAR) && VAR", {"VAR": 0}
+    ... )
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if VAR_1 || VAR_2", {"VAR_1": 1, "VAR_2": 0}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined VAR && defined   (VAR)", {"VAR": 1}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if strangedefinedvar", {}
+    ... )
+    Traceback (most recent call last):
+        ...
+    ValueError: Failed to evaluate '#if strangedefinedvar' directive, stripped down to 'strangedefinedvar'
+    """
+    OPERATORS = { "!": "not ", "&&": "and", "&": "and", "||": "or", "|": "or" }
+
+    input_directive = directive
+
+    # Ignore all directives if they contain __cplusplus check
+    if "__cplusplus" in directive:
+        return True
+
+    directive = directive.strip()
+    if directive.startswith("#ifdef "):
+        var = directive[len("#ifdef "):]
+        return var in preprocessor_definitions
+    if directive.startswith("#ifndef "):
+        var = directive[len("#ifndef "):]
path: modules/python/src2/hdr_parser.py
user: login: asmorkalov
body: var.strip()?
created_at: 2025-05-16T12:20:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1075,12 +1235,14 @@ def print_decls(self, decls):
                     print()
 
 if __name__ == '__main__':
-    parser = CppHeaderParser(generate_umat_decls=True, generate_gpumat_decls=True)
-    decls = []
-    for hname in opencv_hdr_list:
-        decls += parser.parse(hname)
-    #for hname in sys.argv[1:]:
-        #decls += parser.parse(hname, wmode=False)
-    parser.print_decls(decls)
-    print(len(decls))
-    print("namespaces:", " ".join(sorted(parser.namespaces)))
+    import doctest
+    doctest.testmod()
+    # parser = CppHeaderParser(generate_umat_decls=True, generate_gpumat_decls=True)
+    # decls = []
+    # for hname in opencv_hdr_list:
+    #     decls += parser.parse(hname)
+    # #for hname in sys.argv[1:]:
+    #     #decls += parser.parse(hname, wmode=False)
+    # parser.print_decls(decls)
+    # print(len(decls))
+    # print("namespaces:", " ".join(sorted(parser.namespaces)))
path: modules/python/src2/hdr_parser.py
user: login: asmorkalov
body: Restore back.
created_at: 2025-05-16T12:25:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,63 @@
+function(ocv_bindings_generator_populate_preprocessor_definitions
+         opencv_modules
+         output_variable)
+  set(defs "\"CV_VERSION_MAJOR\": ${OPENCV_VERSION_MAJOR}")
+
+  macro(ocv_add_definition name value)
+    set(defs "${defs},\n\"${name}\": ${value}")
+  endmacro()
+
+  ocv_add_definition(CV_VERSION_MINOR ${OPENCV_VERSION_MINOR})
+  ocv_add_definition(CV_VERSION_PATCH ${OPENCV_VERSION_PATCH})
+  ocv_add_definition(OPENCV_ABI_COMPATIBILITY "${OPENCV_VERSION_MAJOR}00")
+
+  foreach(module IN LISTS ${opencv_modules})
+    if(HAVE_${module})
+        string(TOUPPER "${module}" module)
+        ocv_add_definition("HAVE_${module}" 1)
+    endif()
+  endforeach()
+  if(HAVE_EIGEN)
+    ocv_add_definition(HAVE_EIGEN 1)
+    ocv_add_definition(EIGEN_WORLD_VERSION ${EIGEN_WORLD_VERSION})
+    ocv_add_definition(EIGEN_MAJOR_VERSION ${EIGEN_MAJOR_VERSION})
+    ocv_add_definition(EIGEN_MINOR_VERSION ${EIGEN_MINOR_VERSION})
+  else()
+    # Some checks in parsed headers might not be protected with HAVE_EIGEN check
+    ocv_add_definition(EIGEN_WORLD_VERSION 0)
+    ocv_add_definition(EIGEN_MAJOR_VERSION 0)
+    ocv_add_definition(EIGEN_MINOR_VERSION 0)
+  endif()
+  if(HAVE_LAPACK)
+    ocv_add_definition(HAVE_LAPACK 1)
+  endif()
+
+  if(OPENCV_DISABLE_FILESYSTEM_SUPPORT)
+    ocv_add_definition(OPENCV_HAVE_FILESYSTEM_SUPPORT 0)
+  else()
+    ocv_add_definition(OPENCV_HAVE_FILESYSTEM_SUPPORT 1)
+  endif()
+
+  ocv_add_definition(OPENCV_BINDINGS_PARSER 1)
+
+  # Implementation details definitions, having no impact on how bindings are
+  # generated, so their real values can be safely ignored
+  ocv_add_definition(CV_ENABLE_UNROLLED 0)
+  ocv_add_definition(CV__EXCEPTION_PTR 0)
+  ocv_add_definition(CV_NEON 0)
+  ocv_add_definition(TBB_INTERFACE_VERSION 0)
+  ocv_add_definition(CV_SSE2 0)
+  ocv_add_definition(CV_VSX 0)
path: cmake/OpenCVBindingsPreprocessorDefinitions.cmake
user: login: asmorkalov
body: I propose to drop it.
created_at: 2025-05-16T12:31:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,11 +31,157 @@
 original_return_type is None if the original_return_type is the same as return_value_type
 """
 
+def evaluate_conditional_inclusion_directive(directive, preprocessor_definitions):
+    """Evaluates C++ conditional inclusion directive.
+    Reference: https://en.cppreference.com/w/cpp/preprocessor/conditional
+
+    Args:
+        directive(str): input C++ conditional directive.
+        preprocessor_definitions(dict[str, int]): defined preprocessor identifiers.
+
+    Returns:
+        bool: True, if directive is evaluated to 1, False otherwise.
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"A": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"B": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {"A": 1})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 0", {})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 1", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if VAR", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR)", {"VAR": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if !defined(VAR)", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR_1)", {"VAR_2": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined(VAR) && VAR", {"VAR": 0}
+    ... )
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if VAR_1 || VAR_2", {"VAR_1": 1, "VAR_2": 0}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined VAR && defined   (VAR)", {"VAR": 1}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if strangedefinedvar", {}
+    ... )
+    Traceback (most recent call last):
+        ...
+    ValueError: Failed to evaluate '#if strangedefinedvar' directive, stripped down to 'strangedefinedvar'
+    """
+    OPERATORS = { "!": "not ", "&&": "and", "&": "and", "||": "or", "|": "or" }
+
+    input_directive = directive
+
+    # Ignore all directives if they contain __cplusplus check
+    if "__cplusplus" in directive:
+        return True
+
+    directive = directive.strip()
+    if directive.startswith("#ifdef "):
+        var = directive[len("#ifdef "):]
path: modules/python/src2/hdr_parser.py
user: login: VadimLevin
body: ```python
>>> 'www.example.com'.strip('cmowz.')
'example'
```

What do you want to strip?
created_at: 2025-05-16T12:34:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,63 @@
+function(ocv_bindings_generator_populate_preprocessor_definitions
+         opencv_modules
+         output_variable)
+  set(defs "\"CV_VERSION_MAJOR\": ${OPENCV_VERSION_MAJOR}")
+
+  macro(ocv_add_definition name value)
+    set(defs "${defs},\n\"${name}\": ${value}")
+  endmacro()
+
+  ocv_add_definition(CV_VERSION_MINOR ${OPENCV_VERSION_MINOR})
+  ocv_add_definition(CV_VERSION_PATCH ${OPENCV_VERSION_PATCH})
+  ocv_add_definition(OPENCV_ABI_COMPATIBILITY "${OPENCV_VERSION_MAJOR}00")
+
+  foreach(module IN LISTS ${opencv_modules})
+    if(HAVE_${module})
+        string(TOUPPER "${module}" module)
+        ocv_add_definition("HAVE_${module}" 1)
+    endif()
+  endforeach()
+  if(HAVE_EIGEN)
+    ocv_add_definition(HAVE_EIGEN 1)
+    ocv_add_definition(EIGEN_WORLD_VERSION ${EIGEN_WORLD_VERSION})
+    ocv_add_definition(EIGEN_MAJOR_VERSION ${EIGEN_MAJOR_VERSION})
+    ocv_add_definition(EIGEN_MINOR_VERSION ${EIGEN_MINOR_VERSION})
+  else()
+    # Some checks in parsed headers might not be protected with HAVE_EIGEN check
+    ocv_add_definition(EIGEN_WORLD_VERSION 0)
+    ocv_add_definition(EIGEN_MAJOR_VERSION 0)
+    ocv_add_definition(EIGEN_MINOR_VERSION 0)
+  endif()
+  if(HAVE_LAPACK)
+    ocv_add_definition(HAVE_LAPACK 1)
+  endif()
+
+  if(OPENCV_DISABLE_FILESYSTEM_SUPPORT)
+    ocv_add_definition(OPENCV_HAVE_FILESYSTEM_SUPPORT 0)
+  else()
+    ocv_add_definition(OPENCV_HAVE_FILESYSTEM_SUPPORT 1)
+  endif()
+
+  ocv_add_definition(OPENCV_BINDINGS_PARSER 1)
+
+  # Implementation details definitions, having no impact on how bindings are
+  # generated, so their real values can be safely ignored
+  ocv_add_definition(CV_ENABLE_UNROLLED 0)
+  ocv_add_definition(CV__EXCEPTION_PTR 0)
+  ocv_add_definition(CV_NEON 0)
+  ocv_add_definition(TBB_INTERFACE_VERSION 0)
+  ocv_add_definition(CV_SSE2 0)
+  ocv_add_definition(CV_VSX 0)
path: cmake/OpenCVBindingsPreprocessorDefinitions.cmake
user: login: VadimLevin
body: It is not possible to drop it, because header parser parsers "implementation" headers and bumps into lines like:

```cpp
#if CV_VSX
// something
#else
// something
#endif
```

created_at: 2025-05-16T12:37:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,11 +31,157 @@
 original_return_type is None if the original_return_type is the same as return_value_type
 """
 
+def evaluate_conditional_inclusion_directive(directive, preprocessor_definitions):
+    """Evaluates C++ conditional inclusion directive.
+    Reference: https://en.cppreference.com/w/cpp/preprocessor/conditional
+
+    Args:
+        directive(str): input C++ conditional directive.
+        preprocessor_definitions(dict[str, int]): defined preprocessor identifiers.
+
+    Returns:
+        bool: True, if directive is evaluated to 1, False otherwise.
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"A": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"B": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {"A": 1})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 0", {})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 1", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if VAR", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR)", {"VAR": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if !defined(VAR)", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR_1)", {"VAR_2": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined(VAR) && VAR", {"VAR": 0}
+    ... )
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if VAR_1 || VAR_2", {"VAR_1": 1, "VAR_2": 0}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined VAR && defined   (VAR)", {"VAR": 1}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if strangedefinedvar", {}
+    ... )
+    Traceback (most recent call last):
+        ...
+    ValueError: Failed to evaluate '#if strangedefinedvar' directive, stripped down to 'strangedefinedvar'
+    """
+    OPERATORS = { "!": "not ", "&&": "and", "&": "and", "||": "or", "|": "or" }
+
+    input_directive = directive
+
+    # Ignore all directives if they contain __cplusplus check
+    if "__cplusplus" in directive:
+        return True
+
+    directive = directive.strip()
+    if directive.startswith("#ifdef "):
+        var = directive[len("#ifdef "):]
path: modules/python/src2/hdr_parser.py
user: login: asmorkalov
body: what if `#ifdef       HAVE_XXX`
created_at: 2025-05-16T13:03:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,63 @@
+function(ocv_bindings_generator_populate_preprocessor_definitions
+         opencv_modules
+         output_variable)
+  set(defs "\"CV_VERSION_MAJOR\": ${OPENCV_VERSION_MAJOR}")
+
+  macro(ocv_add_definition name value)
+    set(defs "${defs},\n\"${name}\": ${value}")
+  endmacro()
+
+  ocv_add_definition(CV_VERSION_MINOR ${OPENCV_VERSION_MINOR})
+  ocv_add_definition(CV_VERSION_PATCH ${OPENCV_VERSION_PATCH})
+  ocv_add_definition(OPENCV_ABI_COMPATIBILITY "${OPENCV_VERSION_MAJOR}00")
+
+  foreach(module IN LISTS ${opencv_modules})
+    if(HAVE_${module})
+        string(TOUPPER "${module}" module)
+        ocv_add_definition("HAVE_${module}" 1)
+    endif()
+  endforeach()
+  if(HAVE_EIGEN)
+    ocv_add_definition(HAVE_EIGEN 1)
+    ocv_add_definition(EIGEN_WORLD_VERSION ${EIGEN_WORLD_VERSION})
+    ocv_add_definition(EIGEN_MAJOR_VERSION ${EIGEN_MAJOR_VERSION})
+    ocv_add_definition(EIGEN_MINOR_VERSION ${EIGEN_MINOR_VERSION})
+  else()
+    # Some checks in parsed headers might not be protected with HAVE_EIGEN check
+    ocv_add_definition(EIGEN_WORLD_VERSION 0)
+    ocv_add_definition(EIGEN_MAJOR_VERSION 0)
+    ocv_add_definition(EIGEN_MINOR_VERSION 0)
+  endif()
+  if(HAVE_LAPACK)
+    ocv_add_definition(HAVE_LAPACK 1)
+  endif()
+
+  if(OPENCV_DISABLE_FILESYSTEM_SUPPORT)
+    ocv_add_definition(OPENCV_HAVE_FILESYSTEM_SUPPORT 0)
+  else()
+    ocv_add_definition(OPENCV_HAVE_FILESYSTEM_SUPPORT 1)
+  endif()
+
+  ocv_add_definition(OPENCV_BINDINGS_PARSER 1)
+
+  # Implementation details definitions, having no impact on how bindings are
+  # generated, so their real values can be safely ignored
+  ocv_add_definition(CV_ENABLE_UNROLLED 0)
+  ocv_add_definition(CV__EXCEPTION_PTR 0)
+  ocv_add_definition(CV_NEON 0)
+  ocv_add_definition(TBB_INTERFACE_VERSION 0)
+  ocv_add_definition(CV_SSE2 0)
+  ocv_add_definition(CV_VSX 0)
path: cmake/OpenCVBindingsPreprocessorDefinitions.cmake
user: login: asmorkalov
body: hm.. need to check.
created_at: 2025-05-16T13:03:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -31,11 +31,160 @@
 original_return_type is None if the original_return_type is the same as return_value_type
 """
 
+def evaluate_conditional_inclusion_directive(directive, preprocessor_definitions):
+    """Evaluates C++ conditional inclusion directive.
+    Reference: https://en.cppreference.com/w/cpp/preprocessor/conditional
+
+    Args:
+        directive(str): input C++ conditional directive.
+        preprocessor_definitions(dict[str, int]): defined preprocessor identifiers.
+
+    Returns:
+        bool: True, if directive is evaluated to 1, False otherwise.
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef    A", {"A": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"B": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef    A", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {"A": 1})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 0", {})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 1", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if    VAR", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if  VAR  ", {"VAR": 1})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR)", {"VAR": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if !defined(VAR)", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR_1)", {"VAR_2": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined(VAR) && VAR", {"VAR": 0}
+    ... )
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if VAR_1 || VAR_2", {"VAR_1": 1, "VAR_2": 0}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined VAR && defined   (VAR)", {"VAR": 1}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if strangedefinedvar", {}
+    ... )
+    Traceback (most recent call last):
+        ...
+    ValueError: Failed to evaluate '#if strangedefinedvar' directive, stripped down to 'strangedefinedvar'
+    """
+    OPERATORS = { "!": "not ", "&&": "and", "&": "and", "||": "or", "|": "or" }
+
+    input_directive = directive
+
+    # Ignore all directives if they contain __cplusplus check
+    if "__cplusplus" in directive:
+        return True
+
+    directive = directive.strip()
+    if directive.startswith("#ifdef "):
+        var = directive[len("#ifdef "):].strip()
+        return var in preprocessor_definitions
+    if directive.startswith("#ifndef "):
+        var = directive[len("#ifndef "):].strip()
+        return var not in preprocessor_definitions
+
+    if directive.startswith("#if "):
+        directive = directive[len("#if "):].strip()
+    elif directive.startswith("#elif "):
+        directive = directive[len("#elif "):].strip()
+    else:
+        raise ValueError("{} is not known conditional directive".format(directive))
+
+    if directive.isdigit():
+        return int(directive) != 0
+
+    if directive in preprocessor_definitions:
+        return bool(preprocessor_definitions[directive])
+
+    # Converting all `defined` directives to their boolean representations
+    # they have 2 forms: `defined identifier` and `defined(identifier)`
+    directive = re.sub(
+        r"\bdefined\s*(\w+|\(\w+\))",
+        lambda m: "True" if m.group(1).strip("() ") in preprocessor_definitions else "False",
+        directive
+    )
+
+    for src_op, dst_op in OPERATORS.items():
+        directive = directive.replace(src_op, dst_op)
+
+    try:
+        if sys.version_info >= (3, 13):
+            eval_directive = eval(directive,
+                                  globals={"__builtins__": {}},
+                                  locals=preprocessor_definitions)
+        else:
+            eval_directive = eval(directive,
+                                  {"__builtins__": {}},
+                                  preprocessor_definitions)
+    except Exception as e:
+        raise ValueError(
+            "Failed to evaluate '{}' directive, stripped down to '{}'".format(
+                input_directive, directive
+            )
+        ) from e
path: modules/python/src2/hdr_parser.py
user: login: dkurt
body: Please check failed build https://pullrequest.opencv.org/buildbot/builders/precommit_windows64/builds/109668/steps/compile%20release/logs/stdio:
```
 Traceback (most recent call last):
    File "C:/build/precommit_windows64/4.x/opencv/modules/java/generator/../generator/gen_java.py", line 1422, in <module>
      import hdr_parser
    File "C:\build\precommit_windows64\4.x\opencv\modules\python\src2\hdr_parser.py", line 154
      ) from e
           ^
  SyntaxError: invalid syntax
  opencv_gapi_SSE4_1.vcxproj -> C:\build\precommit_windows64\build\modules\gapi\opencv_gapi_SSE4_1.dir\Release\opencv_gapi_SSE4_1.lib
  Generate files for Python bindings and documentation
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\Microsoft.CppCommon.targets(171,5): error MSB6006: "cmd.exe" exited with code 1. [C:\build\precommit_windows64\build\modules\java_bindings_generator\gen_opencv_java_source.vcxproj]
  Traceback (most recent call last):
    File "C:/build/precommit_windows64/4.x/opencv/modules/python/src2/gen2.py", line 4, in <module>
      import hdr_parser, sys, re
    File "C:\build\precommit_windows64\4.x\opencv\modules\python\src2\hdr_parser.py", line 154
      ) from e
           ^
  SyntaxError: invalid syntax
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\Microsoft.CppCommon.targets(171,5): error MSB6006: "cmd.exe" exited with code 1. [C:
```
created_at: 2025-05-21T06:51:27Z
author_association: MEMBER
diff_hunk: @@ -31,11 +31,160 @@
 original_return_type is None if the original_return_type is the same as return_value_type
 """
 
+def evaluate_conditional_inclusion_directive(directive, preprocessor_definitions):
+    """Evaluates C++ conditional inclusion directive.
+    Reference: https://en.cppreference.com/w/cpp/preprocessor/conditional
+
+    Args:
+        directive(str): input C++ conditional directive.
+        preprocessor_definitions(dict[str, int]): defined preprocessor identifiers.
+
+    Returns:
+        bool: True, if directive is evaluated to 1, False otherwise.
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef    A", {"A": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifdef A", {"B": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef    A", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#ifndef A", {"A": 1})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 0", {})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if 1", {})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if    VAR", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if  VAR  ", {"VAR": 1})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR)", {"VAR": 0})
+    True
+
+    >>> evaluate_conditional_inclusion_directive("#if !defined(VAR)", {"VAR": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive("#if defined(VAR_1)", {"VAR_2": 0})
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined(VAR) && VAR", {"VAR": 0}
+    ... )
+    False
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if VAR_1 || VAR_2", {"VAR_1": 1, "VAR_2": 0}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if defined VAR && defined   (VAR)", {"VAR": 1}
+    ... )
+    True
+
+    >>> evaluate_conditional_inclusion_directive(
+    ...     "#if strangedefinedvar", {}
+    ... )
+    Traceback (most recent call last):
+        ...
+    ValueError: Failed to evaluate '#if strangedefinedvar' directive, stripped down to 'strangedefinedvar'
+    """
+    OPERATORS = { "!": "not ", "&&": "and", "&": "and", "||": "or", "|": "or" }
+
+    input_directive = directive
+
+    # Ignore all directives if they contain __cplusplus check
+    if "__cplusplus" in directive:
+        return True
+
+    directive = directive.strip()
+    if directive.startswith("#ifdef "):
+        var = directive[len("#ifdef "):].strip()
+        return var in preprocessor_definitions
+    if directive.startswith("#ifndef "):
+        var = directive[len("#ifndef "):].strip()
+        return var not in preprocessor_definitions
+
+    if directive.startswith("#if "):
+        directive = directive[len("#if "):].strip()
+    elif directive.startswith("#elif "):
+        directive = directive[len("#elif "):].strip()
+    else:
+        raise ValueError("{} is not known conditional directive".format(directive))
+
+    if directive.isdigit():
+        return int(directive) != 0
+
+    if directive in preprocessor_definitions:
+        return bool(preprocessor_definitions[directive])
+
+    # Converting all `defined` directives to their boolean representations
+    # they have 2 forms: `defined identifier` and `defined(identifier)`
+    directive = re.sub(
+        r"\bdefined\s*(\w+|\(\w+\))",
+        lambda m: "True" if m.group(1).strip("() ") in preprocessor_definitions else "False",
+        directive
+    )
+
+    for src_op, dst_op in OPERATORS.items():
+        directive = directive.replace(src_op, dst_op)
+
+    try:
+        if sys.version_info >= (3, 13):
+            eval_directive = eval(directive,
+                                  globals={"__builtins__": {}},
+                                  locals=preprocessor_definitions)
+        else:
+            eval_directive = eval(directive,
+                                  {"__builtins__": {}},
+                                  preprocessor_definitions)
+    except Exception as e:
+        raise ValueError(
+            "Failed to evaluate '{}' directive, stripped down to '{}'".format(
+                input_directive, directive
+            )
+        ) from e
path: modules/python/src2/hdr_parser.py
user: login: asmorkalov
body: It requires Python update. I notified Alexander about it.
created_at: 2025-05-21T06:59:50Z
author_association: CONTRIBUTOR

PR comments:
user: login: dkurt
created_at: 2025-05-16T11:16:18Z
body: Some related issues, can you please also check?
https://github.com/opencv/opencv/issues/10175
https://github.com/opencv/opencv/issues/12972
author_association: MEMBER
user: login: VadimLevin
created_at: 2025-05-16T11:43:33Z
body: > Some related issues, can you please also check? #10175 #12972

Thank you. This patch resolves both mentioned issues. For #10175 if `HAVE_OPENCV_FLANN` resolves to 0

```
compile:
    [mkdir] Created dir: /Users/opencv-cn/GHA-OCV-3/_work/opencv/opencv/build/java_test/build/classes
    [javac] Compiling 63 source files to /Users/opencv-cn/GHA-OCV-3/_work/opencv/opencv/build/java_test/build/classes
    [javac] /Users/opencv-cn/GHA-OCV-3/_work/opencv/opencv/build/java_test/src/org/opencv/test/features2d/FlannBasedDescriptorMatcherTest.java:15: error: cannot find symbol
    [javac] import org.opencv.features2d.FlannBasedMatcher;
    [javac]                             ^
    [javac]   symbol:   class FlannBasedMatcher
    [javac]   location: package org.opencv.features2d
```
author_association: CONTRIBUTOR
user: login: asmorkalov
created_at: 2025-05-19T07:52:03Z
body: @mshabunin @opencv-alalek could you take a look too?
author_association: CONTRIBUTOR
user: login: vrabaud
created_at: 2025-05-24T08:23:05Z
body: Thanks for the patch, that really helps. I have a different build system though (blaze) so I generate the binding files in CMake and then include them in blaze which works fine. Some users might need DNN in the bindings, others might not (typically, Java and Android Java) and one header for both is easier.

Would the following be possible: keep your patch but keep the guards in the bindings instead of removing the guarded sections. This way, the binding headers would be usable for different configurations (e.g. with or without HAVE_OPENCV_DNN being defined).
author_association: CONTRIBUTOR
user: login: VadimLevin
created_at: 2025-07-24T08:10:18Z
body: > Would the following be possible: keep your patch but keep the guards in the bindings instead of removing the guarded sections. This way, the binding headers would be usable for different configurations (e.g. with or without HAVE_OPENCV_DNN being defined).

Thank you for suggestion. Your approach requires significant changes in header parser and all bindings generators, because "availability information" should be a part of "function/enum/class/struct" definition. We can consider doing it once we begin reworking the header parser, updating intermediate representation and unifying all bindings generation.
author_association: CONTRIBUTOR