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 090a0fbd8bfa4bb25f376f9ebb1a1da6e9746cb3
Author: Adam Debreceni <[email protected]>
AuthorDate: Wed Mar 25 13:50:54 2026 +0100

    MINIFICPP-2715 - Use symbols to check api compatibility #2105
    
    Signed-off-by: Martin Zink <[email protected]>
---
 Extensions.md                                      |  41 +++++++-
 cmake/Extensions.cmake                             |  26 +----
 .../include/api/utils/minifi-c-utils.h             |   6 ++
 .../include/utils/ExtensionInitUtils.h             |   4 +
 .../SFTPLoader.cpp => ExtensionInitializer.cpp}    |  21 +---
 .../llamacpp/processors/ExtensionInitializer.cpp   |  10 +-
 extensions/opencv/CMakeLists.txt                   |   1 +
 extensions/opencv/OpenCVLoader.cpp                 |   6 +-
 extensions/python/CMakeLists.txt                   |   2 +
 .../python/pythonlibloader/PythonLibLoader.cpp     |   4 +-
 extensions/python/pythonloader/PyProcLoader.cpp    |   4 +-
 extensions/sftp/CMakeLists.txt                     |   1 +
 extensions/sftp/SFTPLoader.cpp                     |   8 +-
 .../include/core/extension/ApiVersion.h            |  21 ++--
 libminifi/include/core/extension/Extension.h       |  13 ++-
 .../include/core/extension/ExtensionManager.h      |   2 +
 libminifi/include/core/extension/Utils.h           |  26 -----
 .../core/extension/ApiVersion.cpp}                 |  40 ++++----
 libminifi/src/core/extension/Extension.cpp         |  90 ++++++++++++-----
 libminifi/src/core/extension/ExtensionManager.cpp  |  30 +++---
 libminifi/src/core/extension/Utils.cpp             |  65 -------------
 libminifi/src/minifi-c.cpp                         |  19 +++-
 libminifi/test/integration/CMakeLists.txt          |   2 +
 .../extension-verification-test/CApiExtension.cpp  |  37 +++----
 .../extension-verification-test/CMakeLists.txt     |  87 +++++++++++++++++
 .../CppApiExtension.cpp                            |  29 ++----
 .../CreateNotCalled.cpp                            |  19 ++--
 .../ExtensionVerificationTests.cpp                 | 100 +++++++++++++++++++
 .../InvalidMissingInitExtension.cpp                |  18 +---
 .../extension-verification-test/dummy-cpp-api.def  |   4 +
 libminifi/test/unit/ExtensionVerificationTests.cpp | 107 ---------------------
 minifi-api/CMakeLists.txt                          |   1 +
 minifi-api/include/minifi-c/minifi-c.h             |  22 +++--
 minifi-api/minifi-c-api.def                        |   2 +-
 34 files changed, 448 insertions(+), 420 deletions(-)

diff --git a/Extensions.md b/Extensions.md
index 7d3fd5392..f75c068e6 100644
--- a/Extensions.md
+++ b/Extensions.md
@@ -17,8 +17,39 @@
 To enable all extensions for your platform, you may use -DENABLE_ALL=TRUE OR 
