From 3e75dcf03b9e5bd87c0d8963496a1189c909b2d4 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Wed, 11 Feb 2026 14:39:34 +0100 Subject: [PATCH 01/16] MINIFICPP-2715 - Use symbols to check api compatibility --- Extensions.md | 4 +- cmake/Extensions.cmake | 23 +--- extensions/ExtensionInitializer.cpp | 35 ++++++ .../processors/ExtensionInitializer.cpp | 4 +- extensions/opencv/CMakeLists.txt | 1 + extensions/opencv/OpenCVLoader.cpp | 2 +- extensions/python/CMakeLists.txt | 2 + .../pythonlibloader/PythonLibLoader.cpp | 2 +- .../python/pythonloader/PyProcLoader.cpp | 2 +- extensions/sftp/CMakeLists.txt | 1 + extensions/sftp/SFTPLoader.cpp | 2 +- libminifi/include/core/extension/Utils.h | 26 ----- libminifi/src/ApiVersions.cpp | 31 +++++ libminifi/src/core/extension/Extension.cpp | 2 +- .../src/core/extension/ExtensionManager.cpp | 8 -- libminifi/src/core/extension/Utils.cpp | 65 ----------- .../test/unit/ExtensionVerificationTests.cpp | 107 ------------------ minifi-api/CMakeLists.txt | 1 + minifi-api/include/minifi-c/minifi-c.h | 17 ++- 19 files changed, 95 insertions(+), 240 deletions(-) create mode 100644 extensions/ExtensionInitializer.cpp create mode 100644 libminifi/src/ApiVersions.cpp delete mode 100644 libminifi/src/core/extension/Utils.cpp delete mode 100644 libminifi/test/unit/ExtensionVerificationTests.cpp diff --git a/Extensions.md b/Extensions.md index 7d3fd53922..3a82e212fd 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 5786967a8b..6ea93f1cb6 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/ExtensionInitializer.cpp b/extensions/ExtensionInitializer.cpp new file mode 100644 index 0000000000..3caaaeed0f --- /dev/null +++ b/extensions/ExtensionInitializer.cpp @@ -0,0 +1,35 @@ +/** + * + * 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 "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* MinifiInitExtension(MinifiConfig* /*config*/) { + MinifiExtensionCreateInfo ext_create_info{ + .name = minifi::utils::toStringView(MAKESTRING(MODULE_NAME)), + .version = minifi::utils::toStringView(minifi::AgentBuild::VERSION), + .deinit = nullptr, + .user_data = nullptr, + .processors_count = 0, + .processors_ptr = nullptr + }; + return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); +} diff --git a/extensions/llamacpp/processors/ExtensionInitializer.cpp b/extensions/llamacpp/processors/ExtensionInitializer.cpp index 40508955cd..b90b8b133a 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([&] (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 ed812c1a60..3c80e3efdc 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 79a437f757..3f91395c42 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 ac3f5fe577..34b1df6730 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 398322f8fb..cdf0356b70 100644 --- a/extensions/python/pythonlibloader/PythonLibLoader.cpp +++ b/extensions/python/pythonlibloader/PythonLibLoader.cpp @@ -98,7 +98,7 @@ class PythonLibLoader { std::shared_ptr logger_ = minifi::core::logging::LoggerFactory::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::optional 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 3778f15ec0..55ad4ebefa 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::optional 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 c5c1cb6a7d..7200b41ba6 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 8a67842de1..bb23e9998f 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 520827193f..c983a25f90 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& logger) const; - - [[nodiscard]] - LibraryType verify(const std::shared_ptr& 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/ApiVersions.cpp b/libminifi/src/ApiVersions.cpp new file mode 100644 index 0000000000..5bff5c020e --- /dev/null +++ b/libminifi/src/ApiVersions.cpp @@ -0,0 +1,31 @@ +/** +* 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 "minifi-c/minifi-c.h" +#include "minifi-cpp/utils/Export.h" + +extern "C" { + +MINIFIAPI extern const char* const MINIFI_API_VERSION = MINIFI_PRIVATE_STRINGIFY(MINIFI_API_VERSION); + +#define REGISTER_C_API_VERSION(major, minor) \ + MINIFIAPI extern const char* const MINIFI_PRIVATE_JOIN(MINIFI_C_API_VERSION, MINIFI_PRIVATE_JOIN(major, minor)) = \ + MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_MAJOR_VERSION) "." MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_MINOR_VERSION) "." MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_PATCH_VERSION); + +REGISTER_C_API_VERSION(0, 1) + +} // extern "C" diff --git a/libminifi/src/core/extension/Extension.cpp b/libminifi/src/core/extension/Extension.cpp index 8b7b7f7407..3034c8a8fc 100644 --- a/libminifi/src/core/extension/Extension.cpp +++ b/libminifi/src/core/extension/Extension.cpp @@ -113,7 +113,7 @@ Extension::~Extension() { bool Extension::initialize(const std::shared_ptr& 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(init_symbol_ptr); auto config_handle = reinterpret_cast(configure.get()); diff --git a/libminifi/src/core/extension/ExtensionManager.cpp b/libminifi/src/core/extension/ExtensionManager.cpp index b0ce8882f5..0b144a251f 100644 --- a/libminifi/src/core/extension/ExtensionManager.cpp +++ b/libminifi/src/core/extension/ExtensionManager.cpp @@ -58,14 +58,6 @@ ExtensionManager::ExtensionManager(const std::shared_ptr& 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(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 38e90ac159..0000000000 --- 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& 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/test/unit/ExtensionVerificationTests.cpp b/libminifi/test/unit/ExtensionVerificationTests.cpp deleted file mode 100644 index 71a425adaf..0000000000 --- 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 -#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 logger{core::logging::LoggerFactory::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 aa684c154f..4fa9e4bc3a 100644 --- a/minifi-api/CMakeLists.txt +++ b/minifi-api/CMakeLists.txt @@ -5,6 +5,7 @@ target_link_libraries(minifi-api-common INTERFACE gsl-lite) 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_API_VERSION=MINIFI_CPP_API_VERSION_${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 5be2bbb2f1..afccd00c87 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_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_C_API_MAJOR_VERSION 0 +#define MINIFI_C_API_MINOR_VERSION 1 +#define MINIFI_C_API_PATCH_VERSION 0 + +#ifndef MINIFI_API_VERSION +#define MINIFI_API_VERSION MINIFI_PRIVATE_JOIN(MINIFI_C_API_VERSION, MINIFI_PRIVATE_JOIN(MINIFI_C_API_MAJOR_VERSION, MINIFI_C_API_MINOR_VERSION)) +#endif +extern const char* const MINIFI_API_VERSION; + #define MINIFI_NULL nullptr #define MINIFI_OWNED From 34ac5ad4224093e788057d8b9077511b021eb0dc Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Wed, 11 Feb 2026 14:41:38 +0100 Subject: [PATCH 02/16] MINIFICPP-2715 - Add missing symbol --- minifi-api/minifi-c-api.def | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/minifi-api/minifi-c-api.def b/minifi-api/minifi-c-api.def index ee225e7c11..ee763264da 100644 --- a/minifi-api/minifi-c-api.def +++ b/minifi-api/minifi-c-api.def @@ -21,4 +21,5 @@ EXPORTS MinifiStatusToString MinifiFlowFileSetAttribute MinifiFlowFileGetAttribute - MinifiFlowFileGetAttributes \ No newline at end of file + MinifiFlowFileGetAttributes + MINIFI_C_API_VERSION_0_1 From a0ddf3190e4bd0694c2a850ae57465c4e8db0c3b Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Thu, 12 Feb 2026 11:17:30 +0100 Subject: [PATCH 03/16] MINIFICPP-2715 - Windows fix --- extensions/ExtensionInitializer.cpp | 2 +- .../llamacpp/processors/ExtensionInitializer.cpp | 2 +- extensions/opencv/OpenCVLoader.cpp | 2 +- extensions/python/pythonlibloader/PythonLibLoader.cpp | 2 +- extensions/python/pythonloader/PyProcLoader.cpp | 2 +- extensions/sftp/SFTPLoader.cpp | 2 +- libminifi/src/ApiVersions.cpp | 11 +++++++---- libminifi/src/minifi-c.cpp | 2 +- minifi-api/CMakeLists.txt | 2 +- minifi-api/include/minifi-c/minifi-c.h | 11 +++++++---- minifi-api/minifi-c-api.def | 2 +- 11 files changed, 23 insertions(+), 17 deletions(-) diff --git a/extensions/ExtensionInitializer.cpp b/extensions/ExtensionInitializer.cpp index 3caaaeed0f..4044e15c2c 100644 --- a/extensions/ExtensionInitializer.cpp +++ b/extensions/ExtensionInitializer.cpp @@ -31,5 +31,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); } diff --git a/extensions/llamacpp/processors/ExtensionInitializer.cpp b/extensions/llamacpp/processors/ExtensionInitializer.cpp index b90b8b133a..18f5ff58e7 100644 --- a/extensions/llamacpp/processors/ExtensionInitializer.cpp +++ b/extensions/llamacpp/processors/ExtensionInitializer.cpp @@ -35,7 +35,7 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 1, .processors_ptr = &description, }; - extension = MinifiCreateExtension(minifi::api::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + extension = MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); }); return extension; } diff --git a/extensions/opencv/OpenCVLoader.cpp b/extensions/opencv/OpenCVLoader.cpp index 3f91395c42..83e75bdbd7 100644 --- a/extensions/opencv/OpenCVLoader.cpp +++ b/extensions/opencv/OpenCVLoader.cpp @@ -42,5 +42,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); } diff --git a/extensions/python/pythonlibloader/PythonLibLoader.cpp b/extensions/python/pythonlibloader/PythonLibLoader.cpp index cdf0356b70..522c436339 100644 --- a/extensions/python/pythonlibloader/PythonLibLoader.cpp +++ b/extensions/python/pythonlibloader/PythonLibLoader.cpp @@ -114,5 +114,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* config) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); } diff --git a/extensions/python/pythonloader/PyProcLoader.cpp b/extensions/python/pythonloader/PyProcLoader.cpp index 55ad4ebefa..f283847ff0 100644 --- a/extensions/python/pythonloader/PyProcLoader.cpp +++ b/extensions/python/pythonloader/PyProcLoader.cpp @@ -49,5 +49,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* config) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); } diff --git a/extensions/sftp/SFTPLoader.cpp b/extensions/sftp/SFTPLoader.cpp index bb23e9998f..3cf12aeddb 100644 --- a/extensions/sftp/SFTPLoader.cpp +++ b/extensions/sftp/SFTPLoader.cpp @@ -44,5 +44,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); } diff --git a/libminifi/src/ApiVersions.cpp b/libminifi/src/ApiVersions.cpp index 5bff5c020e..76364c0f02 100644 --- a/libminifi/src/ApiVersions.cpp +++ b/libminifi/src/ApiVersions.cpp @@ -20,11 +20,14 @@ extern "C" { -MINIFIAPI extern const char* const MINIFI_API_VERSION = MINIFI_PRIVATE_STRINGIFY(MINIFI_API_VERSION); +const MinifiApiVersion* MINIFI_API_VERSION_FN() { + return reinterpret_cast(MINIFI_PRIVATE_STRINGIFY(MINIFI_API_VERSION_FN)); +} -#define REGISTER_C_API_VERSION(major, minor) \ - MINIFIAPI extern const char* const MINIFI_PRIVATE_JOIN(MINIFI_C_API_VERSION, MINIFI_PRIVATE_JOIN(major, minor)) = \ - MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_MAJOR_VERSION) "." MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_MINOR_VERSION) "." MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_PATCH_VERSION); +#define REGISTER_C_API_VERSION(major, minor) \ + const MinifiApiVersion* MINIFI_PRIVATE_JOIN(MinifiCApiVersion, MINIFI_PRIVATE_JOIN(major, minor))() { \ + return reinterpret_cast(MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_MAJOR_VERSION) "." MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_MINOR_VERSION) "." MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_PATCH_VERSION)); \ + } REGISTER_C_API_VERSION(0, 1) diff --git a/libminifi/src/minifi-c.cpp b/libminifi/src/minifi-c.cpp index ebceb9f02a..2abff81d03 100644 --- a/libminifi/src/minifi-c.cpp +++ b/libminifi/src/minifi-c.cpp @@ -225,7 +225,7 @@ void useCProcessorClassDescription(const MinifiProcessorClassDefinition& class_d extern "C" { -MinifiExtension* MinifiCreateExtension(MinifiStringView /*api_version*/, const MinifiExtensionCreateInfo* extension_create_info) { +MinifiExtension* MinifiCreateExtension(const MinifiApiVersion* /*api_version*/, const MinifiExtensionCreateInfo* extension_create_info) { gsl_Assert(extension_create_info); auto extension_name = toString(extension_create_info->name); minifi::BundleIdentifier bundle{ diff --git a/minifi-api/CMakeLists.txt b/minifi-api/CMakeLists.txt index 4fa9e4bc3a..b9f3906c48 100644 --- a/minifi-api/CMakeLists.txt +++ b/minifi-api/CMakeLists.txt @@ -5,7 +5,7 @@ target_link_libraries(minifi-api-common INTERFACE gsl-lite) 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_API_VERSION=MINIFI_CPP_API_VERSION_${BUILD_IDENTIFIER}) +target_compile_definitions(minifi-api INTERFACE MINIFI_API_VERSION_FN=MinifiCppApiVersion_${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 afccd00c87..d1f45ea281 100644 --- a/minifi-api/include/minifi-c/minifi-c.h +++ b/minifi-api/include/minifi-c/minifi-c.h @@ -38,10 +38,10 @@ extern "C" { #define MINIFI_C_API_MINOR_VERSION 1 #define MINIFI_C_API_PATCH_VERSION 0 -#ifndef MINIFI_API_VERSION -#define MINIFI_API_VERSION MINIFI_PRIVATE_JOIN(MINIFI_C_API_VERSION, MINIFI_PRIVATE_JOIN(MINIFI_C_API_MAJOR_VERSION, MINIFI_C_API_MINOR_VERSION)) +#ifndef MINIFI_API_VERSION_FN +#define MINIFI_API_VERSION_FN MINIFI_PRIVATE_JOIN(MinifiCApiVersion, MINIFI_PRIVATE_JOIN(MINIFI_C_API_MAJOR_VERSION, MINIFI_C_API_MINOR_VERSION)) #endif -extern const char* const MINIFI_API_VERSION; +#define MINIFI_API_VERSION MINIFI_API_VERSION_FN() #define MINIFI_NULL nullptr #define MINIFI_OWNED @@ -94,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 : uint32_t { MINIFI_STATUS_SUCCESS = 0, @@ -188,10 +189,12 @@ typedef struct MinifiExtensionCreateInfo { const MinifiProcessorClassDefinition* processors_ptr; } MinifiExtensionCreateInfo; +const MinifiApiVersion* MINIFI_API_VERSION_FN(); + // 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* MinifiCreateExtension(const MinifiApiVersion* api_version, const MinifiExtensionCreateInfo*); 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 ee763264da..1e9f66ecd3 100644 --- a/minifi-api/minifi-c-api.def +++ b/minifi-api/minifi-c-api.def @@ -22,4 +22,4 @@ EXPORTS MinifiFlowFileSetAttribute MinifiFlowFileGetAttribute MinifiFlowFileGetAttributes - MINIFI_C_API_VERSION_0_1 + MinifiCApiVersion_0_1 From b3fb6556510a3fee3bae56711f1a601476ec0f98 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Thu, 12 Feb 2026 14:45:15 +0100 Subject: [PATCH 04/16] MINIFICPP-2715 - Move version information to MinifiCreateExtension --- extensions/ExtensionInitializer.cpp | 2 +- .../processors/ExtensionInitializer.cpp | 2 +- extensions/opencv/OpenCVLoader.cpp | 2 +- .../pythonlibloader/PythonLibLoader.cpp | 2 +- .../python/pythonloader/PyProcLoader.cpp | 2 +- extensions/sftp/SFTPLoader.cpp | 2 +- libminifi/src/ApiVersions.cpp | 34 ------------ libminifi/src/minifi-c.cpp | 55 +++++++++++-------- minifi-api/CMakeLists.txt | 2 +- minifi-api/include/minifi-c/minifi-c.h | 20 ++++--- minifi-api/minifi-c-api.def | 3 +- 11 files changed, 52 insertions(+), 74 deletions(-) delete mode 100644 libminifi/src/ApiVersions.cpp diff --git a/extensions/ExtensionInitializer.cpp b/extensions/ExtensionInitializer.cpp index 4044e15c2c..3caaaeed0f 100644 --- a/extensions/ExtensionInitializer.cpp +++ b/extensions/ExtensionInitializer.cpp @@ -31,5 +31,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); + return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); } diff --git a/extensions/llamacpp/processors/ExtensionInitializer.cpp b/extensions/llamacpp/processors/ExtensionInitializer.cpp index 18f5ff58e7..b90b8b133a 100644 --- a/extensions/llamacpp/processors/ExtensionInitializer.cpp +++ b/extensions/llamacpp/processors/ExtensionInitializer.cpp @@ -35,7 +35,7 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 1, .processors_ptr = &description, }; - extension = MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); + extension = MinifiCreateExtension(minifi::api::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); }); return extension; } diff --git a/extensions/opencv/OpenCVLoader.cpp b/extensions/opencv/OpenCVLoader.cpp index 83e75bdbd7..3f91395c42 100644 --- a/extensions/opencv/OpenCVLoader.cpp +++ b/extensions/opencv/OpenCVLoader.cpp @@ -42,5 +42,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); + return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); } diff --git a/extensions/python/pythonlibloader/PythonLibLoader.cpp b/extensions/python/pythonlibloader/PythonLibLoader.cpp index 522c436339..cdf0356b70 100644 --- a/extensions/python/pythonlibloader/PythonLibLoader.cpp +++ b/extensions/python/pythonlibloader/PythonLibLoader.cpp @@ -114,5 +114,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* config) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); + return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); } diff --git a/extensions/python/pythonloader/PyProcLoader.cpp b/extensions/python/pythonloader/PyProcLoader.cpp index f283847ff0..55ad4ebefa 100644 --- a/extensions/python/pythonloader/PyProcLoader.cpp +++ b/extensions/python/pythonloader/PyProcLoader.cpp @@ -49,5 +49,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* config) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); + return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); } diff --git a/extensions/sftp/SFTPLoader.cpp b/extensions/sftp/SFTPLoader.cpp index 3cf12aeddb..bb23e9998f 100644 --- a/extensions/sftp/SFTPLoader.cpp +++ b/extensions/sftp/SFTPLoader.cpp @@ -44,5 +44,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(MINIFI_API_VERSION, &ext_create_info); + return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); } diff --git a/libminifi/src/ApiVersions.cpp b/libminifi/src/ApiVersions.cpp deleted file mode 100644 index 76364c0f02..0000000000 --- a/libminifi/src/ApiVersions.cpp +++ /dev/null @@ -1,34 +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 "minifi-c/minifi-c.h" -#include "minifi-cpp/utils/Export.h" - -extern "C" { - -const MinifiApiVersion* MINIFI_API_VERSION_FN() { - return reinterpret_cast(MINIFI_PRIVATE_STRINGIFY(MINIFI_API_VERSION_FN)); -} - -#define REGISTER_C_API_VERSION(major, minor) \ - const MinifiApiVersion* MINIFI_PRIVATE_JOIN(MinifiCApiVersion, MINIFI_PRIVATE_JOIN(major, minor))() { \ - return reinterpret_cast(MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_MAJOR_VERSION) "." MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_MINOR_VERSION) "." MINIFI_PRIVATE_STRINGIFY(MINIFI_C_API_PATCH_VERSION)); \ - } - -REGISTER_C_API_VERSION(0, 1) - -} // extern "C" diff --git a/libminifi/src/minifi-c.cpp b/libminifi/src/minifi-c.cpp index 2abff81d03..0e4144c334 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(extension_name, toString(extension_create_info->processors_ptr[proc_idx].full_name), c_class_description)); + bundle_components.processors.emplace_back(description); + }); + } + return reinterpret_cast(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(const MinifiApiVersion* /*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(extension_name, toString(extension_create_info->processors_ptr[proc_idx].full_name), c_class_description)); - bundle_components.processors.emplace_back(description); - }); - } - return reinterpret_cast(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/minifi-api/CMakeLists.txt b/minifi-api/CMakeLists.txt index b9f3906c48..9e40c11cc4 100644 --- a/minifi-api/CMakeLists.txt +++ b/minifi-api/CMakeLists.txt @@ -5,7 +5,7 @@ target_link_libraries(minifi-api-common INTERFACE gsl-lite) 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_API_VERSION_FN=MinifiCppApiVersion_${BUILD_IDENTIFIER}) +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 d1f45ea281..d246b773b2 100644 --- a/minifi-api/include/minifi-c/minifi-c.h +++ b/minifi-api/include/minifi-c/minifi-c.h @@ -34,14 +34,14 @@ extern "C" { #define MINIFI_PRIVATE_JOIN_HELPER(X, Y) X ## _ ## Y #define MINIFI_PRIVATE_JOIN(X, Y) MINIFI_PRIVATE_JOIN_HELPER(X, Y) -#define MINIFI_C_API_MAJOR_VERSION 0 -#define MINIFI_C_API_MINOR_VERSION 1 -#define MINIFI_C_API_PATCH_VERSION 0 +#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) -#ifndef MINIFI_API_VERSION_FN -#define MINIFI_API_VERSION_FN MINIFI_PRIVATE_JOIN(MinifiCApiVersion, MINIFI_PRIVATE_JOIN(MINIFI_C_API_MAJOR_VERSION, MINIFI_C_API_MINOR_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_API_VERSION MINIFI_API_VERSION_FN() #define MINIFI_NULL nullptr #define MINIFI_OWNED @@ -189,12 +189,10 @@ typedef struct MinifiExtensionCreateInfo { const MinifiProcessorClassDefinition* processors_ptr; } MinifiExtensionCreateInfo; -const MinifiApiVersion* MINIFI_API_VERSION_FN(); - // 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(const MinifiApiVersion* 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); @@ -234,4 +232,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 1e9f66ecd3..a664bdd2db 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 MinifiProcessContextHasNonEmptyProperty @@ -22,4 +22,3 @@ EXPORTS MinifiFlowFileSetAttribute MinifiFlowFileGetAttribute MinifiFlowFileGetAttributes - MinifiCApiVersion_0_1 From c0fcc0c1be603725a7dd6781a9b24463219ea486 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Thu, 19 Feb 2026 13:52:50 +0100 Subject: [PATCH 05/16] MINIFICPP-2715 - Use symbol lookup to check version --- Extensions.md | 43 ++++++++++-- .../include/utils/ExtensionInitUtils.h | 4 ++ extensions/ExtensionInitializer.cpp | 4 +- .../processors/ExtensionInitializer.cpp | 4 +- extensions/opencv/OpenCVLoader.cpp | 4 +- .../pythonlibloader/PythonLibLoader.cpp | 4 +- .../python/pythonloader/PyProcLoader.cpp | 4 +- extensions/sftp/SFTPLoader.cpp | 4 +- libminifi/include/core/extension/Extension.h | 14 +++- .../include/core/extension/ExtensionManager.h | 2 + libminifi/src/core/extension/Extension.cpp | 64 ++++++++++++++---- .../src/core/extension/ExtensionManager.cpp | 22 +++++-- libminifi/src/minifi-c.cpp | 65 +++++++++---------- minifi-api/CMakeLists.txt | 2 +- minifi-api/include/minifi-c/minifi-c.h | 19 ++---- minifi-api/minifi-c-api.def | 2 +- 16 files changed, 176 insertions(+), 85 deletions(-) diff --git a/Extensions.md b/Extensions.md index 3a82e212fd..0bfbd5bd98 100644 --- a/Extensions.md +++ b/Extensions.md @@ -17,8 +17,41 @@ 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" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { + MinifiExtension* extension = nullptr; + minifi::api::core::useProcessorClassDescription([&] (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, + }; + extension = MinifiCreateExtension(&ext_create_info); + }); + return extension; +} +``` + +## 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 +66,10 @@ REGISTER_RESOURCE(RESTSender, DescriptionOnly); ``` Some extensions (e.g. `OpenCVExtension`) require initialization before use. -You need to define an `MinifiInitExtension` 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* MinifiInitExtension(MinifiConfig* /*config*/) { +extern "C" MinifiExtension* MinifiInitCppExtension(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 +82,7 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return MinifiCreateCppExtension(&ext_create_info); } ``` diff --git a/extension-framework/include/utils/ExtensionInitUtils.h b/extension-framework/include/utils/ExtensionInitUtils.h index 1af7617e5a..57f1d063f2 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::string_view key)>; +static inline MinifiExtension* MinifiCreateCppExtension(const MinifiExtensionCreateInfo* create_info) { + return MINIFI_CREATE_EXTENSION_FN(create_info); +} + } // namespace org::apache::nifi::minifi::utils diff --git a/extensions/ExtensionInitializer.cpp b/extensions/ExtensionInitializer.cpp index 3caaaeed0f..e735a78e77 100644 --- a/extensions/ExtensionInitializer.cpp +++ b/extensions/ExtensionInitializer.cpp @@ -22,7 +22,7 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { +extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* /*config*/) { MinifiExtensionCreateInfo ext_create_info{ .name = minifi::utils::toStringView(MAKESTRING(MODULE_NAME)), .version = minifi::utils::toStringView(minifi::AgentBuild::VERSION), @@ -31,5 +31,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return minifi::utils::MinifiCreateCppExtension(&ext_create_info); } diff --git a/extensions/llamacpp/processors/ExtensionInitializer.cpp b/extensions/llamacpp/processors/ExtensionInitializer.cpp index b90b8b133a..29316f8b34 100644 --- a/extensions/llamacpp/processors/ExtensionInitializer.cpp +++ b/extensions/llamacpp/processors/ExtensionInitializer.cpp @@ -24,6 +24,8 @@ namespace minifi = org::apache::nifi::minifi; +extern "C" const char* const MinifiApiVersion = MINIFI_API_VERSION; + extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { MinifiExtension* extension = nullptr; minifi::api::core::useProcessorClassDescription([&] (const MinifiProcessorClassDefinition& description) { @@ -35,7 +37,7 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 1, .processors_ptr = &description, }; - extension = MinifiCreateExtension(minifi::api::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + extension = MinifiCreateExtension(&ext_create_info); }); return extension; } diff --git a/extensions/opencv/OpenCVLoader.cpp b/extensions/opencv/OpenCVLoader.cpp index 3f91395c42..53f61dbcaf 100644 --- a/extensions/opencv/OpenCVLoader.cpp +++ b/extensions/opencv/OpenCVLoader.cpp @@ -24,7 +24,7 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { +extern "C" MinifiExtension* MinifiInitCppExtension(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: @@ -42,5 +42,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return minifi::utils::MinifiCreateCppExtension(&ext_create_info); } diff --git a/extensions/python/pythonlibloader/PythonLibLoader.cpp b/extensions/python/pythonlibloader/PythonLibLoader.cpp index cdf0356b70..9c9a35d284 100644 --- a/extensions/python/pythonlibloader/PythonLibLoader.cpp +++ b/extensions/python/pythonlibloader/PythonLibLoader.cpp @@ -98,7 +98,7 @@ class PythonLibLoader { std::shared_ptr logger_ = minifi::core::logging::LoggerFactory::getLogger(); }; -extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* config) { +extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* config) { static PythonLibLoader python_lib_loader([&] (std::string_view key) -> std::optional { std::optional result; MinifiConfigGet(config, minifi::utils::toStringView(key), [] (void* user_data, MinifiStringView value) { @@ -114,5 +114,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* config) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return minifi::utils::MinifiCreateCppExtension(&ext_create_info); } diff --git a/extensions/python/pythonloader/PyProcLoader.cpp b/extensions/python/pythonloader/PyProcLoader.cpp index 55ad4ebefa..cd9cbb9986 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* MinifiInitExtension(MinifiConfig* config) { +extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* config) { getPythonCreator().configure([&] (std::string_view key) -> std::optional { std::optional result; MinifiConfigGet(config, minifi::utils::toStringView(key), [] (void* user_data, MinifiStringView value) { @@ -49,5 +49,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* config) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return minifi::utils::MinifiCreateCppExtension( &ext_create_info); } diff --git a/extensions/sftp/SFTPLoader.cpp b/extensions/sftp/SFTPLoader.cpp index bb23e9998f..333ab173e5 100644 --- a/extensions/sftp/SFTPLoader.cpp +++ b/extensions/sftp/SFTPLoader.cpp @@ -25,7 +25,7 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { +extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* /*config*/) { if (libssh2_init(0) != 0) { return nullptr; } @@ -44,5 +44,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateExtension(minifi::utils::toStringView(MINIFI_API_VERSION), &ext_create_info); + return minifi::utils::MinifiCreateCppExtension(&ext_create_info); } diff --git a/libminifi/include/core/extension/Extension.h b/libminifi/include/core/extension/Extension.h index e14cb97ed0..2a71a6fbc2 100644 --- a/libminifi/include/core/extension/Extension.h +++ b/libminifi/include/core/extension/Extension.h @@ -17,11 +17,12 @@ #pragma once -#include +#include #include +#include #include -#include +#include "minifi-c/minifi-c.h" #include "minifi-cpp/core/logging/Logger.h" #include "minifi-cpp/properties/Configure.h" @@ -38,6 +39,12 @@ class Extension { void* user_data; }; + struct Version { + int major{MINIFI_API_MAJOR_VERSION}; + int minor{MINIFI_API_MINOR_VERSION}; + int patch{MINIFI_API_PATCH_VERSION}; + }; + Extension(std::string name, std::filesystem::path library_path); Extension(const Extension&) = delete; @@ -49,6 +56,8 @@ class Extension { bool initialize(const std::shared_ptr& configure); + Version version() const {return version_;} + private: #ifdef WIN32 std::map resource_mapping_; @@ -71,6 +80,7 @@ class Extension { std::filesystem::path library_path_; gsl::owner handle_ = nullptr; + Version version_{}; std::unique_ptr info_; const std::shared_ptr logger_; diff --git a/libminifi/include/core/extension/ExtensionManager.h b/libminifi/include/core/extension/ExtensionManager.h index c610bd70a0..7b312ae4bf 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> extensions_; diff --git a/libminifi/src/core/extension/Extension.cpp b/libminifi/src/core/extension/Extension.cpp index 3034c8a8fc..35d0634b9e 100644 --- a/libminifi/src/core/extension/Extension.cpp +++ b/libminifi/src/core/extension/Extension.cpp @@ -38,6 +38,7 @@ #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 { @@ -57,10 +58,43 @@ bool Extension::load(bool global) { if (!handle_) { logger_->log_error("Failed to load extension '{}' at '{}': {}", name_, library_path_, dlerror()); return false; - } else { - logger_->log_trace("Loaded extension '{}' at '{}'", name_, library_path_); + } + logger_->log_trace("Dlopen succeeded for extension '{}' at '{}'", name_, library_path_); + if (findSymbol("MinifiInitCppExtension")) { + logger_->log_trace("Loaded cpp extension '{}' at '{}'", name_, library_path_); return true; } + if (!findSymbol("MinifiInitExtension")) { + logger_->log_error("Failed to load c extension '{}' at '{}': No initializer found", name_, library_path_); + return false; + } + auto api_version = reinterpret_cast(findSymbol("MinifiApiVersion")); + if (!api_version) { + logger_->log_error("Failed to load c extension '{}' at '{}': No MinifiApiVersion symbol found", name_, library_path_); + return false; + } + utils::SVMatch match; + if (!utils::regexSearch(*api_version, match, utils::Regex{R"(^([0-9]+)\.([0-9]+)\.([0-9]+)$)"})) { + logger_->log_error("Failed to load c extension '{}' at '{}': Api version is in invalid format: '{}'", name_, library_path_, *api_version); + return false; + } + gsl_Assert(match.size() == 4); + version_.major = std::stoi(match[1]); + version_.minor = std::stoi(match[2]); + version_.patch = std::stoi(match[3]); + if (version_.major != MINIFI_API_MAJOR_VERSION) { + logger_->log_error("Failed to load c extension '{}' at '{}': Api major version mismatch, application is '{}' while extension is '{}'", + name_, library_path_, MINIFI_API_VERSION, *api_version); + return false; + } + if (version_.minor > MINIFI_API_MINOR_VERSION) { + logger_->log_error("Failed to load c extension '{}' at '{}': Extension is built for a newer version, application is '{}' while extension is '{}'", + name_, library_path_, MINIFI_API_VERSION, *api_version); + return false; + } + logger_->log_debug("Loaded c extension '{}' at '{}': Application version is '{}', extension version is '{}'", + name_, library_path_, MINIFI_API_VERSION, *api_version); + return true; } bool Extension::unload() { @@ -113,17 +147,21 @@ Extension::~Extension() { bool Extension::initialize(const std::shared_ptr& configure) { logger_->log_trace("Initializing extension '{}'", name_); - if (void* init_symbol_ptr = findSymbol("MinifiInitExtension")) { - logger_->log_debug("Found custom initializer for '{}'", name_); - auto init_fn = reinterpret_cast(init_symbol_ptr); - auto config_handle = reinterpret_cast(configure.get()); - info_.reset(reinterpret_cast(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_); + 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 '{}'", name_); + return false; + } + logger_->log_debug("Found initializer for '{}'", name_); + auto init_fn = reinterpret_cast(init_symbol_ptr); + auto config_handle = reinterpret_cast(configure.get()); + info_.reset(reinterpret_cast(init_fn(config_handle))); + if (!info_) { + logger_->log_error("Failed to initialize extension '{}'", name_); + return false; } return true; } diff --git a/libminifi/src/core/extension/ExtensionManager.cpp b/libminifi/src/core/extension/ExtensionManager.cpp index 0b144a251f..689a6cd422 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_being_initialized{nullptr}; + +} // namespace + +Extension* ExtensionManager::getExtensionBeingInitialized() { + return extension_being_initialized.load(); +} + ExtensionManager::ExtensionManager(const std::shared_ptr& config): logger_(logging::LoggerFactory::getLogger()) { logger_->log_trace("Initializing extensions"); if (!config) { @@ -71,10 +81,14 @@ ExtensionManager::ExtensionManager(const std::shared_ptr& 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/minifi-c.cpp b/libminifi/src/minifi-c.cpp index 0e4144c334..e670c44153 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; @@ -146,30 +146,6 @@ 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(extension_name, toString(extension_create_info->processors_ptr[proc_idx].full_name), c_class_description)); - bundle_components.processors.emplace_back(description); - }); - } - return reinterpret_cast(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 { @@ -249,16 +225,35 @@ void useCProcessorClassDescription(const MinifiProcessorClassDefinition& class_d extern "C" { -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); \ +MinifiExtension* MinifiCreateExtension(const MinifiExtensionCreateInfo* extension_create_info) { + gsl_Assert(extension_create_info); + auto current_extension = minifi::core::extension::ExtensionManager::getExtensionBeingInitialized(); + gsl_Assert(current_extension); + 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(extension_name, toString(extension_create_info->processors_ptr[proc_idx].full_name), c_class_description)); + bundle_components.processors.emplace_back(description); + }); } + return reinterpret_cast(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 + }); +} -REGISTER_C_API_VERSION(0, 1) +MinifiExtension* MINIFI_CREATE_EXTENSION_FN(const MinifiExtensionCreateInfo* extension_create_info) { + return MinifiCreateExtension(extension_create_info); +} MinifiStatus MinifiProcessContextGetProperty(MinifiProcessContext* context, MinifiStringView property_name, MinifiFlowFile* flowfile, void (*result_cb)(void* user_ctx, MinifiStringView result), void* user_ctx) { diff --git a/minifi-api/CMakeLists.txt b/minifi-api/CMakeLists.txt index 9e40c11cc4..e82e44f76b 100644 --- a/minifi-api/CMakeLists.txt +++ b/minifi-api/CMakeLists.txt @@ -5,7 +5,7 @@ target_link_libraries(minifi-api-common INTERFACE gsl-lite) 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}) +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 d246b773b2..5bab749412 100644 --- a/minifi-api/include/minifi-c/minifi-c.h +++ b/minifi-api/include/minifi-c/minifi-c.h @@ -39,13 +39,13 @@ extern "C" { #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) -#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 +#ifndef MINIFI_CREATE_EXTENSION_FN +#define MINIFI_CREATE_EXTENSION_FN MinifiCreateExtension +#endif + typedef bool MinifiBool; typedef enum MinifiInputRequirement : uint32_t { @@ -94,7 +94,7 @@ typedef struct MinifiOutputStream MinifiOutputStream; typedef struct MinifiConfig MinifiConfig; typedef struct MinifiExtension MinifiExtension; typedef struct MinifiPublishedMetrics MinifiPublishedMetrics; -typedef struct MinifiApiVersion MinifiApiVersion; +typedef struct MinifiAgent MinifiAgent; typedef enum MinifiStatus : uint32_t { MINIFI_STATUS_SUCCESS = 0, @@ -189,10 +189,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* MINIFI_CREATE_EXTENSION_FN(MinifiStringView api_version, const MinifiExtensionCreateInfo* create_info); +MinifiExtension* MINIFI_CREATE_EXTENSION_FN(const MinifiExtensionCreateInfo* create_info); MINIFI_OWNED MinifiPublishedMetrics* MinifiPublishedMetricsCreate(size_t count, const MinifiStringView* metric_names, const double* metric_values); @@ -232,8 +229,4 @@ 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 a664bdd2db..fd303daeee 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_0_1 + MinifiCreateExtension MinifiPublishedMetricsCreate MinifiProcessContextGetProperty MinifiProcessContextHasNonEmptyProperty From 8e1f526dfdea6f8d7e348c4d33a9ea244b264e71 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Mon, 2 Mar 2026 14:03:04 +0100 Subject: [PATCH 06/16] MINIFICPP-2715 - Add extension loading tests for version verification --- Extensions.md | 2 +- cmake/Extensions.cmake | 3 +- .../python/pythonloader/PyProcLoader.cpp | 2 +- libminifi/include/core/extension/ApiVersion.h | 37 ++++++++ libminifi/include/core/extension/Extension.h | 11 +-- libminifi/src/core/extension/ApiVersion.cpp | 33 +++++++ libminifi/src/core/extension/Extension.cpp | 20 +++-- libminifi/test/integration/CMakeLists.txt | 2 + .../CApiExtension.cpp | 39 ++++++++ .../CMakeLists.txt | 79 ++++++++++++++++ .../CppApiExtension.cpp | 37 ++++++++ .../ExtensionVerificationTests.cpp | 90 +++++++++++++++++++ .../InvalidMissingInitExtension.cpp | 17 ++++ .../dummy-cpp-api.def | 3 + 14 files changed, 354 insertions(+), 21 deletions(-) create mode 100644 libminifi/include/core/extension/ApiVersion.h create mode 100644 libminifi/src/core/extension/ApiVersion.cpp create mode 100644 libminifi/test/integration/extension-verification-test/CApiExtension.cpp create mode 100644 libminifi/test/integration/extension-verification-test/CMakeLists.txt create mode 100644 libminifi/test/integration/extension-verification-test/CppApiExtension.cpp create mode 100644 libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp create mode 100644 libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp create mode 100644 libminifi/test/integration/extension-verification-test/dummy-cpp-api.def diff --git a/Extensions.md b/Extensions.md index 0bfbd5bd98..159232f9ad 100644 --- a/Extensions.md +++ b/Extensions.md @@ -82,7 +82,7 @@ extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return MinifiCreateCppExtension(&ext_create_info); + return minifi::utils::MinifiCreateCppExtension(&ext_create_info); } ``` diff --git a/cmake/Extensions.cmake b/cmake/Extensions.cmake index 6ea93f1cb6..6e25d288b4 100644 --- a/cmake/Extensions.cmake +++ b/cmake/Extensions.cmake @@ -31,8 +31,7 @@ macro(register_extension extension-name extension-display-name extension-guard d 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/extensions/python/pythonloader/PyProcLoader.cpp b/extensions/python/pythonloader/PyProcLoader.cpp index cd9cbb9986..d4edf9cf2e 100644 --- a/extensions/python/pythonloader/PyProcLoader.cpp +++ b/extensions/python/pythonloader/PyProcLoader.cpp @@ -49,5 +49,5 @@ extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* config) { .processors_count = 0, .processors_ptr = nullptr }; - return minifi::utils::MinifiCreateCppExtension( &ext_create_info); + return minifi::utils::MinifiCreateCppExtension(&ext_create_info); } diff --git a/libminifi/include/core/extension/ApiVersion.h b/libminifi/include/core/extension/ApiVersion.h new file mode 100644 index 0000000000..01acb82cba --- /dev/null +++ b/libminifi/include/core/extension/ApiVersion.h @@ -0,0 +1,37 @@ +/** +* 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. + */ + +#pragma once + +#include + +namespace org::apache::nifi::minifi::core::extension { + +struct ApiVersion { + int major; + int minor; + int patch; + + std::string str() const { + return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); + } +}; + +ApiVersion getAgentApiVersion(); +void setAgentApiVersion(ApiVersion api_version); + +} // namespace org::apache::nifi::minifi::core::extension diff --git a/libminifi/include/core/extension/Extension.h b/libminifi/include/core/extension/Extension.h index 2a71a6fbc2..356efed4c2 100644 --- a/libminifi/include/core/extension/Extension.h +++ b/libminifi/include/core/extension/Extension.h @@ -25,6 +25,7 @@ #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 { @@ -39,12 +40,6 @@ class Extension { void* user_data; }; - struct Version { - int major{MINIFI_API_MAJOR_VERSION}; - int minor{MINIFI_API_MINOR_VERSION}; - int patch{MINIFI_API_PATCH_VERSION}; - }; - Extension(std::string name, std::filesystem::path library_path); Extension(const Extension&) = delete; @@ -56,7 +51,7 @@ class Extension { bool initialize(const std::shared_ptr& configure); - Version version() const {return version_;} + [[nodiscard]] ApiVersion apiVersion() const {return api_version_;} private: #ifdef WIN32 @@ -80,7 +75,7 @@ class Extension { std::filesystem::path library_path_; gsl::owner handle_ = nullptr; - Version version_{}; + ApiVersion api_version_; std::unique_ptr info_; const std::shared_ptr logger_; diff --git a/libminifi/src/core/extension/ApiVersion.cpp b/libminifi/src/core/extension/ApiVersion.cpp new file mode 100644 index 0000000000..1f515b25b8 --- /dev/null +++ b/libminifi/src/core/extension/ApiVersion.cpp @@ -0,0 +1,33 @@ +/** +* 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/ApiVersion.h" +#include "minifi-c/minifi-c.h" + +namespace org::apache::nifi::minifi::core::extension { + +static ApiVersion agent_api_version{.major = MINIFI_API_MAJOR_VERSION, .minor = MINIFI_API_MINOR_VERSION, .patch = MINIFI_API_PATCH_VERSION}; + +ApiVersion getAgentApiVersion() { + return agent_api_version; +} + +void setAgentApiVersion(ApiVersion api_version) { + agent_api_version = api_version; +} + +} // namespace org::apache::nifi::minifi::core::extension \ No newline at end of file diff --git a/libminifi/src/core/extension/Extension.cpp b/libminifi/src/core/extension/Extension.cpp index 35d0634b9e..9073b6c9ad 100644 --- a/libminifi/src/core/extension/Extension.cpp +++ b/libminifi/src/core/extension/Extension.cpp @@ -45,6 +45,7 @@ namespace org::apache::nifi::minifi::core::extension { Extension::Extension(std::string name, std::filesystem::path library_path) : name_(std::move(name)), library_path_(std::move(library_path)), + api_version_(getAgentApiVersion()), logger_(logging::LoggerFactory::getLogger()) { } @@ -65,7 +66,7 @@ bool Extension::load(bool global) { return true; } if (!findSymbol("MinifiInitExtension")) { - logger_->log_error("Failed to load c extension '{}' at '{}': No initializer found", name_, library_path_); + logger_->log_error("Failed to load as c extension '{}' at '{}': No initializer found", name_, library_path_); return false; } auto api_version = reinterpret_cast(findSymbol("MinifiApiVersion")); @@ -79,21 +80,21 @@ bool Extension::load(bool global) { return false; } gsl_Assert(match.size() == 4); - version_.major = std::stoi(match[1]); - version_.minor = std::stoi(match[2]); - version_.patch = std::stoi(match[3]); - if (version_.major != MINIFI_API_MAJOR_VERSION) { + api_version_.major = std::stoi(match[1]); + api_version_.minor = std::stoi(match[2]); + api_version_.patch = std::stoi(match[3]); + if (api_version_.major != getAgentApiVersion().major) { logger_->log_error("Failed to load c extension '{}' at '{}': Api major version mismatch, application is '{}' while extension is '{}'", - name_, library_path_, MINIFI_API_VERSION, *api_version); + name_, library_path_, getAgentApiVersion().str(), *api_version); return false; } - if (version_.minor > MINIFI_API_MINOR_VERSION) { + if (api_version_.minor > getAgentApiVersion().minor) { logger_->log_error("Failed to load c extension '{}' at '{}': Extension is built for a newer version, application is '{}' while extension is '{}'", - name_, library_path_, MINIFI_API_VERSION, *api_version); + name_, library_path_, getAgentApiVersion().str(), *api_version); return false; } logger_->log_debug("Loaded c extension '{}' at '{}': Application version is '{}', extension version is '{}'", - name_, library_path_, MINIFI_API_VERSION, *api_version); + name_, library_path_, getAgentApiVersion().str(), *api_version); return true; } @@ -163,6 +164,7 @@ bool Extension::initialize(const std::shared_ptr& configure) logger_->log_error("Failed to initialize extension '{}'", name_); return false; } + logger_->log_debug("Initialized extension '{}'", name_); return true; } diff --git a/libminifi/test/integration/CMakeLists.txt b/libminifi/test/integration/CMakeLists.txt index 3fb3fd88a2..44e98f76a4 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/libminifi/test/integration/extension-verification-test/CApiExtension.cpp b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp new file mode 100644 index 0000000000..e6fd7cfbce --- /dev/null +++ b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp @@ -0,0 +1,39 @@ +/** +* + * 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 "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" const char* const MinifiApiVersion = MINIFI_TEST_API_VERSION; + +extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { + 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 = 0, + .processors_ptr = nullptr, + }; + return MinifiCreateExtension(&ext_create_info); +} \ No newline at end of file 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 0000000000..32c068106f --- /dev/null +++ b/libminifi/test/integration/extension-verification-test/CMakeLists.txt @@ -0,0 +1,79 @@ +# 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) +target_link_libraries(${testfilename} 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-core-minifi INTERFACE) +target_include_directories(dummy-core-minifi INTERFACE $) +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:${IMPORT_LIB_FILE} /machine:x64 + BYPRODUCTS ${IMPORT_LIB_FILE} + ) + + add_dependencies(dummy-core-minifi dummy_cpp_api_import_lib) + target_link_libraries(dummy-core-minifi INTERFACE ${IMPORT_LIB_FILE}) +elseif (APPLE) + target_link_options(dummy-core-minifi 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-core-minifi) +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-core-minifi) +target_compile_definitions(test-extension-loading-valid-build-id-cpp PRIVATE MINIFI_CREATE_EXTENSION_FN=MinifiCreateCppExtension_${BUILD_IDENTIFIER}) + +create_c_test_extension(same-version "1.1.1") +create_c_test_extension(greater-major-version "2.0.0") +create_c_test_extension(smaller-major-version "0.1.0") +create_c_test_extension(greater-minor-version "1.2.0") +create_c_test_extension(smaller-minor-version "1.0.1") +create_c_test_extension(greater-patch-version "1.1.2") +create_c_test_extension(smaller-patch-version "1.1.0") +create_loading_test_extension(test-extension-loading-missing-init InvalidMissingInitExtension.cpp) diff --git a/libminifi/test/integration/extension-verification-test/CppApiExtension.cpp b/libminifi/test/integration/extension-verification-test/CppApiExtension.cpp new file mode 100644 index 0000000000..0503a8c179 --- /dev/null +++ b/libminifi/test/integration/extension-verification-test/CppApiExtension.cpp @@ -0,0 +1,37 @@ +/** +* + * 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 +#include "utils/ExtensionInitUtils.h" + +#define MKSOC(x) #x +#define MAKESTRING(x) MKSOC(x) // NOLINT(cppcoreguidelines-macro-usage) + +namespace minifi = org::apache::nifi::minifi; + +extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* /*config*/) { + MinifiExtensionCreateInfo ext_create_info{ + .name = minifi::utils::toStringView(MAKESTRING(MODULE_NAME)), + .version = minifi::utils::toStringView("1.0.0"), + .deinit = nullptr, + .user_data = nullptr, + .processors_count = 0, + .processors_ptr = nullptr + }; + return minifi::utils::MinifiCreateCppExtension(&ext_create_info); +} 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 0000000000..6fa304691e --- /dev/null +++ b/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp @@ -0,0 +1,90 @@ +/** +* + * 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 "unit/TestBase.h" +#include "unit/Catch.h" +#include "unit/TestUtils.h" +#include "core/extension/ExtensionManager.h" +#include "core/extension/ApiVersion.h" + +namespace minifi = org::apache::nifi::minifi; + +class ExtensionLoadingTestController { + public: + ExtensionLoadingTestController(std::string pattern): extension_manager_{[&] () { + LogTestController::getInstance().clear(); + LogTestController::getInstance().setTrace(); + LogTestController::getInstance().setTrace(); + minifi::core::extension::setAgentApiVersion({.major = 1, .minor = 1, .patch = 1}); + 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 same version") { + ExtensionLoadingTestController controller{"*test-extension-loading-same-version*"}; + REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c extension 'test-extension-loading-same-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't load c-api extensions with greater major version") { + ExtensionLoadingTestController controller{"*test-extension-loading-greater-major-version*"}; + REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to load c extension 'test-extension-loading-greater-major-version'")); + REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Api major version mismatch, application is '1.1.1' while extension is '2.0.0'")); +} + +TEST_CASE("Can't load c-api extensions with smaller major version") { + ExtensionLoadingTestController controller{"*test-extension-loading-smaller-major-version*"}; + REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to load c extension 'test-extension-loading-smaller-major-version'")); + REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Api major version mismatch, application is '1.1.1' while extension is '0.1.0'")); +} + +TEST_CASE("Can't load c-api extensions with greater minor version") { + ExtensionLoadingTestController controller{"*test-extension-loading-greater-minor-version*"}; + REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to load c extension 'test-extension-loading-greater-minor-version'")); + REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Extension is built for a newer version")); +} + +TEST_CASE("Can load c-api extensions with smaller minor version") { + ExtensionLoadingTestController controller{"*test-extension-loading-smaller-minor-version*"}; + REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c extension 'test-extension-loading-smaller-minor-version'")); +} + +TEST_CASE("Can load c-api extensions with greater patch version") { + ExtensionLoadingTestController controller{"*test-extension-loading-greater-patch-version*"}; + REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c extension 'test-extension-loading-greater-patch-version'")); +} + +TEST_CASE("Can load c-api extensions with smaller patch version") { + ExtensionLoadingTestController controller{"*test-extension-loading-smaller-patch-version*"}; + REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c extension 'test-extension-loading-smaller-patch-version'")); +} diff --git a/libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp b/libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp new file mode 100644 index 0000000000..3fbb5023e4 --- /dev/null +++ b/libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp @@ -0,0 +1,17 @@ +/** +* + * 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. + */ \ No newline at end of file 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 0000000000..8da19c75ec --- /dev/null +++ b/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def @@ -0,0 +1,3 @@ +LIBRARY core-minifi.dll +EXPORTS + MinifiCreateCppExtension_NonMatchingBuildId From 7681fd5f41b7c2de5fa9194c3465e05cfb18e59d Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Mon, 2 Mar 2026 17:23:32 +0100 Subject: [PATCH 07/16] MINIFICPP-2715 - Use a single integer as c api version --- Extensions.md | 10 +-- .../include/utils/ExtensionInitUtils.h | 4 +- extensions/ExtensionInitializer.cpp | 4 +- .../processors/ExtensionInitializer.cpp | 8 +- extensions/opencv/OpenCVLoader.cpp | 6 +- .../pythonlibloader/PythonLibLoader.cpp | 4 +- .../python/pythonloader/PyProcLoader.cpp | 4 +- extensions/sftp/SFTPLoader.cpp | 8 +- libminifi/include/core/extension/ApiVersion.h | 16 +--- libminifi/include/core/extension/Extension.h | 8 +- libminifi/src/core/extension/ApiVersion.cpp | 15 +++- libminifi/src/core/extension/Extension.cpp | 76 +++++++++---------- libminifi/src/minifi-c.cpp | 15 ++-- .../CApiExtension.cpp | 6 +- .../CMakeLists.txt | 18 +++-- .../CppApiExtension.cpp | 4 +- .../CreateNotCalled.cpp | 25 ++++++ .../ExtensionVerificationTests.cpp | 49 ++++++------ minifi-api/include/minifi-c/minifi-c.h | 11 ++- 19 files changed, 159 insertions(+), 132 deletions(-) create mode 100644 libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp diff --git a/Extensions.md b/Extensions.md index 159232f9ad..f75c068e67 100644 --- a/Extensions.md +++ b/Extensions.md @@ -30,8 +30,7 @@ One possible example of this is: ```C++ extern "C" const char* const MinifiApiVersion = MINIFI_API_VERSION; -extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { - MinifiExtension* extension = nullptr; +extern "C" void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { minifi::api::core::useProcessorClassDescription([&] (const MinifiProcessorClassDefinition& description) { MinifiExtensionCreateInfo ext_create_info{ .name = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_NAME)), @@ -41,9 +40,8 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 1, .processors_ptr = &description, }; - extension = MinifiCreateExtension(&ext_create_info); + MinifiCreateExtension(extension, &ext_create_info); }); - return extension; } ``` @@ -69,7 +67,7 @@ Some extensions (e.g. `OpenCVExtension`) require initialization before use. You need to define an `MinifiInitCppExtension` function of type `MinifiExtension*(MinifiConfig*)` to be called. ```C++ -extern "C" MinifiExtension* MinifiInitCppExtension(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; @@ -82,7 +80,7 @@ extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return minifi::utils::MinifiCreateCppExtension(&ext_create_info); + minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info); } ``` diff --git a/extension-framework/include/utils/ExtensionInitUtils.h b/extension-framework/include/utils/ExtensionInitUtils.h index 57f1d063f2..75da5c1ae9 100644 --- a/extension-framework/include/utils/ExtensionInitUtils.h +++ b/extension-framework/include/utils/ExtensionInitUtils.h @@ -30,8 +30,8 @@ inline MinifiStringView toStringView(std::string_view str) { using ConfigReader = std::function(std::string_view key)>; -static inline MinifiExtension* MinifiCreateCppExtension(const MinifiExtensionCreateInfo* create_info) { - return MINIFI_CREATE_EXTENSION_FN(create_info); +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/ExtensionInitializer.cpp b/extensions/ExtensionInitializer.cpp index e735a78e77..47a4c21857 100644 --- a/extensions/ExtensionInitializer.cpp +++ b/extensions/ExtensionInitializer.cpp @@ -22,7 +22,7 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* /*config*/) { +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), @@ -31,5 +31,5 @@ extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return minifi::utils::MinifiCreateCppExtension(&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 29316f8b34..8c223dc703 100644 --- a/extensions/llamacpp/processors/ExtensionInitializer.cpp +++ b/extensions/llamacpp/processors/ExtensionInitializer.cpp @@ -24,10 +24,9 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" const char* const MinifiApiVersion = MINIFI_API_VERSION; +extern "C" const uint32_t MinifiApiVersion = MINIFI_API_VERSION; -extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { - MinifiExtension* extension = nullptr; +extern "C" void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { minifi::api::core::useProcessorClassDescription([&] (const MinifiProcessorClassDefinition& description) { MinifiExtensionCreateInfo ext_create_info{ .name = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_NAME)), @@ -37,7 +36,6 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 1, .processors_ptr = &description, }; - extension = MinifiCreateExtension(&ext_create_info); + MinifiCreateExtension(extension, &ext_create_info); }); - return extension; } diff --git a/extensions/opencv/OpenCVLoader.cpp b/extensions/opencv/OpenCVLoader.cpp index 53f61dbcaf..87c6615c51 100644 --- a/extensions/opencv/OpenCVLoader.cpp +++ b/extensions/opencv/OpenCVLoader.cpp @@ -24,7 +24,7 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" MinifiExtension* MinifiInitCppExtension(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* MinifiInitCppExtension(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* MinifiInitCppExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return minifi::utils::MinifiCreateCppExtension(&ext_create_info); + minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info); } diff --git a/extensions/python/pythonlibloader/PythonLibLoader.cpp b/extensions/python/pythonlibloader/PythonLibLoader.cpp index 9c9a35d284..79ef215564 100644 --- a/extensions/python/pythonlibloader/PythonLibLoader.cpp +++ b/extensions/python/pythonlibloader/PythonLibLoader.cpp @@ -98,7 +98,7 @@ class PythonLibLoader { std::shared_ptr logger_ = minifi::core::logging::LoggerFactory::getLogger(); }; -extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* config) { +extern "C" void MinifiInitCppExtension(MinifiExtension* extension, MinifiConfig* config) { static PythonLibLoader python_lib_loader([&] (std::string_view key) -> std::optional { std::optional result; MinifiConfigGet(config, minifi::utils::toStringView(key), [] (void* user_data, MinifiStringView value) { @@ -114,5 +114,5 @@ extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* config) { .processors_count = 0, .processors_ptr = nullptr }; - return minifi::utils::MinifiCreateCppExtension(&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 d4edf9cf2e..e92bbea416 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* MinifiInitCppExtension(MinifiConfig* config) { +extern "C" void MinifiInitCppExtension(MinifiExtension* extension, MinifiConfig* config) { getPythonCreator().configure([&] (std::string_view key) -> std::optional { std::optional result; MinifiConfigGet(config, minifi::utils::toStringView(key), [] (void* user_data, MinifiStringView value) { @@ -49,5 +49,5 @@ extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* config) { .processors_count = 0, .processors_ptr = nullptr }; - return minifi::utils::MinifiCreateCppExtension(&ext_create_info); + minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info); } diff --git a/extensions/sftp/SFTPLoader.cpp b/extensions/sftp/SFTPLoader.cpp index 333ab173e5..c8309cc602 100644 --- a/extensions/sftp/SFTPLoader.cpp +++ b/extensions/sftp/SFTPLoader.cpp @@ -25,13 +25,13 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" MinifiExtension* MinifiInitCppExtension(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* MinifiInitCppExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return minifi::utils::MinifiCreateCppExtension(&ext_create_info); + minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info); } diff --git a/libminifi/include/core/extension/ApiVersion.h b/libminifi/include/core/extension/ApiVersion.h index 01acb82cba..34b917a32e 100644 --- a/libminifi/include/core/extension/ApiVersion.h +++ b/libminifi/include/core/extension/ApiVersion.h @@ -21,17 +21,9 @@ namespace org::apache::nifi::minifi::core::extension { -struct ApiVersion { - int major; - int minor; - int patch; - - std::string str() const { - return std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(patch); - } -}; - -ApiVersion getAgentApiVersion(); -void setAgentApiVersion(ApiVersion api_version); +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::core::extension diff --git a/libminifi/include/core/extension/Extension.h b/libminifi/include/core/extension/Extension.h index 356efed4c2..85cf4fc60a 100644 --- a/libminifi/include/core/extension/Extension.h +++ b/libminifi/include/core/extension/Extension.h @@ -51,7 +51,7 @@ class Extension { bool initialize(const std::shared_ptr& configure); - [[nodiscard]] ApiVersion apiVersion() const {return api_version_;} + bool setInfo(Info info); private: #ifdef WIN32 @@ -71,12 +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 handle_ = nullptr; - ApiVersion api_version_; - std::unique_ptr info_; + std::optional info_; + uint32_t api_version_{0}; const std::shared_ptr logger_; }; diff --git a/libminifi/src/core/extension/ApiVersion.cpp b/libminifi/src/core/extension/ApiVersion.cpp index 1f515b25b8..15f59d18b1 100644 --- a/libminifi/src/core/extension/ApiVersion.cpp +++ b/libminifi/src/core/extension/ApiVersion.cpp @@ -20,14 +20,23 @@ namespace org::apache::nifi::minifi::core::extension { -static ApiVersion agent_api_version{.major = MINIFI_API_MAJOR_VERSION, .minor = MINIFI_API_MINOR_VERSION, .patch = MINIFI_API_PATCH_VERSION}; +static uint32_t agent_api_version{MINIFI_API_VERSION}; +static uint32_t min_supported_api_version{MINIFI_API_VERSION}; -ApiVersion getAgentApiVersion() { +uint32_t getAgentApiVersion() { return agent_api_version; } -void setAgentApiVersion(ApiVersion api_version) { +void test_setAgentApiVersion(uint32_t api_version) { agent_api_version = api_version; } +uint32_t getMinSupportedApiVersion() { + return min_supported_api_version; +} + +void test_setMinSupportedApiVersion(uint32_t min_api_version) { + min_supported_api_version = min_api_version; +} + } // namespace org::apache::nifi::minifi::core::extension \ No newline at end of file diff --git a/libminifi/src/core/extension/Extension.cpp b/libminifi/src/core/extension/Extension.cpp index 9073b6c9ad..9382da9d1c 100644 --- a/libminifi/src/core/extension/Extension.cpp +++ b/libminifi/src/core/extension/Extension.cpp @@ -42,10 +42,9 @@ 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)), - api_version_(getAgentApiVersion()), logger_(logging::LoggerFactory::getLogger()) { } @@ -57,59 +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; } - logger_->log_trace("Dlopen succeeded for 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 '{}'", name_, library_path_); + 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", name_, library_path_); + logger_->log_error("Failed to load as c extension '{}' at '{}': No initializer found", library_name_, library_path_); return false; } - auto api_version = reinterpret_cast(findSymbol("MinifiApiVersion")); - if (!api_version) { - logger_->log_error("Failed to load c extension '{}' at '{}': No MinifiApiVersion symbol found", name_, library_path_); + auto api_version_ptr = reinterpret_cast(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; } - utils::SVMatch match; - if (!utils::regexSearch(*api_version, match, utils::Regex{R"(^([0-9]+)\.([0-9]+)\.([0-9]+)$)"})) { - logger_->log_error("Failed to load c extension '{}' at '{}': Api version is in invalid format: '{}'", name_, library_path_, *api_version); + 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; } - gsl_Assert(match.size() == 4); - api_version_.major = std::stoi(match[1]); - api_version_.minor = std::stoi(match[2]); - api_version_.patch = std::stoi(match[3]); - if (api_version_.major != getAgentApiVersion().major) { - logger_->log_error("Failed to load c extension '{}' at '{}': Api major version mismatch, application is '{}' while extension is '{}'", - name_, library_path_, getAgentApiVersion().str(), *api_version); + 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; } - if (api_version_.minor > getAgentApiVersion().minor) { - logger_->log_error("Failed to load c extension '{}' at '{}': Extension is built for a newer version, application is '{}' while extension is '{}'", - name_, library_path_, getAgentApiVersion().str(), *api_version); - return false; - } - logger_->log_debug("Loaded c extension '{}' at '{}': Application version is '{}', extension version is '{}'", - name_, library_path_, getAgentApiVersion().str(), *api_version); + 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; } @@ -146,25 +137,34 @@ Extension::~Extension() { #endif } +bool Extension::setInfo(Info info) { + if (info_) { + return false; + } + info_ = std::move(info); + return true; +} + bool Extension::initialize(const std::shared_ptr& configure) { - logger_->log_trace("Initializing extension '{}'", 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 '{}'", name_); + logger_->log_error("No initializer for '{}'", library_name_); return false; } - logger_->log_debug("Found initializer for '{}'", name_); - auto init_fn = reinterpret_cast(init_symbol_ptr); + logger_->log_debug("Found initializer for '{}'", library_name_); + auto init_fn = reinterpret_cast(init_symbol_ptr); auto config_handle = reinterpret_cast(configure.get()); - info_.reset(reinterpret_cast(init_fn(config_handle))); + auto extension_handle = reinterpret_cast(this); + init_fn(extension_handle, config_handle); if (!info_) { - logger_->log_error("Failed to initialize extension '{}'", name_); + logger_->log_error("Failed to initialize extension '{}'", library_name_); return false; } - logger_->log_debug("Initialized extension '{}'", name_); + logger_->log_debug("Initialized extension '{}', name = '{}', version = '{}'", library_name_, info_->name, info_->version); return true; } diff --git a/libminifi/src/minifi-c.cpp b/libminifi/src/minifi-c.cpp index e670c44153..0936f48082 100644 --- a/libminifi/src/minifi-c.cpp +++ b/libminifi/src/minifi-c.cpp @@ -225,10 +225,9 @@ void useCProcessorClassDescription(const MinifiProcessorClassDefinition& class_d extern "C" { -MinifiExtension* MinifiCreateExtension(const MinifiExtensionCreateInfo* extension_create_info) { +MinifiStatus MinifiCreateExtension(MinifiExtension* extension, const MinifiExtensionCreateInfo* extension_create_info) { + gsl_Assert(extension); gsl_Assert(extension_create_info); - auto current_extension = minifi::core::extension::ExtensionManager::getExtensionBeingInitialized(); - gsl_Assert(current_extension); auto extension_name = toString(extension_create_info->name); minifi::BundleIdentifier bundle{ .name = extension_name, @@ -243,16 +242,20 @@ MinifiExtension* MinifiCreateExtension(const MinifiExtensionCreateInfo* extensio bundle_components.processors.emplace_back(description); }); } - return reinterpret_cast(new org::apache::nifi::minifi::core::extension::Extension::Info{ + bool success = reinterpret_cast(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; } -MinifiExtension* MINIFI_CREATE_EXTENSION_FN(const MinifiExtensionCreateInfo* extension_create_info) { - return MinifiCreateExtension(extension_create_info); +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/extension-verification-test/CApiExtension.cpp b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp index e6fd7cfbce..bbd51809a7 100644 --- a/libminifi/test/integration/extension-verification-test/CApiExtension.cpp +++ b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp @@ -24,9 +24,9 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" const char* const MinifiApiVersion = MINIFI_TEST_API_VERSION; +extern "C" const uint32_t MinifiApiVersion = MINIFI_TEST_API_VERSION; -extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { +extern "C" void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { MinifiExtensionCreateInfo ext_create_info{ .name = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_NAME)), .version = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_VERSION)), @@ -35,5 +35,5 @@ extern "C" MinifiExtension* MinifiInitExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr, }; - return MinifiCreateExtension(&ext_create_info); + MinifiCreateExtension(extension, &ext_create_info); } \ No newline at end of file diff --git a/libminifi/test/integration/extension-verification-test/CMakeLists.txt b/libminifi/test/integration/extension-verification-test/CMakeLists.txt index 32c068106f..fa428b044a 100644 --- a/libminifi/test/integration/extension-verification-test/CMakeLists.txt +++ b/libminifi/test/integration/extension-verification-test/CMakeLists.txt @@ -41,7 +41,7 @@ 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 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() @@ -69,11 +69,13 @@ create_loading_test_extension(test-extension-loading-valid-build-id-cpp CppApiEx target_link_libraries(test-extension-loading-valid-build-id-cpp dummy-core-minifi) target_compile_definitions(test-extension-loading-valid-build-id-cpp PRIVATE MINIFI_CREATE_EXTENSION_FN=MinifiCreateCppExtension_${BUILD_IDENTIFIER}) -create_c_test_extension(same-version "1.1.1") -create_c_test_extension(greater-major-version "2.0.0") -create_c_test_extension(smaller-major-version "0.1.0") -create_c_test_extension(greater-minor-version "1.2.0") -create_c_test_extension(smaller-minor-version "1.0.1") -create_c_test_extension(greater-patch-version "1.1.2") -create_c_test_extension(smaller-patch-version "1.1.0") +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-c-api) +target_compile_definitions(test-extension-loading-create-not-called PRIVATE MINIFI_TEST_API_VERSION=10) diff --git a/libminifi/test/integration/extension-verification-test/CppApiExtension.cpp b/libminifi/test/integration/extension-verification-test/CppApiExtension.cpp index 0503a8c179..59daee45d5 100644 --- a/libminifi/test/integration/extension-verification-test/CppApiExtension.cpp +++ b/libminifi/test/integration/extension-verification-test/CppApiExtension.cpp @@ -24,7 +24,7 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* /*config*/) { +extern "C" void MinifiInitCppExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { MinifiExtensionCreateInfo ext_create_info{ .name = minifi::utils::toStringView(MAKESTRING(MODULE_NAME)), .version = minifi::utils::toStringView("1.0.0"), @@ -33,5 +33,5 @@ extern "C" MinifiExtension* MinifiInitCppExtension(MinifiConfig* /*config*/) { .processors_count = 0, .processors_ptr = nullptr }; - return minifi::utils::MinifiCreateCppExtension(&ext_create_info); + minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info); } diff --git a/libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp b/libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp new file mode 100644 index 0000000000..ec6e5394d3 --- /dev/null +++ b/libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp @@ -0,0 +1,25 @@ +/** +* + * 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 "minifi-c.h" + +extern "C" const uint32_t MinifiApiVersion = MINIFI_TEST_API_VERSION; + +extern "C" void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { + // NOT CALLING CREATE +} \ No newline at end of file diff --git a/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp b/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp index 6fa304691e..a31700bf73 100644 --- a/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp +++ b/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp @@ -32,7 +32,8 @@ class ExtensionLoadingTestController { LogTestController::getInstance().clear(); LogTestController::getInstance().setTrace(); LogTestController::getInstance().setTrace(); - minifi::core::extension::setAgentApiVersion({.major = 1, .minor = 1, .patch = 1}); + 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; @@ -46,9 +47,9 @@ TEST_CASE("Can load cpp-api extensions with same build id") { REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded cpp extension 'test-extension-loading-valid-build-id-cpp'")); } -TEST_CASE("Can load c-api extensions with same version") { - ExtensionLoadingTestController controller{"*test-extension-loading-same-version*"}; - REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c extension 'test-extension-loading-same-version'")); +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") { @@ -56,35 +57,35 @@ TEST_CASE("Can't load cpp-api extensions with different build id") { REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to load extension 'test-extension-loading-invalid-build-id-cpp'")); } -TEST_CASE("Can't load c-api extensions with greater major version") { - ExtensionLoadingTestController controller{"*test-extension-loading-greater-major-version*"}; - REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to load c extension 'test-extension-loading-greater-major-version'")); - REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Api major version mismatch, application is '1.1.1' while extension is '2.0.0'")); +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't load c-api extensions with smaller major version") { - ExtensionLoadingTestController controller{"*test-extension-loading-smaller-major-version*"}; - REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to load c extension 'test-extension-loading-smaller-major-version'")); - REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Api major version mismatch, application is '1.1.1' while extension is '0.1.0'")); +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 minor version") { - ExtensionLoadingTestController controller{"*test-extension-loading-greater-minor-version*"}; - REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Failed to load c extension 'test-extension-loading-greater-minor-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 load c-api extensions with smaller minor version") { - ExtensionLoadingTestController controller{"*test-extension-loading-smaller-minor-version*"}; - REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c extension 'test-extension-loading-smaller-minor-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 load c-api extensions with greater patch version") { - ExtensionLoadingTestController controller{"*test-extension-loading-greater-patch-version*"}; - REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c extension 'test-extension-loading-greater-patch-version'")); +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 load c-api extensions with smaller patch version") { - ExtensionLoadingTestController controller{"*test-extension-loading-smaller-patch-version*"}; - REQUIRE(minifi::test::utils::verifyLogLinePresenceInPollTime(0s, "Loaded c extension 'test-extension-loading-smaller-patch-version'")); +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/minifi-api/include/minifi-c/minifi-c.h b/minifi-api/include/minifi-c/minifi-c.h index 5bab749412..9a80b5791c 100644 --- a/minifi-api/include/minifi-c/minifi-c.h +++ b/minifi-api/include/minifi-c/minifi-c.h @@ -34,11 +34,6 @@ extern "C" { #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_NULL nullptr #define MINIFI_OWNED @@ -46,6 +41,10 @@ extern "C" { #define MINIFI_CREATE_EXTENSION_FN MinifiCreateExtension #endif +enum : uint32_t { + MINIFI_API_VERSION = 1 +}; + typedef bool MinifiBool; typedef enum MinifiInputRequirement : uint32_t { @@ -189,7 +188,7 @@ typedef struct MinifiExtensionCreateInfo { const MinifiProcessorClassDefinition* processors_ptr; } MinifiExtensionCreateInfo; -MinifiExtension* MINIFI_CREATE_EXTENSION_FN(const MinifiExtensionCreateInfo* create_info); +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); From 678a0da33a820d1d0392dba5e84d1709e462b855 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Wed, 11 Mar 2026 13:37:42 +0100 Subject: [PATCH 08/16] MINIFICPP-2715 - Rebase fix --- libminifi/src/core/extension/Extension.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libminifi/src/core/extension/Extension.cpp b/libminifi/src/core/extension/Extension.cpp index 9382da9d1c..f8ca0f5f04 100644 --- a/libminifi/src/core/extension/Extension.cpp +++ b/libminifi/src/core/extension/Extension.cpp @@ -118,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 @@ -127,12 +129,12 @@ 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 } From a08182459ce4008fc27d44a474b34ca123ad631b Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Wed, 11 Mar 2026 13:39:13 +0100 Subject: [PATCH 09/16] MINIFICPP-2715 - Add missing include --- libminifi/include/core/extension/ApiVersion.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libminifi/include/core/extension/ApiVersion.h b/libminifi/include/core/extension/ApiVersion.h index 34b917a32e..4754f4ef0b 100644 --- a/libminifi/include/core/extension/ApiVersion.h +++ b/libminifi/include/core/extension/ApiVersion.h @@ -17,7 +17,8 @@ #pragma once -#include +#include +#include namespace org::apache::nifi::minifi::core::extension { From 17b9305861598591103fd903e90e4e82ca3fbed6 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Thu, 12 Mar 2026 09:03:06 +0100 Subject: [PATCH 10/16] MINIFICPP-2715 - Windows fix, format --- libminifi/src/core/extension/ApiVersion.cpp | 2 +- .../extension-verification-test/CApiExtension.cpp | 2 +- .../integration/extension-verification-test/CMakeLists.txt | 6 +++--- .../extension-verification-test/CreateNotCalled.cpp | 2 +- .../ExtensionVerificationTests.cpp | 4 ++-- .../InvalidMissingInitExtension.cpp | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/libminifi/src/core/extension/ApiVersion.cpp b/libminifi/src/core/extension/ApiVersion.cpp index 15f59d18b1..bf52594206 100644 --- a/libminifi/src/core/extension/ApiVersion.cpp +++ b/libminifi/src/core/extension/ApiVersion.cpp @@ -39,4 +39,4 @@ void test_setMinSupportedApiVersion(uint32_t min_api_version) { min_supported_api_version = min_api_version; } -} // namespace org::apache::nifi::minifi::core::extension \ No newline at end of file +} // namespace org::apache::nifi::minifi::core::extension diff --git a/libminifi/test/integration/extension-verification-test/CApiExtension.cpp b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp index bbd51809a7..c93f1c1f24 100644 --- a/libminifi/test/integration/extension-verification-test/CApiExtension.cpp +++ b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp @@ -36,4 +36,4 @@ extern "C" void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /* .processors_ptr = nullptr, }; MinifiCreateExtension(extension, &ext_create_info); -} \ No newline at end of file +} diff --git a/libminifi/test/integration/extension-verification-test/CMakeLists.txt b/libminifi/test/integration/extension-verification-test/CMakeLists.txt index fa428b044a..378b9c64e4 100644 --- a/libminifi/test/integration/extension-verification-test/CMakeLists.txt +++ b/libminifi/test/integration/extension-verification-test/CMakeLists.txt @@ -51,12 +51,12 @@ target_include_directories(dummy-core-minifi INTERFACE $(); LogTestController::getInstance().setTrace(); @@ -39,7 +39,7 @@ class ExtensionLoadingTestController { return config; }()} {} - core::extension::ExtensionManager extension_manager_; + core::extension::ExtensionManager extension_manager_; }; TEST_CASE("Can load cpp-api extensions with same build id") { diff --git a/libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp b/libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp index 3fbb5023e4..55b2539b77 100644 --- a/libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp +++ b/libminifi/test/integration/extension-verification-test/InvalidMissingInitExtension.cpp @@ -14,4 +14,4 @@ * 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. - */ \ No newline at end of file + */ From 4ed46e1af4585523acda798f41437e840bdae98d Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Thu, 12 Mar 2026 10:54:54 +0100 Subject: [PATCH 11/16] MINIFICPP-2715 - Windows fix --- .../test/integration/extension-verification-test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libminifi/test/integration/extension-verification-test/CMakeLists.txt b/libminifi/test/integration/extension-verification-test/CMakeLists.txt index 378b9c64e4..4e29806097 100644 --- a/libminifi/test/integration/extension-verification-test/CMakeLists.txt +++ b/libminifi/test/integration/extension-verification-test/CMakeLists.txt @@ -66,7 +66,7 @@ target_link_libraries(test-extension-loading-invalid-build-id-cpp dummy-core-min 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-core-minifi) +target_link_libraries(test-extension-loading-valid-build-id-cpp minifi-c-api dummy-core-minifi) target_compile_definitions(test-extension-loading-valid-build-id-cpp PRIVATE MINIFI_CREATE_EXTENSION_FN=MinifiCreateCppExtension_${BUILD_IDENTIFIER}) create_c_test_extension(max-version 10) From 912479b03a8eb65698dc858573730a5ed253b323 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Thu, 12 Mar 2026 12:16:24 +0100 Subject: [PATCH 12/16] MINIFICPP-2715 - Windows fix --- .../extension-verification-test/CMakeLists.txt | 6 +++--- .../ExtensionVerificationTests.cpp | 9 +++++++++ .../extension-verification-test/dummy-cpp-api.def | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libminifi/test/integration/extension-verification-test/CMakeLists.txt b/libminifi/test/integration/extension-verification-test/CMakeLists.txt index 4e29806097..2ef595e9bc 100644 --- a/libminifi/test/integration/extension-verification-test/CMakeLists.txt +++ b/libminifi/test/integration/extension-verification-test/CMakeLists.txt @@ -19,7 +19,7 @@ add_minifi_executable(ExtensionVerificationTests ExtensionVerificationTests.cpp) createIntegrationTests(ExtensionVerificationTests) -target_link_libraries(${testfilename} core-minifi Catch2WithMain Threads::Threads) +target_link_libraries(ExtensionVerificationTests core-minifi Catch2WithMain Threads::Threads) add_test(NAME ExtensionVerificationTests COMMAND ExtensionVerificationTests) function(create_loading_test_extension extension-name extension-main) @@ -66,8 +66,8 @@ target_link_libraries(test-extension-loading-invalid-build-id-cpp dummy-core-min 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 minifi-c-api dummy-core-minifi) -target_compile_definitions(test-extension-loading-valid-build-id-cpp PRIVATE MINIFI_CREATE_EXTENSION_FN=MinifiCreateCppExtension_${BUILD_IDENTIFIER}) +target_link_libraries(test-extension-loading-valid-build-id-cpp dummy-core-minifi) +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) diff --git a/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp b/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp index c0bec8b8cf..86084f80de 100644 --- a/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp +++ b/libminifi/test/integration/extension-verification-test/ExtensionVerificationTests.cpp @@ -18,12 +18,21 @@ #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 { diff --git a/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def b/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def index 8da19c75ec..33c34291e2 100644 --- a/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def +++ b/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def @@ -1,3 +1,4 @@ LIBRARY core-minifi.dll EXPORTS MinifiCreateCppExtension_NonMatchingBuildId + MinifiCreateCppExtension_MatchingBuildId From 029dbe21336b32bc266c1b1b9b68dee0f0afb482 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Thu, 12 Mar 2026 14:46:15 +0100 Subject: [PATCH 13/16] MINIFICPP-2715 - Windows fix --- .../cpp-extension-lib/include/api/utils/minifi-c-utils.h | 6 ++++++ extensions/llamacpp/processors/ExtensionInitializer.cpp | 4 ++-- .../extension-verification-test/CApiExtension.cpp | 4 ++-- .../integration/extension-verification-test/CMakeLists.txt | 2 +- .../extension-verification-test/CreateNotCalled.cpp | 5 +++-- 5 files changed, 14 insertions(+), 7 deletions(-) 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 756c521139..76a5a43aec 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 __declspec(dllexport) extern "C" +#else + #define CEXTENSIONAPI extern "C" +#endif + namespace org::apache::nifi::minifi::api::utils { inline MinifiStringView toStringView(std::string_view str) { diff --git a/extensions/llamacpp/processors/ExtensionInitializer.cpp b/extensions/llamacpp/processors/ExtensionInitializer.cpp index 8c223dc703..4b3c5b4e0d 100644 --- a/extensions/llamacpp/processors/ExtensionInitializer.cpp +++ b/extensions/llamacpp/processors/ExtensionInitializer.cpp @@ -24,9 +24,9 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" const uint32_t MinifiApiVersion = MINIFI_API_VERSION; +CEXTENSIONAPI const uint32_t MinifiApiVersion = MINIFI_API_VERSION; -extern "C" void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { +CEXTENSIONAPI void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { minifi::api::core::useProcessorClassDescription([&] (const MinifiProcessorClassDefinition& description) { MinifiExtensionCreateInfo ext_create_info{ .name = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_NAME)), diff --git a/libminifi/test/integration/extension-verification-test/CApiExtension.cpp b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp index c93f1c1f24..18f47f2806 100644 --- a/libminifi/test/integration/extension-verification-test/CApiExtension.cpp +++ b/libminifi/test/integration/extension-verification-test/CApiExtension.cpp @@ -24,9 +24,9 @@ namespace minifi = org::apache::nifi::minifi; -extern "C" const uint32_t MinifiApiVersion = MINIFI_TEST_API_VERSION; +CEXTENSIONAPI const uint32_t MinifiApiVersion = MINIFI_TEST_API_VERSION; -extern "C" void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { +CEXTENSIONAPI void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { MinifiExtensionCreateInfo ext_create_info{ .name = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_NAME)), .version = minifi::api::utils::toStringView(MAKESTRING(EXTENSION_VERSION)), diff --git a/libminifi/test/integration/extension-verification-test/CMakeLists.txt b/libminifi/test/integration/extension-verification-test/CMakeLists.txt index 2ef595e9bc..d0069db323 100644 --- a/libminifi/test/integration/extension-verification-test/CMakeLists.txt +++ b/libminifi/test/integration/extension-verification-test/CMakeLists.txt @@ -77,5 +77,5 @@ 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-c-api) +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/libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp b/libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp index 1ae88ebf8a..276ffa6d54 100644 --- a/libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp +++ b/libminifi/test/integration/extension-verification-test/CreateNotCalled.cpp @@ -17,9 +17,10 @@ */ #include "minifi-c.h" +#include "api/utils/minifi-c-utils.h" -extern "C" const uint32_t MinifiApiVersion = MINIFI_TEST_API_VERSION; +CEXTENSIONAPI const uint32_t MinifiApiVersion = MINIFI_TEST_API_VERSION; -extern "C" void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { +CEXTENSIONAPI void MinifiInitExtension(MinifiExtension* extension, MinifiConfig* /*config*/) { // NOT CALLING CREATE } From fa51693db7ca3435ce383995de587ed32405d330 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Thu, 12 Mar 2026 16:40:49 +0100 Subject: [PATCH 14/16] MINIFICPP-2715 - Windows fix --- .../cpp-extension-lib/include/api/utils/minifi-c-utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 76a5a43aec..43fb283067 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 @@ -25,7 +25,7 @@ #include "minifi-cpp/core/PropertyDefinition.h" #ifdef WIN32 - #define CEXTENSIONAPI __declspec(dllexport) extern "C" + #define CEXTENSIONAPI extern "C" __declspec(dllexport) #else #define CEXTENSIONAPI extern "C" #endif From a8c619ef97c422b876419b7d6e72040414e64cb6 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Fri, 13 Mar 2026 09:57:17 +0100 Subject: [PATCH 15/16] MINIFICPP-2715 - Fix build --- .../extension-verification-test/CMakeLists.txt | 18 +++++++++++------- .../dummy-cpp-api.def | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/libminifi/test/integration/extension-verification-test/CMakeLists.txt b/libminifi/test/integration/extension-verification-test/CMakeLists.txt index d0069db323..b7cc087de7 100644 --- a/libminifi/test/integration/extension-verification-test/CMakeLists.txt +++ b/libminifi/test/integration/extension-verification-test/CMakeLists.txt @@ -19,6 +19,10 @@ 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) +endif() target_link_libraries(ExtensionVerificationTests core-minifi Catch2WithMain Threads::Threads) add_test(NAME ExtensionVerificationTests COMMAND ExtensionVerificationTests) @@ -46,8 +50,8 @@ function(create_c_test_extension extension-name custom_api_version) PRIVATE "EXTENSION_NAME=${extension-name}" "EXTENSION_VERSION=${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") endfunction() -add_library(dummy-core-minifi INTERFACE) -target_include_directories(dummy-core-minifi INTERFACE $) +add_library(dummy-cpp-api INTERFACE) +target_include_directories(dummy-cpp-api INTERFACE $) if (WIN32) set(DUMMY_IMPORT_LIB_FILE ${CMAKE_CURRENT_BINARY_DIR}/dummy-cpp-api.lib) add_custom_target(dummy_cpp_api_import_lib @@ -55,18 +59,18 @@ if (WIN32) BYPRODUCTS ${DUMMY_IMPORT_LIB_FILE} ) - add_dependencies(dummy-core-minifi dummy_cpp_api_import_lib) - target_link_libraries(dummy-core-minifi INTERFACE ${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-core-minifi INTERFACE -undefined dynamic_lookup) + 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-core-minifi) +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-core-minifi) +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) diff --git a/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def b/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def index 33c34291e2..3971f17df5 100644 --- a/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def +++ b/libminifi/test/integration/extension-verification-test/dummy-cpp-api.def @@ -1,4 +1,4 @@ -LIBRARY core-minifi.dll +LIBRARY ExtensionVerificationTests.exe EXPORTS MinifiCreateCppExtension_NonMatchingBuildId MinifiCreateCppExtension_MatchingBuildId From bfe4f5a119e82dc531fb1c9643ce9f5405dbe6c8 Mon Sep 17 00:00:00 2001 From: Adam Debreceni Date: Fri, 13 Mar 2026 17:00:10 +0100 Subject: [PATCH 16/16] MINIFICPP-2715 - Mac fix --- .../test/integration/extension-verification-test/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libminifi/test/integration/extension-verification-test/CMakeLists.txt b/libminifi/test/integration/extension-verification-test/CMakeLists.txt index b7cc087de7..7f3146b195 100644 --- a/libminifi/test/integration/extension-verification-test/CMakeLists.txt +++ b/libminifi/test/integration/extension-verification-test/CMakeLists.txt @@ -22,6 +22,8 @@ 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)