This is an automated email from the ASF dual-hosted git repository.

martinzink pushed a commit to branch controller_c_api_requirements
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit 233e9b6baf26a721ddf8cdc9bd8db577f9778f93
Author: Adam Debreceni <[email protected]>
AuthorDate: Thu Feb 12 16:37:05 2026 +0100

    MINIFICPP-2715 - Use symbols to check api compatibility
    
    Closes #2105
    
    Signed-off-by: Martin Zink <[email protected]>
---
 Extensions.md                                      |   4 +-
 cmake/Extensions.cmake                             |  23 +----
 .../SFTPLoader.cpp => ExtensionInitializer.cpp}    |  19 +---
 .../llamacpp/processors/ExtensionInitializer.cpp   |   4 +-
 extensions/opencv/CMakeLists.txt                   |   1 +
 extensions/opencv/OpenCVLoader.cpp                 |   2 +-
 extensions/python/CMakeLists.txt                   |   2 +
 .../python/pythonlibloader/PythonLibLoader.cpp     |   2 +-
 extensions/python/pythonloader/PyProcLoader.cpp    |   2 +-
 extensions/sftp/CMakeLists.txt                     |   1 +
 extensions/sftp/SFTPLoader.cpp                     |   2 +-
 libminifi/include/core/extension/Utils.h           |  26 -----
 libminifi/src/core/extension/Extension.cpp         |   2 +-
 libminifi/src/core/extension/ExtensionManager.cpp  |   8 --
 libminifi/src/core/extension/Utils.cpp             |  65 -------------
 libminifi/src/minifi-c.cpp                         |  55 ++++++-----
 libminifi/test/unit/ExtensionVerificationTests.cpp | 107 ---------------------
 minifi-api/CMakeLists.txt                          |   1 +
 minifi-api/include/minifi-c/minifi-c.h             |  16 ++-
 minifi-api/minifi-c-api.def                        |   4 +-
 20 files changed, 69 insertions(+), 277 deletions(-)

diff --git a/Extensions.md b/Extensions.md
index 7d3fd5392..3a82e212f 100644
--- a/Extensions.md
+++ b/Extensions.md
@@ -33,10 +33,10 @@ REGISTER_RESOURCE(RESTSender, DescriptionOnly);
 ```
 
 Some extensions (e.g. `OpenCVExtension`) require initialization before use.
-You need to define an `InitExtension` function of type 
`MinifiExtension*(MinifiConfig*)` to be called.
+You need to define an `MinifiInitExtension` function of type 
`MinifiExtension*(MinifiConfig*)` to be called.
 
 ```C++