select the option to "Enable all Extensions" in the bootstrap script. 
[ReadMe](https://github.com/apache/nifi-minifi-cpp/#bootstrapping)
 
 # Extension internals
-Extensions are dynamic libraries loaded at runtime by the agent. An extension 
makes its
-capabilities (classes) available to the system through registrars. 
Registration must happen in source files, not headers.
+Extensions are dynamic libraries loaded at runtime by the agent.
+
+## C extensions
+You can build a shared library depending on the C capabilities of the agent as 
given in the `minifi-c.h` file.
+For the shared library to be considered a valid extension, it has to have a 
global symbol with the name `MinifiCApiVersion`
+with its value as a null terminated string (`const char*`) of the macro 
`MINIFI_API_VERSION` from `minifi-c.h`.
+
+Moreover the actual resource registration (processors/controller services) has 
to happen during the `MinifiInitExtension` call.
+One possible example of this is:
+
+```C++
+extern "C" const char* const MinifiApiVersion = MINIFI_API_VERSION;
+
+extern "C" void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* 
/*config*/) {
+  
minifi::api::core::useProcessorClassDescription<minifi::extensions::llamacpp::processors::RunLlamaCppInference>([&]
 (const MinifiProcessorClassDefinition& description) {
+    MinifiExtensionCreateInfo ext_create_info{
+      .name = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_NAME)),
+      .version = 
minifi::api::utils::toStringView(MAKESTRING(EXTENSION_VERSION)),
+      .deinit = nullptr,
+      .user_data = nullptr,
+      .processors_count = 1,
+      .processors_ptr = &description,
+    };
+    MinifiCreateExtension(extension, &ext_create_info);
+  });
+}
+```
+
+## C++ extensions
+You can utilize the C++ api, linking to `minifi-api` and possibly using the 
helpers in `extension-framework`.
+No compatibilities are guaranteed beyond what extensions are built together 
with the agent at the same time.
+
+An extension makes its capabilities (classes) available to the system through 
registrars. Registration must happen in source files, not headers.
 
 ```C++
 // register user-facing classes as
@@ -33,10 +64,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 `MinifiInitCppExtension` function of type 
`MinifiExtension*(MinifiConfig*)` to be called.
 
 ```C++
-extern "C" MinifiExtension* InitExtension(MinifiConfig* /*config*/) {
+extern "C" void MinifiInitCppExtension(MinifiExtension* extension, 
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;
@@ -49,7 +80,7 @@ extern "C" MinifiExtension* InitExtension(MinifiConfig* 
/*config*/) {
     .processors_count = 0,
     .processors_ptr = nullptr
   };
-  return 
MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), 
&ext_create_info);
+  minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info);
 }
 ```
 
diff --git a/cmake/Extensions.cmake b/cmake/Extensions.cmake
index 5786967a8..6e25d288b 100644
--- a/cmake/Extensions.cmake
+++ b/cmake/Extensions.cmake
@@ -22,32 +22,16 @@ 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}")
+        PRIVATE "MODULE_NAME=${extension-name}")
     set_target_properties(${extension-name} PROPERTIES
         ENABLE_EXPORTS True
         POSITION_INDEPENDENT_CODE ON)
diff --git 
a/extension-framework/cpp-extension-lib/include/api/utils/minifi-c-utils.h 
b/extension-framework/cpp-extension-lib/include/api/utils/minifi-c-utils.h
index 756c52113..43fb28306 100644
--- a/extension-framework/cpp-extension-lib/include/api/utils/minifi-c-utils.h
+++ b/extension-framework/cpp-extension-lib/include/api/utils/minifi-c-utils.h
@@ -24,6 +24,12 @@
 #include "utils/StringUtils.h"
 #include "minifi-cpp/core/PropertyDefinition.h"
 
+#ifdef WIN32
+  #define CEXTENSIONAPI extern "C" __declspec(dllexport)
+#else
+  #define CEXTENSIONAPI extern "C"
+#endif
+
 namespace org::apache::nifi::minifi::api::utils {
 
 inline MinifiStringView toStringView(std::string_view str) {
diff --git a/extension-framework/include/utils/ExtensionInitUtils.h 
b/extension-framework/include/utils/ExtensionInitUtils.h
index 1af7617e5..75da5c1ae 100644
--- a/extension-framework/include/utils/ExtensionInitUtils.h
+++ b/extension-framework/include/utils/ExtensionInitUtils.h
@@ -30,4 +30,8 @@ inline MinifiStringView toStringView(std::string_view str) {
 
 using ConfigReader = std::function<std::optional<std::string>(std::string_view 
key)>;
 
+static inline void MinifiCreateCppExtension(MinifiExtension* extension, const 
MinifiExtensionCreateInfo* create_info) {
+  MINIFI_CREATE_EXTENSION_FN(extension, create_info);
+}
+
 }  // namespace org::apache::nifi::minifi::utils
diff --git a/extensions/sftp/SFTPLoader.cpp 
b/extensions/ExtensionInitializer.cpp
similarity index 71%
copy from extensions/sftp/SFTPLoader.cpp
copy to extensions/ExtensionInitializer.cpp
index 8a67842de..47a4c2185 100644
--- a/extensions/sftp/SFTPLoader.cpp
+++ b/extensions/ExtensionInitializer.cpp
@@ -15,34 +15,21 @@
  * 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" void MinifiInitCppExtension(MinifiExtension* extension, 
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
   };
-  return 
MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), 
&ext_create_info);
+  minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info);
 }
diff --git a/extensions/llamacpp/processors/ExtensionInitializer.cpp 
b/extensions/llamacpp/processors/ExtensionInitializer.cpp
index 40508955c..4b3c5b4e0 100644
--- a/extensions/llamacpp/processors/ExtensionInitializer.cpp
+++ b/extensions/llamacpp/processors/ExtensionInitializer.cpp
@@ -24,8 +24,9 @@
 
 namespace minifi = org::apache::nifi::minifi;
 
-extern "C" MinifiExtension* InitExtension(MinifiConfig* /*config*/) {
-  MinifiExtension* extension = nullptr;
+CEXTENSIONAPI const uint32_t MinifiApiVersion = MINIFI_API_VERSION;
+
+CEXTENSIONAPI void MinifiInitExtension(MinifiExtension* extension, 
MinifiConfig* /*config*/) {
   
minifi::api::core::useProcessorClassDescription<minifi::extensions::llamacpp::processors::RunLlamaCppInference>([&]
 (const MinifiProcessorClassDefinition& description) {
     MinifiExtensionCreateInfo ext_create_info{
       .name = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_NAME)),
@@ -35,9 +36,6 @@ extern "C" MinifiExtension* InitExtension(MinifiConfig* 
/*config*/) {
       .processors_count = 1,
       .processors_ptr = &description,
     };
-    extension = 
MinifiCreateExtension(minifi::api::utils::toStringView(MINIFI_API_VERSION), 
&ext_create_info);
+    MinifiCreateExtension(extension, &ext_create_info);
   });
-  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..87c6615c5 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" void MinifiInitCppExtension(MinifiExtension* extension, 
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:
@@ -32,7 +32,7 @@ extern "C" MinifiExtension* InitExtension(MinifiConfig* 
/*config*/) {
   // 2. The command will not overwrite value if 
"OPENCV_FFMPEG_CAPTURE_OPTIONS" already exists.
   const auto success = 
org::apache::nifi::minifi::utils::Environment::setEnvironmentVariable("OPENCV_FFMPEG_CAPTURE_OPTIONS",
 "rtsp_transport;udp", false /*overwrite*/);
   if (!success) {
-    return nullptr;
+    return;
   }
   MinifiExtensionCreateInfo ext_create_info{
     .name = minifi::utils::toStringView(MAKESTRING(MODULE_NAME)),
@@ -42,5 +42,5 @@ extern "C" MinifiExtension* InitExtension(MinifiConfig* 
/*config*/) {
     .processors_count = 0,
     .processors_ptr = nullptr
   };
-  return 
MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), 
&ext_create_info);
+  minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info);
 }
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..79ef21556 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" void MinifiInitCppExtension(MinifiExtension* extension, 
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) {
@@ -114,5 +114,5 @@ extern "C" MinifiExtension* InitExtension(MinifiConfig* 
config) {
     .processors_count = 0,
     .processors_ptr = nullptr
   };
-  return 
MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), 
&ext_create_info);
+  minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info);
 }
diff --git a/extensions/python/pythonloader/PyProcLoader.cpp 
b/extensions/python/pythonloader/PyProcLoader.cpp
index 3778f15ec..e92bbea41 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" void MinifiInitCppExtension(MinifiExtension* extension, 
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) {
@@ -49,5 +49,5 @@ extern "C" MinifiExtension* InitExtension(MinifiConfig* 
config) {
     .processors_count = 0,
     .processors_ptr = nullptr
   };
-  return 
MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), 
&ext_create_info);
+  minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info);
 }
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..c8309cc60 100644
--- a/extensions/sftp/SFTPLoader.cpp
+++ b/extensions/sftp/SFTPLoader.cpp
@@ -25,13 +25,13 @@
 
 namespace minifi = org::apache::nifi::minifi;
 
-extern "C" MinifiExtension* InitExtension(MinifiConfig* /*config*/) {
+extern "C" void MinifiInitCppExtension(MinifiExtension* extension, 
MinifiConfig* /*config*/) {
   if (libssh2_init(0) != 0) {
-    return nullptr;
+    return;
   }
   if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) {
     libssh2_exit();
-    return nullptr;
+    return;
   }
   MinifiExtensionCreateInfo ext_create_info{
     .name = minifi::utils::toStringView(MAKESTRING(MODULE_NAME)),
@@ -44,5 +44,5 @@ extern "C" MinifiExtension* InitExtension(MinifiConfig* 
/*config*/) {
     .processors_count = 0,
     .processors_ptr = nullptr
   };
-  return 
MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), 
&ext_create_info);
+  minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info);
 }
diff --git a/extension-framework/include/utils/ExtensionInitUtils.h 
b/libminifi/include/core/extension/ApiVersion.h
similarity index 61%
copy from extension-framework/include/utils/ExtensionInitUtils.h
copy to libminifi/include/core/extension/ApiVersion.h
index 1af7617e5..4754f4ef0 100644
--- a/extension-framework/include/utils/ExtensionInitUtils.h
+++ b/libminifi/include/core/extension/ApiVersion.h
@@ -1,5 +1,5 @@
 /**
- * Licensed to the Apache Software Foundation (ASF) under one or more
+* 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
@@ -14,20 +14,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #pragma once
 
 #include <string>
-#include <string_view>
-#include <optional>
-#include <functional>
-#include "minifi-c/minifi-c.h"
-
-namespace org::apache::nifi::minifi::utils {
+#include <cinttypes>
 
-inline MinifiStringView toStringView(std::string_view str) {
-  return MinifiStringView{.data = str.data(), .length = str.length()};
-}
+namespace org::apache::nifi::minifi::core::extension {
 
-using ConfigReader = std::function<std::optional<std::string>(std::string_view 
key)>;
+uint32_t getAgentApiVersion();
+uint32_t getMinSupportedApiVersion();
+void test_setAgentApiVersion(uint32_t api_version);
+void test_setMinSupportedApiVersion(uint32_t min_api_version);
 
-}  // namespace org::apache::nifi::minifi::utils
+}  // namespace org::apache::nifi::minifi::core::extension
diff --git a/libminifi/include/core/extension/Extension.h 
b/libminifi/include/core/extension/Extension.h
index e14cb97ed..85cf4fc60 100644
--- a/libminifi/include/core/extension/Extension.h
+++ b/libminifi/include/core/extension/Extension.h
@@ -17,13 +17,15 @@
 
 #pragma once
 
-#include <memory>
+#include <filesystem>
 #include <map>
+#include <memory>
 #include <string>
-#include <filesystem>
 
+#include "minifi-c/minifi-c.h"
 #include "minifi-cpp/core/logging/Logger.h"
 #include "minifi-cpp/properties/Configure.h"
+#include "ApiVersion.h"
 
 namespace org::apache::nifi::minifi::core::extension {
 
@@ -49,6 +51,8 @@ class Extension {
 
   bool initialize(const std::shared_ptr<minifi::Configure>& configure);
 
+  bool setInfo(Info info);
+
  private:
 #ifdef WIN32
   std::map<void*, std::string> resource_mapping_;
@@ -67,11 +71,12 @@ class Extension {
   bool unload();
   void* findSymbol(const char* name);
 
-  std::string name_;
+  std::string library_name_;
   std::filesystem::path library_path_;
   gsl::owner<void*> handle_ = nullptr;
 
-  std::unique_ptr<Info> info_;
+  std::optional<Info> info_;
+  uint32_t api_version_{0};
 
   const std::shared_ptr<logging::Logger> logger_;
 };
diff --git a/libminifi/include/core/extension/ExtensionManager.h 
b/libminifi/include/core/extension/ExtensionManager.h
index c610bd70a..7b312ae4b 100644
--- a/libminifi/include/core/extension/ExtensionManager.h
+++ b/libminifi/include/core/extension/ExtensionManager.h
@@ -37,6 +37,8 @@ class ExtensionManager {
   ExtensionManager& operator=(const ExtensionManager&) = delete;
   ExtensionManager& operator=(ExtensionManager&&) = delete;
 
+  static Extension* getExtensionBeingInitialized();
+
  private:
   std::vector<std::unique_ptr<Extension>> extensions_;
 
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/include/core/extension/ExtensionManager.h 
b/libminifi/src/core/extension/ApiVersion.cpp
similarity index 54%
copy from libminifi/include/core/extension/ExtensionManager.h
copy to libminifi/src/core/extension/ApiVersion.cpp
index c610bd70a..bf5259420 100644
--- a/libminifi/include/core/extension/ExtensionManager.h
+++ b/libminifi/src/core/extension/ApiVersion.cpp
@@ -1,5 +1,5 @@
 /**
- * Licensed to the Apache Software Foundation (ASF) under one or more
+* 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
@@ -15,32 +15,28 @@
  * limitations under the License.
  */
 
-#pragma once
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "minifi-cpp/core/logging/Logger.h"
-#include "Extension.h"
-#include "properties/Configure.h"
+#include "core/extension/ApiVersion.h"
+#include "minifi-c/minifi-c.h"
 
 namespace org::apache::nifi::minifi::core::extension {
 
-class ExtensionManager {
- public:
-  explicit ExtensionManager(const std::shared_ptr<Configure>& config);
-  ~ExtensionManager() = default;
+static uint32_t agent_api_version{MINIFI_API_VERSION};
+static uint32_t min_supported_api_version{MINIFI_API_VERSION};
+
+uint32_t getAgentApiVersion() {
+  return agent_api_version;
+}
 
-  ExtensionManager(const ExtensionManager&) = delete;
-  ExtensionManager(ExtensionManager&&) = delete;
-  ExtensionManager& operator=(const ExtensionManager&) = delete;
-  ExtensionManager& operator=(ExtensionManager&&) = delete;
+void test_setAgentApiVersion(uint32_t api_version) {
+  agent_api_version = api_version;
+}
 
- private:
-  std::vector<std::unique_ptr<Extension>> extensions_;
+uint32_t getMinSupportedApiVersion() {
+  return min_supported_api_version;
+}
 
-  std::shared_ptr<logging::Logger> logger_;
-};
+void test_setMinSupportedApiVersion(uint32_t min_api_version) {
+  min_supported_api_version = min_api_version;
+}
 
 }  // namespace org::apache::nifi::minifi::core::extension
diff --git a/libminifi/src/core/extension/Extension.cpp 
b/libminifi/src/core/extension/Extension.cpp
index 8b7b7f740..f8ca0f5f0 100644
--- a/libminifi/src/core/extension/Extension.cpp
+++ b/libminifi/src/core/extension/Extension.cpp
@@ -38,11 +38,12 @@
 #include "core/logging/LoggerFactory.h"
 #include "minifi-c/minifi-c.h"
 #include "minifi-cpp/agent/agent_docs.h"
+#include "utils/RegexUtils.h"
 
 namespace org::apache::nifi::minifi::core::extension {
 
-Extension::Extension(std::string name, std::filesystem::path library_path)
-  : name_(std::move(name)),
+Extension::Extension(std::string library_name, std::filesystem::path 
library_path)
+  : library_name_(std::move(library_name)),
     library_path_(std::move(library_path)),
     logger_(logging::LoggerFactory<Extension>::getLogger()) {
 }
@@ -55,26 +56,51 @@ bool Extension::load(bool global) {
     handle_ = dlopen(library_path_.string().c_str(), RTLD_NOW | RTLD_LOCAL);  
// NOLINT(cppcoreguidelines-owning-memory)
   }
   if (!handle_) {
-    logger_->log_error("Failed to load extension '{}' at '{}': {}", name_, 
library_path_, dlerror());
+    logger_->log_error("Failed to load extension '{}' at '{}': {}", 
library_name_, library_path_, dlerror());
     return false;
-  } else {
-    logger_->log_trace("Loaded extension '{}' at '{}'", name_, library_path_);
+  }
+  logger_->log_trace("Dlopen succeeded for extension '{}' at '{}'", 
library_name_, library_path_);
+  if (findSymbol("MinifiInitCppExtension")) {
+    logger_->log_trace("Loaded cpp extension '{}' at '{}'", library_name_, 
library_path_);
     return true;
   }
+  if (!findSymbol("MinifiInitExtension")) {
+    logger_->log_error("Failed to load as c extension '{}' at '{}': No 
initializer found", library_name_, library_path_);
+    return false;
+  }
+  auto api_version_ptr = reinterpret_cast<const 
uint32_t*>(findSymbol("MinifiApiVersion"));
+  if (!api_version_ptr) {
+    logger_->log_error("Failed to load c extension '{}' at '{}': No 
MinifiApiVersion symbol found", library_name_, library_path_);
+    return false;
+  }
+  api_version_ = *api_version_ptr;
+  if (api_version_ < getMinSupportedApiVersion()) {
+    logger_->log_error("Failed to load c extension '{}' at '{}': Api version 
is no longer supported, application supports {}-{} while extension is {}",
+        library_name_, library_path_, getMinSupportedApiVersion(), 
getAgentApiVersion(), api_version_);
+    return false;
+  }
+  if (api_version_ > getAgentApiVersion()) {
+    logger_->log_error("Failed to load c extension '{}' at '{}': Extension is 
built for a newer version, application supports {}-{} while extension is {}",
+        library_name_, library_path_, getMinSupportedApiVersion(), 
getAgentApiVersion(), api_version_);
+    return false;
+  }
+  logger_->log_debug("Loaded c extension '{}' at '{}': Application version is 
{}, extension version is {}",
+        library_name_, library_path_, getAgentApiVersion(), api_version_);
+  return true;
 }
 
 bool Extension::unload() {
-  logger_->log_trace("Unloading library '{}' at '{}'", name_, library_path_);
+  logger_->log_trace("Unloading library '{}' at '{}'", library_name_, 
library_path_);
   if (!handle_) {
-    logger_->log_error("Extension does not have a handle_ '{}' at '{}'", 
name_, library_path_);
+    logger_->log_error("Extension does not have a handle_ '{}' at '{}'", 
library_name_, library_path_);
     return true;
   }
   dlerror();
   if (dlclose(handle_)) {
-    logger_->log_error("Failed to unload extension '{}' at '{}': {}", name_, 
library_path_, dlerror());
+    logger_->log_error("Failed to unload extension '{}' at '{}': {}", 
library_name_, library_path_, dlerror());
     return false;
   }
-  logger_->log_trace("Unloaded extension '{}' at '{}'", name_, library_path_);
+  logger_->log_trace("Unloaded extension '{}' at '{}'", library_name_, 
library_path_);
   handle_ = nullptr;
   return true;
 }
@@ -92,6 +118,8 @@ Extension::~Extension() {
   }
   unload();
 
+  const std::string bundle_name = info_ ? info_->name : library_name_;
+
   // Check if library was truly unloaded and clear class descriptions if it 
was.
   // On Linux/GCC, STB_GNU_UNIQUE symbols can prevent dlclose from actually 
unloading the library.
   // Some libraries could prevent unloading like RocksDB where Env::Default() 
creates a global singleton where background threads hold references which 
prevents unloading
@@ -101,30 +129,44 @@ Extension::~Extension() {
     // Keep class descriptions if library is still in memory
     dlclose(check);
   } else {
-    ClassDescriptionRegistry::clearClassDescriptionsForBundle(name_);
+    ClassDescriptionRegistry::clearClassDescriptionsForBundle(bundle_name);
   }
 #else
-  HMODULE handle = GetModuleHandleA(name_.c_str());
+  HMODULE handle = GetModuleHandleA(library_name_.c_str());
   if (handle == nullptr) {
-    ClassDescriptionRegistry::clearClassDescriptionsForBundle(name_);
+    ClassDescriptionRegistry::clearClassDescriptionsForBundle(bundle_name);
   }
 #endif
 }
 
+bool Extension::setInfo(Info info) {
+  if (info_) {
+    return false;
+  }
+  info_ = std::move(info);
+  return true;
+}
+
 bool Extension::initialize(const std::shared_ptr<minifi::Configure>& 
configure) {
-  logger_->log_trace("Initializing extension '{}'", name_);
-  if (void* init_symbol_ptr = findSymbol("InitExtension")) {
-    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());
-    info_.reset(reinterpret_cast<Info*>(init_fn(config_handle)));
-    if (!info_) {
-      logger_->log_error("Failed to initialize extension '{}'", name_);
-      return false;
-    }
-  } else {
-    logger_->log_debug("No custom initializer for '{}'", name_);
+  logger_->log_trace("Initializing extension '{}'", library_name_);
+  void* init_symbol_ptr = findSymbol("MinifiInitCppExtension");
+  if (!init_symbol_ptr) {
+    init_symbol_ptr = findSymbol("MinifiInitExtension");
+  }
+  if (!init_symbol_ptr) {
+    logger_->log_error("No initializer for '{}'", library_name_);
+    return false;
+  }
+  logger_->log_debug("Found initializer for '{}'", library_name_);
+  auto init_fn = reinterpret_cast<void(*)(MinifiExtension*, 
MinifiConfig*)>(init_symbol_ptr);
+  auto config_handle = reinterpret_cast<MinifiConfig*>(configure.get());
+  auto extension_handle = reinterpret_cast<MinifiExtension*>(this);
+  init_fn(extension_handle, config_handle);
+  if (!info_) {
+    logger_->log_error("Failed to initialize extension '{}'", library_name_);
+    return false;
   }
+  logger_->log_debug("Initialized extension '{}', name = '{}', version = 
'{}'", library_name_, info_->name, info_->version);
   return true;
 }
 
diff --git a/libminifi/src/core/extension/ExtensionManager.cpp 
b/libminifi/src/core/extension/ExtensionManager.cpp
index b0ce8882f..689a6cd42 100644
--- a/libminifi/src/core/extension/ExtensionManager.cpp
+++ b/libminifi/src/core/extension/ExtensionManager.cpp
@@ -30,6 +30,16 @@
 
 namespace org::apache::nifi::minifi::core::extension {
 
+namespace {
+
+std::atomic<Extension*> extension_being_initialized{nullptr};
+
+}  // namespace
+
+Extension* ExtensionManager::getExtensionBeingInitialized() {
+  return extension_being_initialized.load();
+}
+
 ExtensionManager::ExtensionManager(const std::shared_ptr<Configure>& config): 
logger_(logging::LoggerFactory<ExtensionManager>::getLogger()) {
   logger_->log_trace("Initializing extensions");
   if (!config) {
@@ -58,14 +68,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
@@ -79,10 +81,14 @@ ExtensionManager::ExtensionManager(const 
std::shared_ptr<Configure>& config): lo
         continue;
       }
     }
-    if (!extension->initialize(config)) {
-      logger_->log_error("Failed to initialize extension '{}' at '{}'", 
library->name, library->getFullPath());
-    } else {
-      extensions_.push_back(std::move(extension));
+    {
+      auto extension_guard = gsl::finally([] {extension_being_initialized = 
nullptr;});
+      extension_being_initialized = extension.get();
+      if (!extension->initialize(config)) {
+        logger_->log_error("Failed to initialize extension '{}' at '{}'", 
library->name, library->getFullPath());
+      } else {
+        extensions_.push_back(std::move(extension));
+      }
     }
   }
 }
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 ba15ee0cf..55fe7cf8d 100644
--- a/libminifi/src/minifi-c.cpp
+++ b/libminifi/src/minifi-c.cpp
@@ -22,6 +22,8 @@
 
 #include "agent/agent_docs.h"
 #include "core/ProcessorMetrics.h"
+#include "core/extension/ExtensionManager.h"
+#include "minifi-cpp/Exception.h"
 #include "minifi-cpp/core/Annotation.h"
 #include "minifi-cpp/core/ClassLoader.h"
 #include "minifi-cpp/core/ProcessContext.h"
@@ -33,10 +35,8 @@
 #include "minifi-cpp/core/PropertyValidator.h"
 #include "minifi-cpp/core/logging/Logger.h"
 #include "minifi-cpp/core/state/PublishedMetricProvider.h"
-#include "minifi-cpp/Exception.h"
-#include "core/extension/ExtensionManager.h"
-#include "utils/PropertyErrors.h"
 #include "utils/CProcessor.h"
+#include "utils/PropertyErrors.h"
 
 namespace minifi = org::apache::nifi::minifi;
 
@@ -225,7 +225,8 @@ void useCProcessorClassDescription(const 
MinifiProcessorClassDefinition& class_d
 
 extern "C" {
 
-MinifiExtension* MinifiCreateExtension(MinifiStringView /*api_version*/, const 
MinifiExtensionCreateInfo* extension_create_info) {
+MinifiStatus MinifiCreateExtension(MinifiExtension* extension, const 
MinifiExtensionCreateInfo* extension_create_info) {
+  gsl_Assert(extension);
   gsl_Assert(extension_create_info);
   auto extension_name = toString(extension_create_info->name);
   minifi::BundleIdentifier bundle{
@@ -241,12 +242,20 @@ MinifiExtension* MinifiCreateExtension(MinifiStringView 
/*api_version*/, const M
       bundle_components.processors.emplace_back(description);
     });
   }
-  return reinterpret_cast<MinifiExtension*>(new 
org::apache::nifi::minifi::core::extension::Extension::Info{
+  bool success = 
reinterpret_cast<org::apache::nifi::minifi::core::extension::Extension*>(extension)->setInfo(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
   });
+  if (success) {
+    return MINIFI_STATUS_SUCCESS;
+  }
+  return MINIFI_STATUS_UNKNOWN_ERROR;
+}
+
+MinifiStatus MINIFI_CREATE_EXTENSION_FN(MinifiExtension* extension, const 
MinifiExtensionCreateInfo* extension_create_info) {
+  return MinifiCreateExtension(extension, extension_create_info);
 }
 
 MinifiStatus MinifiProcessContextGetProperty(MinifiProcessContext* context, 
MinifiStringView property_name, MinifiFlowFile* flowfile,
diff --git a/libminifi/test/integration/CMakeLists.txt 
b/libminifi/test/integration/CMakeLists.txt
index 3fb3fd88a..44e98f76a 100644
--- a/libminifi/test/integration/CMakeLists.txt
+++ b/libminifi/test/integration/CMakeLists.txt
@@ -45,3 +45,5 @@ ENDFOREACH()
 target_link_libraries(OnScheduleErrorHandlingTests minifi-test-processors)
 
 message("-- Finished building ${INT_TEST_COUNT} integration test file(s)...")
+
+add_subdirectory(extension-verification-test)
diff --git a/extensions/sftp/SFTPLoader.cpp 
b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp
similarity index 54%
copy from extensions/sftp/SFTPLoader.cpp
copy to libminifi/test/integration/extension-verification-test/CApiExtension.cpp
index 8a67842de..18f47f280 100644
--- a/extensions/sftp/SFTPLoader.cpp
+++ b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp
@@ -1,5 +1,5 @@
 /**
- *
+*
  * 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.
@@ -16,33 +16,24 @@
  * 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 "core/Resource.h"
+#include "api/core/Resource.h"
+#include "api/utils/minifi-c-utils.h"
+
+#define MKSOC(x) #x
+#define MAKESTRING(x) MKSOC(x)  // NOLINT(cppcoreguidelines-macro-usage)
 
 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;
-  }
+CEXTENSIONAPI const uint32_t MinifiApiVersion = MINIFI_TEST_API_VERSION;
+
+CEXTENSIONAPI void MinifiInitExtension(MinifiExtension* extension, 
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();
-    },
+    .name = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_NAME)),
+    .version = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_VERSION)),
+    .deinit = nullptr,
     .user_data = nullptr,
     .processors_count = 0,
-    .processors_ptr = nullptr
+    .processors_ptr = nullptr,
   };
-  return 
MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), 
&ext_create_info);
+  MinifiCreateExtension(extension, &ext_create_info);
 }
diff --git 
a/libminifi/test/integration/extension-verification-test/CMakeLists.txt 
b/libminifi/test/integration/extension-verification-test/CMakeLists.txt
new file mode 100644
index 000000000..7f3146b19
--- /dev/null
+++ b/libminifi/test/integration/extension-verification-test/CMakeLists.txt
@@ -0,0 +1,87 @@
+# 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.
+#
+
+add_minifi_executable(ExtensionVerificationTests 
ExtensionVerificationTests.cpp)
+createIntegrationTests(ExtensionVerificationTests)
+set_target_properties(ExtensionVerificationTests PROPERTIES ENABLE_EXPORTS 
True POSITION_INDEPENDENT_CODE ON)
+if(WIN32)
+    set_target_properties(ExtensionVerificationTests PROPERTIES 
WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
+elseif(APPLE)
+    target_link_options(ExtensionVerificationTests PRIVATE -rdynamic)
+endif()
+target_link_libraries(ExtensionVerificationTests core-minifi Catch2WithMain 
Threads::Threads)
+add_test(NAME ExtensionVerificationTests COMMAND ExtensionVerificationTests)
+
+function(create_loading_test_extension extension-name extension-main)
+    add_library(${extension-name} SHARED ${extension-main})
+    add_dependencies(ExtensionVerificationTests ${extension-name})
+    target_compile_definitions(${extension-name} PRIVATE 
MODULE_NAME=${extension-name})
+    set_target_properties(${extension-name} PROPERTIES ENABLE_EXPORTS True 
POSITION_INDEPENDENT_CODE ON)
+    if(WIN32)
+        set_target_properties(${extension-name} PROPERTIES
+            RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
+            ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
+            WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
+    else()
+        set_target_properties(${extension-name} PROPERTIES 
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
+    endif()
+endfunction()
+
+function(create_c_test_extension extension-name custom_api_version)
+    set(extension-name "test-extension-loading-${extension-name}")
+    create_loading_test_extension(${extension-name} CApiExtension.cpp)
+    target_link_libraries(${extension-name} minifi-cpp-extension-lib)
+    target_compile_definitions(${extension-name} PRIVATE 
MINIFI_TEST_API_VERSION=${custom_api_version})
+    target_compile_definitions(${extension-name}
+            PRIVATE "EXTENSION_NAME=${extension-name}" 
"EXTENSION_VERSION=${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
+endfunction()
+
+add_library(dummy-cpp-api INTERFACE)
+target_include_directories(dummy-cpp-api INTERFACE 
$<TARGET_PROPERTY:minifi-extension-framework,INTERFACE_INCLUDE_DIRECTORIES>)
+if (WIN32)
+    set(DUMMY_IMPORT_LIB_FILE ${CMAKE_CURRENT_BINARY_DIR}/dummy-cpp-api.lib)
+    add_custom_target(dummy_cpp_api_import_lib
+            COMMAND lib.exe /def:${CMAKE_CURRENT_SOURCE_DIR}/dummy-cpp-api.def 
/out:${DUMMY_IMPORT_LIB_FILE} /machine:x64
+            BYPRODUCTS ${DUMMY_IMPORT_LIB_FILE}
+    )
+
+    add_dependencies(dummy-cpp-api dummy_cpp_api_import_lib)
+    target_link_libraries(dummy-cpp-api INTERFACE ${DUMMY_IMPORT_LIB_FILE})
+elseif (APPLE)
+    target_link_options(dummy-cpp-api INTERFACE -undefined dynamic_lookup)
+endif()
+
+create_loading_test_extension(test-extension-loading-invalid-build-id-cpp 
CppApiExtension.cpp)
+target_link_libraries(test-extension-loading-invalid-build-id-cpp 
dummy-cpp-api)
+target_compile_definitions(test-extension-loading-invalid-build-id-cpp PRIVATE 
MINIFI_CREATE_EXTENSION_FN=MinifiCreateCppExtension_NonMatchingBuildId)
+
+create_loading_test_extension(test-extension-loading-valid-build-id-cpp 
CppApiExtension.cpp)
+target_link_libraries(test-extension-loading-valid-build-id-cpp dummy-cpp-api)
+target_compile_definitions(test-extension-loading-valid-build-id-cpp PRIVATE 
MINIFI_CREATE_EXTENSION_FN=MinifiCreateCppExtension_MatchingBuildId)
+
+create_c_test_extension(max-version 10)
+create_c_test_extension(valid-version 7)
+create_c_test_extension(min-version 5)
+create_c_test_extension(greater-version 11)
+create_c_test_extension(smaller-version 4)
+create_loading_test_extension(test-extension-loading-missing-init 
InvalidMissingInitExtension.cpp)
+
+create_loading_test_extension(test-extension-loading-create-not-called 
CreateNotCalled.cpp)
+target_link_libraries(test-extension-loading-create-not-called 
minifi-cpp-extension-lib)
+target_compile_definitions(test-extension-loading-create-not-called PRIVATE 
MINIFI_TEST_API_VERSION=10)
diff --git a/extensions/sftp/SFTPLoader.cpp 
b/libminifi/test/integration/extension-verification-test/CppApiExtension.cpp
similarity index 61%
copy from extensions/sftp/SFTPLoader.cpp
copy to 
libminifi/test/integration/extension-verification-test/CppApiExtension.cpp
index 8a67842de..59daee45d 100644
--- a/extensions/sftp/SFTPLoader.cpp
+++ b/libminifi/test/integration/extension-verification-test/CppApiExtension.cpp
@@ -1,5 +1,5 @@
 /**
- *
+*
  * 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.
@@ -16,33 +16,22 @@
  * 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 <string_view>
 #include "utils/ExtensionInitUtils.h"
-#include "core/Resource.h"
+
+#define MKSOC(x) #x
+#define MAKESTRING(x) MKSOC(x)  // NOLINT(cppcoreguidelines-macro-usage)
 
 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" void MinifiInitCppExtension(MinifiExtension* extension, 
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();
-    },
+    .version = minifi::utils::toStringView("1.0.0"),
+    .deinit = nullptr,
     .user_data = nullptr,
     .processors_count = 0,
     .processors_ptr = nullptr
   };
-  return 
MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), 
&ext_create_info);
+  minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info);
 }
diff --git a/extension-framework/include/utils/ExtensionInitUtils.h 
b/libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp
similarity index 64%
copy from extension-framework/include/utils/ExtensionInitUtils.h
copy to 
libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp
index 1af7617e5..276ffa6d5 100644
--- a/extension-framework/include/utils/ExtensionInitUtils.h
+++ b/libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp
@@ -1,4 +1,5 @@
 /**
+*
  * 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.
@@ -14,20 +15,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
 
-#include <string>
-#include <string_view>
-#include <optional>
-#include <functional>
-#include "minifi-c/minifi-c.h"
+#include "minifi-c.h"
+#include "api/utils/minifi-c-utils.h"
 
-namespace org::apache::nifi::minifi::utils {
+CEXTENSIONAPI const uint32_t MinifiApiVersion = MINIFI_TEST_API_VERSION;
 
-inline MinifiStringView toStringView(std::string_view str) {
-  return MinifiStringView{.data = str.data(), .length = str.length()};
+CEXTENSIONAPI void MinifiInitExtension(MinifiExtension* extension, 
MinifiConfig* /*config*/) {
+  // NOT CALLING CREATE
 }
-
-using ConfigReader = std::function<std::optional<std::string>(std::string_view 
key)>;
-
-}  // namespace org::apache::nifi::minifi::utils
diff --git 
a/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp
 
b/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp
new file mode 100644
index 000000000..86084f80d
--- /dev/null
+++ 
b/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp
@@ -0,0 +1,100 @@
+/**
+*
+ * 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
+
+#undef MINIFI_CREATE_EXTENSION_FN
+
+#include "unit/TestBase.h"
+#include "unit/Catch.h"
+#include "unit/TestUtils.h"
+#include "core/extension/ExtensionManager.h"
+#include "core/extension/ApiVersion.h"
+
+
+extern "C" {
+MinifiStatus MinifiCreateCppExtension_MatchingBuildId(MinifiExtension* 
extension, const MinifiExtensionCreateInfo* extension_create_info) {
+  return MinifiCreateExtension(extension, extension_create_info);
+}
+}  // extern "C"
+
+namespace minifi = org::apache::nifi::minifi;
+
+class ExtensionLoadingTestController {
+ public:
+  explicit ExtensionLoadingTestController(std::string pattern): 
extension_manager_{[&] () {
+    LogTestController::getInstance().clear();
+    
LogTestController::getInstance().setTrace<core::extension::ExtensionManager>();
+    LogTestController::getInstance().setTrace<core::extension::Extension>();
+    minifi::core::extension::test_setAgentApiVersion(10);
+    minifi::core::extension::test_setMinSupportedApiVersion(5);
+    auto config = minifi::Configure::create();
+    config->set(minifi::Configuration::nifi_extension_path, pattern);
+    return config;
+  }()} {}
+
+  core::extension::ExtensionManager extension_manager_;
+};
+
+TEST_CASE("Can load cpp-api extensions with same build id") {
+  ExtensionLoadingTestController 
controller{"*test-extension-loading-valid-build-id-cpp*"};
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded cpp 
extension 'test-extension-loading-valid-build-id-cpp'"));
+}
+
+TEST_CASE("Can load c-api extensions with max version") {
+  ExtensionLoadingTestController 
controller{"*test-extension-loading-max-version*"};
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c 
extension 'test-extension-loading-max-version'"));
+}
+
+TEST_CASE("Can't load cpp-api extensions with different build id") {
+  ExtensionLoadingTestController 
controller{"*test-extension-loading-invalid-build-id-cpp*"};
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to 
load extension 'test-extension-loading-invalid-build-id-cpp'"));
+}
+
+TEST_CASE("Can load c-api extensions with min version") {
+  ExtensionLoadingTestController 
controller{"*test-extension-loading-min-version*"};
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c 
extension 'test-extension-loading-min-version'"));
+}
+
+TEST_CASE("Can load c-api extensions with valid version") {
+  ExtensionLoadingTestController 
controller{"*test-extension-loading-valid-version*"};
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c 
extension 'test-extension-loading-valid-version'"));
+}
+
+TEST_CASE("Can't load c-api extensions with greater version") {
+  ExtensionLoadingTestController 
controller{"*test-extension-loading-greater-version*"};
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to 
load c extension 'test-extension-loading-greater-version'"));
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Extension 
is built for a newer version"));
+}
+
+TEST_CASE("Can't load c-api extensions with smaller version") {
+  ExtensionLoadingTestController 
controller{"*test-extension-loading-smaller-version*"};
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to 
load c extension 'test-extension-loading-smaller-version'"));
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Api 
version is no longer supported, application supports 5-10 while extension is 
4"));
+}
+
+TEST_CASE("Can't load c-api extensions with no MinifiInitExtension function") {
+  ExtensionLoadingTestController 
controller{"*test-extension-loading-missing-init*"};
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to 
load as c extension 'test-extension-loading-missing-init'"));
+}
+
+TEST_CASE("Can't load c-api extensions with no MinifiCreateExtension call") {
+  ExtensionLoadingTestController 
controller{"*test-extension-loading-create-not-called*"};
+  REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to 
initialize extension 'test-extension-loading-create-not-called'"));
+}
+
diff --git a/extension-framework/include/utils/ExtensionInitUtils.h 
b/libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp
similarity index 64%
copy from extension-framework/include/utils/ExtensionInitUtils.h
copy to 
libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp
index 1af7617e5..55b2539b7 100644
--- a/extension-framework/include/utils/ExtensionInitUtils.h
+++ 
b/libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp
@@ -1,4 +1,5 @@
 /**
+*
  * 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.
@@ -14,20 +15,3 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#pragma once
-
-#include <string>
-#include <string_view>
-#include <optional>
-#include <functional>
-#include "minifi-c/minifi-c.h"
-
-namespace org::apache::nifi::minifi::utils {
-
-inline MinifiStringView toStringView(std::string_view str) {
-  return MinifiStringView{.data = str.data(), .length = str.length()};
-}
-
-using ConfigReader = std::function<std::optional<std::string>(std::string_view 
key)>;
-
-}  // namespace org::apache::nifi::minifi::utils
diff --git 
a/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def 
b/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def
new file mode 100644
index 000000000..3971f17df
--- /dev/null
+++ b/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def
@@ -0,0 +1,4 @@
+LIBRARY ExtensionVerificationTests.exe
+EXPORTS
+  MinifiCreateCppExtension_NonMatchingBuildId
+  MinifiCreateCppExtension_MatchingBuildId
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..ebdd239c3 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=MinifiCreateCppExtension_${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 ce47d30cd..a171e38fb 100644
--- a/minifi-api/include/minifi-c/minifi-c.h
+++ b/minifi-api/include/minifi-c/minifi-c.h
@@ -31,14 +31,20 @@ extern "C" {
 #define MINIFI_PRIVATE_STRINGIFY_HELPER(X) #X
 #define MINIFI_PRIVATE_STRINGIFY(X) MINIFI_PRIVATE_STRINGIFY_HELPER(X)
 
-#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 "]"
+#define MINIFI_PRIVATE_JOIN_HELPER(X, Y) X ## _ ## Y
+#define MINIFI_PRIVATE_JOIN(X, Y) MINIFI_PRIVATE_JOIN_HELPER(X, Y)
+
 #define MINIFI_NULL nullptr
 #define MINIFI_OWNED
 
+#ifndef MINIFI_CREATE_EXTENSION_FN
+#define MINIFI_CREATE_EXTENSION_FN MinifiCreateExtension
+#endif
+
+enum : uint32_t {
+  MINIFI_API_VERSION = 1
+};
+
 typedef bool MinifiBool;
 
 typedef enum MinifiInputRequirement : uint32_t {
@@ -87,6 +93,7 @@ typedef struct MinifiOutputStream MinifiOutputStream;
 typedef struct MinifiConfig MinifiConfig;
 typedef struct MinifiExtension MinifiExtension;
 typedef struct MinifiPublishedMetrics MinifiPublishedMetrics;
+typedef struct MinifiAgent MinifiAgent;
 
 typedef enum MinifiStatus : uint32_t {
   MINIFI_STATUS_SUCCESS = 0,
@@ -180,10 +187,7 @@ typedef struct MinifiExtensionCreateInfo {
   const MinifiProcessorClassDefinition* processors_ptr;
 } 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*);
+MinifiStatus MINIFI_CREATE_EXTENSION_FN(MinifiExtension* extension, const 
MinifiExtensionCreateInfo* create_info);
 
 MINIFI_OWNED MinifiPublishedMetrics* MinifiPublishedMetricsCreate(size_t 
count, const MinifiStringView* metric_names, const double* metric_values);
 
diff --git a/minifi-api/minifi-c-api.def b/minifi-api/minifi-c-api.def
index 67802f40b..9e1592b3a 100644
--- a/minifi-api/minifi-c-api.def
+++ b/minifi-api/minifi-c-api.def
@@ -20,4 +20,4 @@ EXPORTS
   MinifiOutputStreamWrite
   MinifiFlowFileSetAttribute
   MinifiFlowFileGetAttribute
-  MinifiFlowFileGetAttributes
\ No newline at end of file
+  MinifiFlowFileGetAttributes

Reply via email to