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