-extern "C" MinifiExtension* InitExtension(MinifiConfig* /*config*/) {
+extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) {
   const auto success = 
org::apache::nifi::minifi::utils::Environment::setEnvironmentVariable("OPENCV_FFMPEG_CAPTURE_OPTIONS",
 "rtsp_transport;udp", false /*overwrite*/);
   if (!success) {
     return nullptr;
diff --git a/cmake/Extensions.cmake b/cmake/Extensions.cmake
index 5786967a8..6ea93f1cb 100644
--- a/cmake/Extensions.cmake
+++ b/cmake/Extensions.cmake
@@ -22,29 +22,14 @@ define_property(GLOBAL PROPERTY EXTENSION-OPTIONS
 
 set_property(GLOBAL PROPERTY EXTENSION-OPTIONS "")
 
-set(extension-build-info-file 
"${CMAKE_CURRENT_BINARY_DIR}/ExtensionBuildInfo.cpp")
-file(GENERATE OUTPUT ${extension-build-info-file}
-    CONTENT "\
-    #include \"minifi-cpp/utils/Export.h\"\n\
-    #ifdef BUILD_ID_VARIABLE_NAME\n\
-    EXTENSIONAPI extern const char* const BUILD_ID_VARIABLE_NAME = 
\"__EXTENSION_BUILD_IDENTIFIER_BEGIN__${BUILD_IDENTIFIER}__EXTENSION_BUILD_IDENTIFIER_END__\";\n\
-    #else\n\
-    static_assert(false, \"BUILD_ID_VARIABLE_NAME is not defined\");\n\
-    #endif\n")
-
-function(get_build_id_variable_name extension-name output)
-    string(REPLACE "-" "_" result ${extension-name})
-    string(APPEND result "_build_identifier")
-    set("${output}" "${result}" PARENT_SCOPE)
-endfunction()
-
 macro(register_extension extension-name extension-display-name extension-guard 
description)
     set(${extension-guard} ${extension-name} PARENT_SCOPE)
     get_property(extensions GLOBAL PROPERTY EXTENSION-OPTIONS)
     set_property(GLOBAL APPEND PROPERTY EXTENSION-OPTIONS ${extension-name})
-    get_build_id_variable_name(${extension-name} build-id-variable-name)
-    set_source_files_properties(${extension-build-info-file} PROPERTIES 
GENERATED TRUE)
-    target_sources(${extension-name} PRIVATE ${extension-build-info-file})
+    get_target_property(has_custom_initializer ${extension-name} 
HAS_CUSTOM_INITIALIZER)
+    if (NOT has_custom_initializer)
+        target_sources(${extension-name} PRIVATE 
${CMAKE_SOURCE_DIR}/extensions/ExtensionInitializer.cpp)
+    endif()
     target_compile_definitions(${extension-name}
         PRIVATE "MODULE_NAME=${extension-name}"
         PRIVATE "BUILD_ID_VARIABLE_NAME=${build-id-variable-name}")
diff --git a/extensions/sftp/SFTPLoader.cpp 
b/extensions/ExtensionInitializer.cpp
similarity index 77%
copy from extensions/sftp/SFTPLoader.cpp
copy to extensions/ExtensionInitializer.cpp
index 8a67842de..3caaaeed0 100644
--- a/extensions/sftp/SFTPLoader.cpp
+++ b/extensions/ExtensionInitializer.cpp
@@ -15,31 +15,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-#include "minifi-cpp/agent/agent_version.h"
-#include "minifi-cpp/properties/Configure.h"
-#include "client/SFTPClient.h"
 #include "minifi-c/minifi-c.h"
 #include "utils/ExtensionInitUtils.h"
+#include "minifi-cpp/agent/agent_version.h"
 #include "core/Resource.h"
 
 namespace minifi = org::apache::nifi::minifi;
 
-extern "C" MinifiExtension* InitExtension(MinifiConfig* /*config*/) {
-  if (libssh2_init(0) != 0) {
-    return nullptr;
-  }
-  if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
-    libssh2_exit();
-    return nullptr;
-  }
+extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) {
   MinifiExtensionCreateInfo ext_create_info{
     .name = minifi::utils::toStringView(MAKESTRING(MODULE_NAME)),
     .version = minifi::utils::toStringView(minifi::AgentBuild::VERSION),
-    .deinit = [] (void* /*user_data*/) {
-      curl_global_cleanup();
-      libssh2_exit();
-    },
+    .deinit = nullptr,
     .user_data = nullptr,
     .processors_count = 0,
     .processors_ptr = nullptr
diff --git a/extensions/llamacpp/processors/ExtensionInitializer.cpp 
b/extensions/llamacpp/processors/ExtensionInitializer.cpp
index 40508955c..b90b8b133 100644
--- a/extensions/llamacpp/processors/ExtensionInitializer.cpp
+++ b/extensions/llamacpp/processors/ExtensionInitializer.cpp
@@ -24,7 +24,7 @@
 
 namespace minifi = org::apache::nifi::minifi;
 
-extern "C" MinifiExtension* InitExtension(MinifiConfig* /*config*/) {
+extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) {
   MinifiExtension* extension = nullptr;
   
minifi::api::core::useProcessorClassDescription<minifi::extensions::llamacpp::processors::RunLlamaCppInference>([&]
 (const MinifiProcessorClassDefinition& description) {
     MinifiExtensionCreateInfo ext_create_info{
@@ -39,5 +39,3 @@ extern "C" MinifiExtension* InitExtension(MinifiConfig* 
/*config*/) {
   });
   return extension;
 }
-
-extern const char* const MINIFI_API_VERSION_TAG_var = MINIFI_API_VERSION_TAG;
diff --git a/extensions/opencv/CMakeLists.txt b/extensions/opencv/CMakeLists.txt
index ed812c1a6..3c80e3efd 100644
--- a/extensions/opencv/CMakeLists.txt
+++ b/extensions/opencv/CMakeLists.txt
@@ -29,6 +29,7 @@ include(${CMAKE_SOURCE_DIR}/extensions/ExtensionHeader.txt)
 file(GLOB SOURCES  "*.cpp")
 
 add_minifi_library(minifi-opencv SHARED ${SOURCES})
+set_target_properties(minifi-opencv PROPERTIES HAS_CUSTOM_INITIALIZER TRUE)
 
 target_link_libraries(minifi-opencv ${LIBMINIFI})
 target_link_libraries(minifi-opencv OPENCV::libopencv)
diff --git a/extensions/opencv/OpenCVLoader.cpp 
b/extensions/opencv/OpenCVLoader.cpp
index 79a437f75..3f91395c4 100644
--- a/extensions/opencv/OpenCVLoader.cpp
+++ b/extensions/opencv/OpenCVLoader.cpp
@@ -24,7 +24,7 @@
 
 namespace minifi = org::apache::nifi::minifi;
 
-extern "C" MinifiExtension* InitExtension(MinifiConfig* /*config*/) {
+extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) {
   // By default in OpenCV, ffmpeg capture is hardcoded to use TCP and this is 
a workaround
   // also if UDP timeout, ffmpeg will retry with TCP
   // Note:
diff --git a/extensions/python/CMakeLists.txt b/extensions/python/CMakeLists.txt
index ac3f5fe57..34b1df673 100644
--- a/extensions/python/CMakeLists.txt
+++ b/extensions/python/CMakeLists.txt
@@ -25,12 +25,14 @@ include(${CMAKE_SOURCE_DIR}/extensions/ExtensionHeader.txt)
 
 if (NOT WIN32)
     add_minifi_library(minifi-python-lib-loader-extension SHARED 
pythonlibloader/PythonLibLoader.cpp)
+    set_target_properties(minifi-python-lib-loader-extension PROPERTIES 
HAS_CUSTOM_INITIALIZER TRUE)
     target_link_libraries(minifi-python-lib-loader-extension PRIVATE 
${LIBMINIFI})
 endif()
 
 file(GLOB SOURCES "*.cpp" "types/*.cpp" "pythonloader/PyProcLoader.cpp")
 
 add_minifi_library(minifi-python-script-extension SHARED ${SOURCES})
+set_target_properties(minifi-python-script-extension PROPERTIES 
HAS_CUSTOM_INITIALIZER TRUE)
 
 target_link_libraries(minifi-python-script-extension PRIVATE ${LIBMINIFI} 
Threads::Threads)
 
diff --git a/extensions/python/pythonlibloader/PythonLibLoader.cpp 
b/extensions/python/pythonlibloader/PythonLibLoader.cpp
index 398322f8f..cdf0356b7 100644
--- a/extensions/python/pythonlibloader/PythonLibLoader.cpp
+++ b/extensions/python/pythonlibloader/PythonLibLoader.cpp
@@ -98,7 +98,7 @@ class PythonLibLoader {
   std::shared_ptr<minifi::core::logging::Logger> logger_ = 
minifi::core::logging::LoggerFactory<PythonLibLoader>::getLogger();
 };
 
-extern "C" MinifiExtension* InitExtension(MinifiConfig* config) {
+extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* config) {
   static PythonLibLoader python_lib_loader([&] (std::string_view key) -> 
std::optional<std::string> {
     std::optional<std::string> result;
     MinifiConfigGet(config, minifi::utils::toStringView(key), [] (void* 
user_data, MinifiStringView value) {
diff --git a/extensions/python/pythonloader/PyProcLoader.cpp 
b/extensions/python/pythonloader/PyProcLoader.cpp
index 3778f15ec..55ad4ebef 100644
--- a/extensions/python/pythonloader/PyProcLoader.cpp
+++ b/extensions/python/pythonloader/PyProcLoader.cpp
@@ -33,7 +33,7 @@ static minifi::extensions::python::PythonCreator& 
getPythonCreator() {
 // the symbols of the python library
 extern "C" const int LOAD_MODULE_AS_GLOBAL = 1;
 
-extern "C" MinifiExtension* InitExtension(MinifiConfig* config) {
+extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* config) {
   getPythonCreator().configure([&] (std::string_view key) -> 
std::optional<std::string> {
     std::optional<std::string> result;
     MinifiConfigGet(config, minifi::utils::toStringView(key), [] (void* 
user_data, MinifiStringView value) {
diff --git a/extensions/sftp/CMakeLists.txt b/extensions/sftp/CMakeLists.txt
index c5c1cb6a7..7200b41ba 100644
--- a/extensions/sftp/CMakeLists.txt
+++ b/extensions/sftp/CMakeLists.txt
@@ -31,6 +31,7 @@ include_directories(client processors)
 file(GLOB SOURCES  "*.cpp" "client/*.cpp" "processors/*.cpp")
 
 add_minifi_library(minifi-sftp SHARED ${SOURCES})
+set_target_properties(minifi-sftp PROPERTIES HAS_CUSTOM_INITIALIZER TRUE)
 
 target_link_libraries(minifi-sftp ${LIBMINIFI} Threads::Threads)
 target_link_libraries(minifi-sftp libssh2 RapidJSON)
diff --git a/extensions/sftp/SFTPLoader.cpp b/extensions/sftp/SFTPLoader.cpp
index 8a67842de..bb23e9998 100644
--- a/extensions/sftp/SFTPLoader.cpp
+++ b/extensions/sftp/SFTPLoader.cpp
@@ -25,7 +25,7 @@
 
 namespace minifi = org::apache::nifi::minifi;
 
-extern "C" MinifiExtension* InitExtension(MinifiConfig* /*config*/) {
+extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) {
   if (libssh2_init(0) != 0) {
     return nullptr;
   }
diff --git a/libminifi/include/core/extension/Utils.h 
b/libminifi/include/core/extension/Utils.h
index 520827193..c983a25f9 100644
--- a/libminifi/include/core/extension/Utils.h
+++ b/libminifi/include/core/extension/Utils.h
@@ -44,37 +44,11 @@ class Timer {
   Callback cb_;
 };
 
-enum LibraryType {
-  Cpp,
-  CApi,
-  Invalid
-};
-
 struct LibraryDescriptor {
   std::string name;
   std::filesystem::path dir;
   std::string filename;
 
-
-  [[nodiscard]] bool verify_as_cpp_extension() const;
-
-  [[nodiscard]] bool verify_as_c_extension(const 
std::shared_ptr<logging::Logger>& logger) const;
-
-  [[nodiscard]]
-  LibraryType verify(const std::shared_ptr<logging::Logger>& logger) const {
-    const auto path = getFullPath();
-    const Timer timer{[&](const std::chrono::milliseconds elapsed) {
-      logger->log_debug("Verification for '{}' took {}", path, elapsed);
-    }};
-    if (verify_as_cpp_extension()) {
-      return Cpp;
-    }
-    if (verify_as_c_extension(logger)) {
-      return CApi;
-    }
-    return Invalid;
-  }
-
   [[nodiscard]]
   std::filesystem::path getFullPath() const {
     return dir / filename;
diff --git a/libminifi/src/core/extension/Extension.cpp 
b/libminifi/src/core/extension/Extension.cpp
index ac3675b57..268cc2ff4 100644
--- a/libminifi/src/core/extension/Extension.cpp
+++ b/libminifi/src/core/extension/Extension.cpp
@@ -94,7 +94,7 @@ Extension::~Extension() {
 
 bool Extension::initialize(const std::shared_ptr<minifi::Configure>& 
configure) {
   logger_->log_trace("Initializing extension '{}'", name_);
-  if (void* init_symbol_ptr = findSymbol("InitExtension")) {
+  if (void* init_symbol_ptr = findSymbol("MinifiInitExtension")) {
     logger_->log_debug("Found custom initializer for '{}'", name_);
     auto init_fn = 
reinterpret_cast<MinifiExtension*(*)(MinifiConfig*)>(init_symbol_ptr);
     auto config_handle = reinterpret_cast<MinifiConfig*>(configure.get());
diff --git a/libminifi/src/core/extension/ExtensionManager.cpp 
b/libminifi/src/core/extension/ExtensionManager.cpp
index 0d60e7c6b..98736afcc 100644
--- a/libminifi/src/core/extension/ExtensionManager.cpp
+++ b/libminifi/src/core/extension/ExtensionManager.cpp
@@ -57,14 +57,6 @@ ExtensionManager::ExtensionManager(const 
std::shared_ptr<Configure>& config): lo
     if (!library) {
       continue;
     }
-    const auto library_type = library->verify(logger_);
-    if (library_type == internal::Invalid) {
-      logger_->log_warn("Skipping library '{}' at '{}': failed verification, 
different build?",
-          library->name, library->getFullPath());
-      continue;
-    }
-
-    logger_->log_trace("Verified library {} at {} as {} extension", 
library->name, library->getFullPath(), magic_enum::enum_name(library_type));
     auto extension = std::make_unique<Extension>(library->name, 
library->getFullPath());
     if (!extension->load()) {
       // error already logged by method
diff --git a/libminifi/src/core/extension/Utils.cpp 
b/libminifi/src/core/extension/Utils.cpp
deleted file mode 100644
index 38e90ac15..000000000
--- a/libminifi/src/core/extension/Utils.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "core/extension/Utils.h"
-#include "minifi-c/minifi-c.h"
-#include "minifi-cpp/agent/agent_version.h"
-#include "utils/RegexUtils.h"
-
-namespace org::apache::nifi::minifi::core::extension::internal {
-
-[[nodiscard]] bool LibraryDescriptor::verify_as_cpp_extension() const {
-  const auto path = getFullPath();
-  if (!std::filesystem::exists(path)) {
-    throw std::runtime_error{"File not found: " + path.string()};
-  }
-  const std::string_view begin_marker = "__EXTENSION_BUILD_IDENTIFIER_BEGIN__";
-  const std::string_view end_marker = "__EXTENSION_BUILD_IDENTIFIER_END__";
-  const std::string magic_constant = utils::string::join_pack(begin_marker, 
AgentBuild::BUILD_IDENTIFIER, end_marker);
-  return utils::file::contains(path, magic_constant);
-}
-
-[[nodiscard]] bool LibraryDescriptor::verify_as_c_extension(const 
std::shared_ptr<logging::Logger>& logger) const {
-  const auto path = getFullPath();
-  if (!std::filesystem::exists(path)) {
-    throw std::runtime_error{"File not found: " + path.string()};
-  }
-  const std::string_view api_tag_prefix = "MINIFI_API_VERSION=[";
-  if (auto version = utils::file::findSubstringWithPrefix(path, 
api_tag_prefix, api_tag_prefix.size() + 20)) {
-    utils::SVMatch match;
-    if (!utils::regexSearch(version.value(), match, 
utils::Regex{R"(MINIFI_API_VERSION=\[([0-9]+)\.([0-9]+)\.([0-9]+)\])"})) {
-      logger->log_error("Found api version in invalid format: '{}'", 
version.value());
-      return false;
-    }
-    gsl_Assert(match.size() == 4);
-    const int major = std::stoi(match[1]);
-    const int minor = std::stoi(match[2]);
-    const int patch = std::stoi(match[3]);
-    if (major != MINIFI_API_MAJOR_VERSION) {
-      logger->log_error("API major version mismatch, application is '{}' while 
extension is '{}.{}.{}'", MINIFI_API_VERSION, major, minor, patch);
-      return false;
-    }
-    if (minor > MINIFI_API_MINOR_VERSION) {
-      logger->log_error("Extension is built for a newer version, application 
is '{}' while extension is '{}.{}.{}'", MINIFI_API_VERSION, major, minor, 
patch);
-      return false;
-    }
-    return true;
-  }
-  return false;
-}
-
-}  // namespace org::apache::nifi::minifi::core::extension::internal
diff --git a/libminifi/src/minifi-c.cpp b/libminifi/src/minifi-c.cpp
index f14847363..6a2d947ab 100644
--- a/libminifi/src/minifi-c.cpp
+++ b/libminifi/src/minifi-c.cpp
@@ -146,6 +146,30 @@ class CProcessorFactory : public 
minifi::core::ProcessorFactory {
   minifi::utils::CProcessorClassDescription class_description_;
 };
 
+MinifiExtension* MinifiCreateExtensionImpl(MinifiStringView /*api_version*/, 
const MinifiExtensionCreateInfo* extension_create_info) {
+  gsl_Assert(extension_create_info);
+  auto extension_name = toString(extension_create_info->name);
+  minifi::BundleIdentifier bundle{
+    .name = extension_name,
+    .version = toString(extension_create_info->version)
+  };
+  auto& bundle_components = 
minifi::ClassDescriptionRegistry::getMutableClassDescriptions()[bundle];
+  for (size_t proc_idx = 0; proc_idx < 
extension_create_info->processors_count; ++proc_idx) {
+    
minifi::utils::useCProcessorClassDescription(extension_create_info->processors_ptr[proc_idx],
 [&] (const auto& description, const auto& c_class_description) {
+      
minifi::core::ClassLoader::getDefaultClassLoader().getClassLoader(extension_name).registerClass(
+        c_class_description.name,
+        std::make_unique<CProcessorFactory>(extension_name, 
toString(extension_create_info->processors_ptr[proc_idx].full_name), 
c_class_description));
+      bundle_components.processors.emplace_back(description);
+    });
+  }
+  return reinterpret_cast<MinifiExtension*>(new 
org::apache::nifi::minifi::core::extension::Extension::Info{
+    .name = toString(extension_create_info->name),
+    .version = toString(extension_create_info->version),
+    .deinit = extension_create_info->deinit,
+    .user_data = extension_create_info->user_data
+  });
+}
+
 }  // namespace
 
 namespace org::apache::nifi::minifi::utils {
@@ -225,30 +249,17 @@ void useCProcessorClassDescription(const 
MinifiProcessorClassDefinition& class_d
 
 extern "C" {
 
-MinifiExtension* MinifiCreateExtension(MinifiStringView /*api_version*/, const 
MinifiExtensionCreateInfo* extension_create_info) {
-  gsl_Assert(extension_create_info);
-  auto extension_name = toString(extension_create_info->name);
-  minifi::BundleIdentifier bundle{
-    .name = extension_name,
-    .version = toString(extension_create_info->version)
-  };
-  auto& bundle_components = 
minifi::ClassDescriptionRegistry::getMutableClassDescriptions()[bundle];
-  for (size_t proc_idx = 0; proc_idx < 
extension_create_info->processors_count; ++proc_idx) {
-    
minifi::utils::useCProcessorClassDescription(extension_create_info->processors_ptr[proc_idx],
 [&] (const auto& description, const auto& c_class_description) {
-      
minifi::core::ClassLoader::getDefaultClassLoader().getClassLoader(extension_name).registerClass(
-        c_class_description.name,
-        std::make_unique<CProcessorFactory>(extension_name, 
toString(extension_create_info->processors_ptr[proc_idx].full_name), 
c_class_description));
-      bundle_components.processors.emplace_back(description);
-    });
-  }
-  return reinterpret_cast<MinifiExtension*>(new 
org::apache::nifi::minifi::core::extension::Extension::Info{
-    .name = toString(extension_create_info->name),
-    .version = toString(extension_create_info->version),
-    .deinit = extension_create_info->deinit,
-    .user_data = extension_create_info->user_data
-  });
+MinifiExtension* MINIFI_CREATE_EXTENSION_FN(MinifiStringView api_version, 
const MinifiExtensionCreateInfo* extension_create_info) {
+  return MinifiCreateExtensionImpl(api_version, extension_create_info);
 }
 
+#define REGISTER_C_API_VERSION(major, minor)  \
+  MinifiExtension* MINIFI_PRIVATE_JOIN(MinifiCreateExtension, 
MINIFI_PRIVATE_JOIN(major, minor))(MinifiStringView api_version, const 
MinifiExtensionCreateInfo* extension_create_info) {  \
+    return MinifiCreateExtensionImpl(api_version, extension_create_info);  \
+  }
+
+REGISTER_C_API_VERSION(0, 1)
+
 MinifiStatus MinifiProcessContextGetProperty(MinifiProcessContext* context, 
MinifiStringView property_name, MinifiFlowFile* flowfile,
     void (*result_cb)(void* user_ctx, MinifiStringView result), void* 
user_ctx) {
   gsl_Assert(context != MINIFI_NULL);
diff --git a/libminifi/test/unit/ExtensionVerificationTests.cpp 
b/libminifi/test/unit/ExtensionVerificationTests.cpp
deleted file mode 100644
index 71a425ada..000000000
--- a/libminifi/test/unit/ExtensionVerificationTests.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#define CUSTOM_EXTENSION_INIT
-
-#include <filesystem>
-#include "unit/TestBase.h"
-#include "unit/Catch.h"
-#include "minifi-cpp/agent/agent_version.h"
-#include "core/extension/Utils.h"
-#include "unit/TestUtils.h"
-#include "minifi-c/minifi-c.h"
-
-using namespace std::literals;
-
-namespace {
-
-#if defined(WIN32)
-const std::string extension_file = "extension.dll";
-#elif defined(__APPLE__)
-const std::string extension_file = "libextension.dylib";
-#else
-const std::string extension_file = "libextension.so";
-#endif
-
-
-struct Fixture : public TestController {
-  Fixture() {
-    extension_ = createTempDirectory() / extension_file;
-  }
-  std::filesystem::path extension_;
-};
-
-const std::shared_ptr<logging::Logger> 
logger{core::logging::LoggerFactory<Fixture>::getLogger()};
-
-}  // namespace
-
-TEST_CASE_METHOD(Fixture, "Could load extension with matching build id") {
-  std::ofstream{extension_} << "__EXTENSION_BUILD_IDENTIFIER_BEGIN__"
-      << minifi::AgentBuild::BUILD_IDENTIFIER << 
"__EXTENSION_BUILD_IDENTIFIER_END__";
-
-  auto lib = minifi::core::extension::internal::asDynamicLibrary(extension_);
-  REQUIRE(lib);
-  CHECK(lib->verify(logger) == core::extension::internal::Cpp);
-}
-
-TEST_CASE_METHOD(Fixture, "Could load extension with matching C api") {
-  std::ofstream{extension_} << MINIFI_API_VERSION_TAG;
-
-  auto lib = minifi::core::extension::internal::asDynamicLibrary(extension_);
-  REQUIRE(lib);
-  CHECK(lib->verify(logger) == core::extension::internal::CApi);
-}
-
-TEST_CASE_METHOD(Fixture, "Can't load extension if the build id begin marker 
is missing") {
-  std::ofstream{extension_} << "__MISSING_BEGIN__"
-      << minifi::AgentBuild::BUILD_IDENTIFIER << 
"__EXTENSION_BUILD_IDENTIFIER_END__";
-
-  auto lib = minifi::core::extension::internal::asDynamicLibrary(extension_);
-  REQUIRE(lib);
-  CHECK(lib->verify(logger) == core::extension::internal::Invalid);
-}
-
-TEST_CASE_METHOD(Fixture, "Can't load extension if the build id end marker is 
missing") {
-  std::ofstream{extension_} << "__EXTENSION_BUILD_IDENTIFIER_BEGIN__"
-      << minifi::AgentBuild::BUILD_IDENTIFIER << "__MISSING_END__";
-
-  auto lib = minifi::core::extension::internal::asDynamicLibrary(extension_);
-  REQUIRE(lib);
-  CHECK(lib->verify(logger) == core::extension::internal::Invalid);
-}
-
-TEST_CASE_METHOD(Fixture, "Can't load extension if the build id does not 
match") {
-  std::ofstream{extension_} << "__EXTENSION_BUILD_IDENTIFIER_BEGIN__"
-      << "not the build id" << "__EXTENSION_BUILD_IDENTIFIER_END__";
-
-  auto lib = minifi::core::extension::internal::asDynamicLibrary(extension_);
-  REQUIRE(lib);
-  CHECK(lib->verify(logger) == core::extension::internal::Invalid);
-}
-
-TEST_CASE_METHOD(Fixture, "Can't load extension if the file does not exist") {
-  auto lib = minifi::core::extension::internal::asDynamicLibrary(extension_);
-  REQUIRE(lib);
-  REQUIRE_THROWS_AS(lib->verify(logger), std::runtime_error);
-}
-
-TEST_CASE_METHOD(Fixture, "Can't load extension if the file has zero length") {
-  std::ofstream{extension_};  // NOLINT(bugprone-unused-raii)
-
-  auto lib = minifi::core::extension::internal::asDynamicLibrary(extension_);
-  REQUIRE(lib);
-  CHECK(lib->verify(logger) == core::extension::internal::Invalid);
-}
diff --git a/minifi-api/CMakeLists.txt b/minifi-api/CMakeLists.txt
index 95fbb885c..05f78fc99 100644
--- a/minifi-api/CMakeLists.txt
+++ b/minifi-api/CMakeLists.txt
@@ -6,6 +6,7 @@ target_compile_definitions(minifi-api-common INTERFACE 
MINIFI_VERSION_STR="${MIN
 add_library(minifi-api INTERFACE)
 target_include_directories(minifi-api INTERFACE include)
 target_link_libraries(minifi-api INTERFACE minifi-api-common)
+target_compile_definitions(minifi-api INTERFACE 
MINIFI_CREATE_EXTENSION_FN=MinifiCreateExtension_${BUILD_IDENTIFIER})
 
 add_library(minifi-c-api INTERFACE)
 target_include_directories(minifi-c-api INTERFACE include/minifi-c)
diff --git a/minifi-api/include/minifi-c/minifi-c.h 
b/minifi-api/include/minifi-c/minifi-c.h
index 1f22c908c..97af61f5b 100644
--- a/minifi-api/include/minifi-c/minifi-c.h
+++ b/minifi-api/include/minifi-c/minifi-c.h
@@ -31,11 +31,18 @@ extern "C" {
 #define MINIFI_PRIVATE_STRINGIFY_HELPER(X) #X
 #define MINIFI_PRIVATE_STRINGIFY(X) MINIFI_PRIVATE_STRINGIFY_HELPER(X)
 
+#define MINIFI_PRIVATE_JOIN_HELPER(X, Y) X ## _ ## Y
+#define MINIFI_PRIVATE_JOIN(X, Y) MINIFI_PRIVATE_JOIN_HELPER(X, Y)
+
 #define MINIFI_API_MAJOR_VERSION 0
 #define MINIFI_API_MINOR_VERSION 1
 #define MINIFI_API_PATCH_VERSION 0
 #define MINIFI_API_VERSION MINIFI_PRIVATE_STRINGIFY(MINIFI_API_MAJOR_VERSION) 
"." MINIFI_PRIVATE_STRINGIFY(MINIFI_API_MINOR_VERSION) "." 
MINIFI_PRIVATE_STRINGIFY(MINIFI_API_PATCH_VERSION)
-#define MINIFI_API_VERSION_TAG "MINIFI_API_VERSION=[" MINIFI_API_VERSION "]"
+
+#ifndef MINIFI_CREATE_EXTENSION_FN
+#define MINIFI_CREATE_EXTENSION_FN MINIFI_PRIVATE_JOIN(MinifiCreateExtension, 
MINIFI_PRIVATE_JOIN(MINIFI_API_MAJOR_VERSION, MINIFI_API_MINOR_VERSION))
+#endif
+
 #define MINIFI_NULL nullptr
 #define MINIFI_OWNED
 
@@ -87,6 +94,7 @@ typedef struct MinifiOutputStream MinifiOutputStream;
 typedef struct MinifiConfig MinifiConfig;
 typedef struct MinifiExtension MinifiExtension;
 typedef struct MinifiPublishedMetrics MinifiPublishedMetrics;
+typedef struct MinifiApiVersion MinifiApiVersion;
 
 typedef enum MinifiStatus {
   MINIFI_STATUS_SUCCESS = 0,
@@ -184,7 +192,7 @@ typedef struct MinifiExtensionCreateInfo {
 // api_version is used to provide backwards compatible changes to the 
MinifiExtensionCreateInfo structure,
 // e.g. if MinifiExtensionCreateInfo gets a new field in version 1.2.0, 
extensions built with api 1.1.0 do not
 // have to be rebuilt
-MinifiExtension* MinifiCreateExtension(MinifiStringView api_version, const 
MinifiExtensionCreateInfo*);
+MinifiExtension* MINIFI_CREATE_EXTENSION_FN(MinifiStringView api_version, 
const MinifiExtensionCreateInfo* create_info);
 
 MINIFI_OWNED MinifiPublishedMetrics* MinifiPublishedMetricsCreate(size_t 
count, const MinifiStringView* metric_names, const double* metric_values);
 
@@ -225,4 +233,8 @@ void MinifiFlowFileGetAttributes(MinifiProcessSession* 
session, MinifiFlowFile*
 }  // extern "C"
 #endif  // __cplusplus
 
+static inline MinifiExtension* MinifiCreateExtension(MinifiStringView 
api_version, const MinifiExtensionCreateInfo* create_info) {
+  return MINIFI_CREATE_EXTENSION_FN(api_version, create_info);
+}
+
 #endif  // MINIFI_API_INCLUDE_MINIFI_C_MINIFI_C_H_
diff --git a/minifi-api/minifi-c-api.def b/minifi-api/minifi-c-api.def
index 18fad4bff..d7faf9426 100644
--- a/minifi-api/minifi-c-api.def
+++ b/minifi-api/minifi-c-api.def
@@ -1,6 +1,6 @@
 LIBRARY core-minifi.dll
 EXPORTS
-  MinifiCreateExtension
+  MinifiCreateExtension_0_1
   MinifiPublishedMetricsCreate
   MinifiProcessContextGetProperty
   MinifiProcessContextGetProcessorName
@@ -22,4 +22,4 @@ EXPORTS
   MinifiStatusToString
   MinifiFlowFileSetAttribute
   MinifiFlowFileGetAttribute
-  MinifiFlowFileGetAttributes
\ No newline at end of file
+  MinifiFlowFileGetAttributes

Reply via email to