This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/celix.git
commit 84fed65e9ac1b9e29a5a30bef9d4a026d26cf897 Author: Pepijn Noltes <[email protected]> AuthorDate: Sat Sep 13 17:16:14 2025 +0200 Update macOS workflow to use MacOS 15 To get MacOS 15 working, several changes were needed: - Changed Python setup in the workflow to use actions/setup-python. - Added gettext dependency for MacOS 15 in Conanfile. - Fixed zlib version conflict in Conanfile by specifying zlib/1.3.1. - Removed unnecessary `main.cc` file from gtest. - Introduced CURL error injection support in the framework. - Added new error injector for CURL with corresponding tests. - Refactored various files to remove redundant 'template' keywords. - Updated benchmarks to use constexpr for maximum length. --- .github/workflows/macos.yml | 14 +++-- .../src/ConfiguredDiscoveryManager.cc | 72 +++++++++++----------- .../remote_service_admin_dfi/gtest/CMakeLists.txt | 4 +- .../remote_service_admin_dfi/gtest/src/main.cc | 32 ---------- bundles/shell/shell_tui/src/shell_tui.c | 2 +- cmake/Findcivetweb.cmake | 10 +-- cmake/celix_project/CelixProject.cmake | 12 ++-- cmake/cmake_celix/BundlePackaging.cmake | 4 +- conanfile.py | 12 ++-- examples/conan_test_package/conanfile.py | 2 - examples/conan_test_package_v2/conanfile.py | 2 - libs/dfi/gtest/src/dyn_type_tests.cpp | 2 +- libs/error_injector/CMakeLists.txt | 5 ++ .../error_injector/curl/CMakeLists.txt | 30 ++++----- libs/error_injector/curl/include/curl_ei.h | 36 +++++++++++ libs/error_injector/curl/src/curl_ei.cc | 31 ++++++++++ libs/framework/gtest/CMakeLists.txt | 15 +++++ .../CelixLauncherCurlErrorInjectionTestSuite.cc | 68 ++++++++++++++++++++ libs/framework/gtest/src/CelixLauncherTestSuite.cc | 2 +- libs/framework/include/celix/Trackers.h | 12 ++-- libs/framework/include/celix/dm/Component_Impl.h | 3 +- .../include/celix/dm/ServiceDependency_Impl.h | 7 ++- libs/framework/src/celix_launcher.c | 71 ++++++++++++++++++--- libs/framework/src/celix_libloader.c | 3 +- libs/promises/api/celix/Promise.h | 6 +- libs/utils/benchmark/src/StringHashmapBenchmark.cc | 2 +- 26 files changed, 318 insertions(+), 141 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 3ae2a876a..61bfd9eba 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -16,15 +16,17 @@ env: jobs: macos-build-conan: - runs-on: macOS-13 + runs-on: macOS-15 timeout-minutes: 120 steps: - name: Checkout source code uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c #v3.3.0 - - name: Install conan - run: | - brew install python ninja - pip3 install -U conan + - name: Set up Python + uses: actions/setup-python@7f4fc3e22c37d6ff65e88745f38bd3157c663f7c #v4.9.1 + with: + python-version: '3.x' + - name: Install Conan + run: pip install conan - name: Setup Conan Profile run: | conan profile detect -f @@ -70,7 +72,7 @@ jobs: source deactivate_conanrun.sh macos-build-brew: - runs-on: macOS-14 + runs-on: macOS-15 timeout-minutes: 120 steps: - name: Checkout source code diff --git a/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.cc b/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.cc index fc47bbcaa..b8ad63265 100644 --- a/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.cc +++ b/bundles/cxx_remote_services/discovery_configured/src/ConfiguredDiscoveryManager.cc @@ -19,29 +19,26 @@ #include <ConfiguredDiscoveryManager.h> -#include <optional> -#include <fstream> #include <filesystem> +#include <fstream> +#include <optional> #include <rapidjson/writer.h> -#define L_TRACE(...) \ - logHelper.trace(__VA_ARGS__); -#define L_DEBUG(...) \ - logHelper.debug(__VA_ARGS__); -#define L_INFO(...) \ - logHelper.info(__VA_ARGS__); -#define L_WARN(...) \ - logHelper.warning(__VA_ARGS__); -#define L_ERROR(...) \ - logHelper.error(__VA_ARGS__); +#define L_TRACE(...) logHelper.trace(__VA_ARGS__); +#define L_DEBUG(...) logHelper.debug(__VA_ARGS__); +#define L_INFO(...) logHelper.info(__VA_ARGS__); +#define L_WARN(...) logHelper.warning(__VA_ARGS__); +#define L_ERROR(...) logHelper.error(__VA_ARGS__); -static constexpr const char* ENDPOINT_ARRAY = "endpoints"; +#define ENDPOINT_ARRAY "endpoints" +namespace /*anon*/ +{ + static std::optional<std::string> readFile(const std::string& path) { - std::string contents; std::ifstream file(path); - if(!file) { + if (!file) { throw celix::rsa::RemoteServicesException{"Cannot open file"}; } file.seekg(0, std::ios::end); @@ -52,17 +49,18 @@ static std::optional<std::string> readFile(const std::string& path) { return contents; } -static rapidjson::Document parseJSONFile(std::string& contents) { - +static rapidjson::Document parseJSONFile(std::string& contents) { rapidjson::Document resultDocument{}; resultDocument.ParseInsitu(contents.data()); return resultDocument; } -celix::rsa::ConfiguredDiscoveryManager::ConfiguredDiscoveryManager(std::shared_ptr<celix::BundleContext> _ctx) : - ctx{std::move(_ctx)}, - configuredDiscoveryFiles{ctx->getConfigProperty(celix::rsa::CONFIGURED_DISCOVERY_DISCOVERY_FILES, "")}, - logHelper{ctx, celix::typeName<ConfiguredDiscoveryManager>()}{ +} // namespace + +celix::rsa::ConfiguredDiscoveryManager::ConfiguredDiscoveryManager(std::shared_ptr<celix::BundleContext> _ctx) + : ctx{std::move(_ctx)}, + configuredDiscoveryFiles{ctx->getConfigProperty(celix::rsa::CONFIGURED_DISCOVERY_DISCOVERY_FILES, "")}, + logHelper{ctx, celix::typeName<ConfiguredDiscoveryManager>()} { readConfiguredDiscoveryFiles(); } @@ -78,12 +76,13 @@ void celix::rsa::ConfiguredDiscoveryManager::readConfiguredDiscoveryFiles() { } } -celix::Properties celix::rsa::ConfiguredDiscoveryManager::convertToEndpointProperties(const rapidjson::Value &endpointJSON) { +celix::Properties +celix::rsa::ConfiguredDiscoveryManager::convertToEndpointProperties(const rapidjson::Value& endpointJSON) { celix::Properties result{}; result.set(celix::rsa::ENDPOINT_FRAMEWORK_UUID, ctx->getFramework()->getUUID()); for (auto it = endpointJSON.MemberBegin(); it != endpointJSON.MemberEnd(); ++it) { if (it->value.IsString()) { - if (celix_utils_stringEquals(it->name.GetString(), "endpoint.objectClass")) { //TODO improve + if (celix_utils_stringEquals(it->name.GetString(), "endpoint.objectClass")) { // TODO improve result.set(celix::SERVICE_NAME, it->value.GetString()); } else { result.set(it->name.GetString(), it->value.GetString()); @@ -97,11 +96,12 @@ celix::Properties celix::rsa::ConfiguredDiscoveryManager::convertToEndpointPrope strArray.append(entry.GetString()); strArray.append(","); } else { - L_WARN("Cannot parse endpoint member %s. Cannot parse array where the elements are not strings", it->name.GetString()); + L_WARN("Cannot parse endpoint member %s. Cannot parse array where the elements are not strings", + it->name.GetString()); continue; } } - result.set(it->name.GetString(), strArray.substr(0, strArray.size() -1 /*remote last ","*/)); + result.set(it->name.GetString(), strArray.substr(0, strArray.size() - 1 /*remote last ","*/)); } else { L_WARN("Cannot parse endpoint member %s. Type is %i", it->name.GetString(), (int)it->value.GetType()); } @@ -117,20 +117,23 @@ void celix::rsa::ConfiguredDiscoveryManager::addConfiguredDiscoveryFile(const st auto parsedJson = parseJSONFile(contents.value()); if (parsedJson.IsObject()) { if (parsedJson.HasMember(ENDPOINT_ARRAY) && parsedJson[ENDPOINT_ARRAY].IsArray()) { - - for (auto &jsonEndpoint : parsedJson[ENDPOINT_ARRAY].GetArray()) { + for (auto& jsonEndpoint : parsedJson[ENDPOINT_ARRAY].GetArray()) { try { auto endpointProperties = convertToEndpointProperties(jsonEndpoint); - auto endpointDescription = std::make_shared<EndpointDescription>( - std::move(endpointProperties)); - L_TRACE("Created endpoint description from %s: %s", path.c_str(), endpointDescription->toString().c_str()) - - auto reg = ctx->registerService<celix::rsa::EndpointDescription>( - std::move(endpointDescription)) + auto endpointDescription = + std::make_shared<EndpointDescription>(std::move(endpointProperties)); + L_TRACE("Created endpoint description from %s: %s", + path.c_str(), + endpointDescription->toString().c_str()) + + auto reg = + ctx->registerService<celix::rsa::EndpointDescription>(std::move(endpointDescription)) .build(); newEndpoints.emplace_back(std::move(reg)); } catch (celix::rsa::RemoteServicesException& e) { - L_ERROR("Error creating EndpointDescription from endpoints entry in JSON from path %s: %s", path.c_str(), e.what()); + L_ERROR("Error creating EndpointDescription from endpoints entry in JSON from path %s: %s", + path.c_str(), + e.what()); } } } @@ -158,4 +161,3 @@ std::vector<std::string> celix::rsa::ConfiguredDiscoveryManager::getConfiguredDi } return result; } - diff --git a/bundles/remote_services/remote_service_admin_dfi/gtest/CMakeLists.txt b/bundles/remote_services/remote_service_admin_dfi/gtest/CMakeLists.txt index 9d023c128..124158889 100644 --- a/bundles/remote_services/remote_service_admin_dfi/gtest/CMakeLists.txt +++ b/bundles/remote_services/remote_service_admin_dfi/gtest/CMakeLists.txt @@ -38,7 +38,6 @@ target_link_libraries(rsa_dfi_tst_bundle PRIVATE calculator_api remote_example_a target_include_directories(rsa_dfi_tst_bundle PRIVATE src) add_executable(test_rsa_dfi - src/main.cc src/rsa_tests.cc src/rsa_client_server_tests.cc ) @@ -48,13 +47,12 @@ celix_deprecated_framework_headers(test_rsa_dfi) target_link_libraries(test_rsa_dfi PRIVATE civetweb::civetweb - CURL::libcurl Celix::framework Celix::rsa_common calculator_api GTest::gtest + GTest::gtest_main libffi::libffi - ${LIBXML2_LIBRARIES} # work around memory leak reported by ASAN ) get_property(rsa_bundle_file TARGET rsa_dfi PROPERTY BUNDLE_FILE) diff --git a/bundles/remote_services/remote_service_admin_dfi/gtest/src/main.cc b/bundles/remote_services/remote_service_admin_dfi/gtest/src/main.cc deleted file mode 100644 index 9b7f7645d..000000000 --- a/bundles/remote_services/remote_service_admin_dfi/gtest/src/main.cc +++ /dev/null @@ -1,32 +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 <curl/curl.h> -#include <civetweb.h> -#include <gtest/gtest.h> - -int main(int argc, char **argv) { - curl_global_init(CURL_GLOBAL_ALL); - mg_init_library(MG_FEATURES_ALL); - ::testing::InitGoogleTest(&argc, argv); - int rc = RUN_ALL_TESTS(); - mg_exit_library(); - curl_global_cleanup(); - return rc; -} \ No newline at end of file diff --git a/bundles/shell/shell_tui/src/shell_tui.c b/bundles/shell/shell_tui/src/shell_tui.c index 1d8e78f83..e02a78d63 100644 --- a/bundles/shell/shell_tui/src/shell_tui.c +++ b/bundles/shell/shell_tui/src/shell_tui.c @@ -47,7 +47,7 @@ #define KEY_DEL1 '3' #define KEY_DEL2 '~' -const char * const SHELL_NOT_AVAILABLE_MSG = "[Shell TUI] Shell service not available."; +#define SHELL_NOT_AVAILABLE_MSG "[Shell TUI] Shell service not available." struct shell_tui { celix_bundle_context_t* ctx; diff --git a/cmake/Findcivetweb.cmake b/cmake/Findcivetweb.cmake index dc8aa91ec..1488e9b3e 100644 --- a/cmake/Findcivetweb.cmake +++ b/cmake/Findcivetweb.cmake @@ -18,16 +18,18 @@ find_package(civetweb CONFIG QUIET) if (NOT civetweb_FOUND) include(FetchContent) - set(CIVETWEB_ENABLE_WEBSOCKETS TRUE CACHE BOOL "" FORCE) - set(CIVETWEB_BUILD_TESTING FALSE CACHE BOOL "" FORCE) - set(BUILD_SHARED_LIBS TRUE CACHE BOOL "" FORCE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error") FetchContent_Declare( civetweb GIT_REPOSITORY https://github.com/civetweb/civetweb.git # GIT_REPOSITORY https://gitee.com/mirrors/civetweb.git GIT_TAG d7ba35bbb649209c66e582d5a0244ba988a15159 # V1.16 ) + + set(CIVETWEB_ENABLE_WEBSOCKETS TRUE CACHE BOOL "" FORCE) + set(CIVETWEB_BUILD_TESTING FALSE CACHE BOOL "" FORCE) + set(BUILD_SHARED_LIBS TRUE CACHE BOOL "" FORCE) + set(CMAKE_C_FLAGS "-Wno-error") + FetchContent_MakeAvailable(civetweb) if (NOT TARGET civetweb::civetweb) add_library(civetweb::civetweb ALIAS civetweb-c-library) diff --git a/cmake/celix_project/CelixProject.cmake b/cmake/celix_project/CelixProject.cmake index 96b6e1ea8..0f3132744 100644 --- a/cmake/celix_project/CelixProject.cmake +++ b/cmake/celix_project/CelixProject.cmake @@ -26,9 +26,13 @@ mark_as_advanced(CLEAR ENABLE_THREAD_SANITIZER) if (ENABLE_ADDRESS_SANITIZER) if("${CMAKE_C_COMPILER_ID}" MATCHES "Clang") + set(CMAKE_C_FLAGS "-DCELIX_ASAN_ENABLED ${CMAKE_C_FLAGS}") set(CMAKE_C_FLAGS "-shared-libasan -fsanitize=address -fno-omit-frame-pointer ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-shared-libasan -fsanitize=address -fno-omit-frame-pointer ${CMAKE_CXX_FLAGS}") - if (NOT APPLE) + if (APPLE) + set(CMAKE_EXE_LINKER_FLAGS "-fsanitize=address ${CMAKE_EXE_LINKER_FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "-fsanitize=address ${CMAKE_SHARED_LINKER_FLAGS}") + else () # Fix a linux clang deficiency where the ASan runtime library is not found automatically # Find the ASan runtime library path and set RPATH execute_process( @@ -51,16 +55,12 @@ if (ENABLE_ADDRESS_SANITIZER) endif() endif () elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + set(CMAKE_C_FLAGS "-DCELIX_ASAN_ENABLED ${CMAKE_C_FLAGS}") set(CMAKE_C_FLAGS "-lasan -fsanitize=address -fno-omit-frame-pointer ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-lasan -fsanitize=address -fno-omit-frame-pointer ${CMAKE_CXX_FLAGS}") else () message(WARNING "Address sanitizer is not supported for ${CMAKE_C_COMPILER_ID}") endif () - - if (ENABLE_TESTING) - set(CMAKE_C_FLAGS "-DCPPUTEST_MEM_LEAK_DETECTION_DISABLED ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "-DCPPUTEST_MEM_LEAK_DETECTION_DISABLED ${CMAKE_CXX_FLAGS}") - endif () endif() if (ENABLE_UNDEFINED_SANITIZER) diff --git a/cmake/cmake_celix/BundlePackaging.cmake b/cmake/cmake_celix/BundlePackaging.cmake index 9c1335e87..4200f390a 100644 --- a/cmake/cmake_celix/BundlePackaging.cmake +++ b/cmake/cmake_celix/BundlePackaging.cmake @@ -251,9 +251,7 @@ function(add_celix_bundle) "BUNDLE_TARGET" "${BUNDLE_TARGET_NAME}_bundle" ) target_link_libraries(${BUNDLE_TARGET_NAME} PRIVATE Celix::framework) - if(APPLE) - target_link_options(${BUNDLE_TARGET_NAME} PRIVATE "-Wl,-undefined,error") - else () + if (LINUX) target_link_options(${BUNDLE_TARGET_NAME} PRIVATE "-Wl,-z,defs") endif() else () diff --git a/conanfile.py b/conanfile.py index cfce0de63..30f6d190a 100644 --- a/conanfile.py +++ b/conanfile.py @@ -329,6 +329,8 @@ class CelixConan(ConanFile): self.requires("libzip/[>=1.7.3 <2.0.0]") if self.options.build_framework: self.requires("util-linux-libuuid/[>=2.39 <3.0.0]") + if self.settings.os == "Macos": + self.requires("gettext/0.21") #needed on MacOS 15 by libuuid if ((self.options.build_framework and self.options.framework_curlinit) or self.options.build_celix_etcdlib or self.options.build_rsa_discovery_common or self.options.build_rsa_remote_service_admin_dfi @@ -350,9 +352,9 @@ class CelixConan(ConanFile): # TODO: To be replaced with mdnsresponder/1790.80.10, resolve some problems of mdnsresponder # https://github.com/conan-io/conan-center-index/pull/16254 self.requires("mdnsresponder/1310.140.1") - # 'libzip/1.10.1' requires 'zlib/1.2.13' while 'libcurl/7.64.1' requires 'zlib/1.2.12' self.requires("openssl/[>=3.2.0]", override=True) - self.requires("zlib/1.2.13", override=True) + # Fix zlib to 1.3.1, 'libzip/1.10.1' and 'libcurl/7.64.1' requires different zlib versions causing conflicts + self.requires("zlib/1.3.1", override=True) if self.options.build_event_admin_remote_provider_mqtt: self.requires("mosquitto/[>=2.0.3 <3.0.0]") self.validate() @@ -369,13 +371,15 @@ class CelixConan(ConanFile): tc.cache_variables["BUILD_ERROR_INJECTOR_JANSSON"] = "ON" if "mosquitto" in lst: tc.cache_variables["BUILD_ERROR_INJECTOR_MOSQUITTO"] = "ON" + if "libcurl" in lst: + tc.cache_variables["BUILD_ERROR_INJECTOR_CURL"] = "ON" tc.cache_variables["CELIX_ERR_BUFFER_SIZE"] = str(self.options.celix_err_buffer_size) # tc.cache_variables["CMAKE_PROJECT_Celix_INCLUDE"] = os.path.join(self.build_folder, "conan_paths.cmake") # the following is workaround for https://github.com/conan-io/conan/issues/7192 if self.settings.os == "Linux": tc.cache_variables["CMAKE_EXE_LINKER_FLAGS"] = "-Wl,--unresolved-symbols=ignore-in-shared-libs" - elif self.settings.os == "Macos": - tc.cache_variables["CMAKE_EXE_LINKER_FLAGS"] = "-Wl,-undefined -Wl,dynamic_lookup" + #elif self.settings.os == "Macos": + # tc.cache_variables["CMAKE_EXE_LINKER_FLAGS"] = "-Wl,undefined -Wl,dynamic_lookup" v = Version(self.version) tc.cache_variables["CELIX_MAJOR"] = str(v.major.value) tc.cache_variables["CELIX_MINOR"] = str(v.minor.value) diff --git a/examples/conan_test_package/conanfile.py b/examples/conan_test_package/conanfile.py index 6db8716d1..bdcb51686 100644 --- a/examples/conan_test_package/conanfile.py +++ b/examples/conan_test_package/conanfile.py @@ -61,8 +61,6 @@ class TestPackageConan(ConanFile): # the following is workaround https://github.com/conan-io/conan/issues/7192 if self.settings.os == "Linux": cmake.definitions["CMAKE_EXE_LINKER_FLAGS"] = "-Wl,--unresolved-symbols=ignore-in-shared-libs" - elif self.settings.os == "Macos": - cmake.definitions["CMAKE_EXE_LINKER_FLAGS"] = "-Wl,-undefined -Wl,dynamic_lookup" cmake.configure() cmake.build() diff --git a/examples/conan_test_package_v2/conanfile.py b/examples/conan_test_package_v2/conanfile.py index bb202185c..0b57a9d8f 100644 --- a/examples/conan_test_package_v2/conanfile.py +++ b/examples/conan_test_package_v2/conanfile.py @@ -70,8 +70,6 @@ class TestPackageConan(ConanFile): # the following is workaround https://github.com/conan-io/conan/issues/7192 if self.settings.os == "Linux": tc.cache_variables["CMAKE_EXE_LINKER_FLAGS"] = "-Wl,--unresolved-symbols=ignore-in-shared-libs" - elif self.settings.os == "Macos": - tc.cache_variables["CMAKE_EXE_LINKER_FLAGS"] = "-Wl,-undefined -Wl,dynamic_lookup" tc.user_presets_path = False tc.generate() diff --git a/libs/dfi/gtest/src/dyn_type_tests.cpp b/libs/dfi/gtest/src/dyn_type_tests.cpp index 84b4c9c6a..7f6ef037e 100644 --- a/libs/dfi/gtest/src/dyn_type_tests.cpp +++ b/libs/dfi/gtest/src/dyn_type_tests.cpp @@ -37,7 +37,7 @@ extern "C" { int i; int j; int nrOfBurst = 10; - int burst = 50; + constexpr int burst = 50; void *pointers[burst]; for (j = 0; j < nrOfBurst; j += 1) { for (i = 0; i < burst ; i +=1 ) { diff --git a/libs/error_injector/CMakeLists.txt b/libs/error_injector/CMakeLists.txt index c34fd0d26..769f7bd67 100644 --- a/libs/error_injector/CMakeLists.txt +++ b/libs/error_injector/CMakeLists.txt @@ -53,3 +53,8 @@ celix_subproject(ERROR_INJECTOR_MOSQUITTO "Option to enable building the mosquit if (ERROR_INJECTOR_MOSQUITTO) add_subdirectory(mosquitto) endif () + +celix_subproject(ERROR_INJECTOR_CURL "Option to enable building the curl error injector" ON) +if (ERROR_INJECTOR_CURL) + add_subdirectory(curl) +endif () diff --git a/cmake/Findcivetweb.cmake b/libs/error_injector/curl/CMakeLists.txt similarity index 51% copy from cmake/Findcivetweb.cmake copy to libs/error_injector/curl/CMakeLists.txt index dc8aa91ec..6aeae0f8f 100644 --- a/cmake/Findcivetweb.cmake +++ b/libs/error_injector/curl/CMakeLists.txt @@ -15,21 +15,15 @@ # specific language governing permissions and limitations # under the License. -find_package(civetweb CONFIG QUIET) -if (NOT civetweb_FOUND) - include(FetchContent) - set(CIVETWEB_ENABLE_WEBSOCKETS TRUE CACHE BOOL "" FORCE) - set(CIVETWEB_BUILD_TESTING FALSE CACHE BOOL "" FORCE) - set(BUILD_SHARED_LIBS TRUE CACHE BOOL "" FORCE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error") - FetchContent_Declare( - civetweb - GIT_REPOSITORY https://github.com/civetweb/civetweb.git -# GIT_REPOSITORY https://gitee.com/mirrors/civetweb.git - GIT_TAG d7ba35bbb649209c66e582d5a0244ba988a15159 # V1.16 - ) - FetchContent_MakeAvailable(civetweb) - if (NOT TARGET civetweb::civetweb) - add_library(civetweb::civetweb ALIAS civetweb-c-library) - endif () -endif() +add_library(curl_ei STATIC src/curl_ei.cc) + +find_package(CURL REQUIRED) + +target_include_directories(curl_ei PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) +target_link_libraries(curl_ei PUBLIC Celix::error_injector CURL::libcurl) + +target_link_options(curl_ei INTERFACE + LINKER:--wrap,curl_global_init +) + +add_library(Celix::curl_ei ALIAS curl_ei) diff --git a/libs/error_injector/curl/include/curl_ei.h b/libs/error_injector/curl/include/curl_ei.h new file mode 100644 index 000000000..80ddad351 --- /dev/null +++ b/libs/error_injector/curl/include/curl_ei.h @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef CELIX_CURL_EI_H +#define CELIX_CURL_EI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <curl/curl.h> +#include "celix_error_injector.h" + +CELIX_EI_DECLARE(curl_global_init, CURLcode); + +#ifdef __cplusplus +} +#endif + +#endif //CELIX_CURL_EI_H diff --git a/libs/error_injector/curl/src/curl_ei.cc b/libs/error_injector/curl/src/curl_ei.cc new file mode 100644 index 000000000..d79b8974b --- /dev/null +++ b/libs/error_injector/curl/src/curl_ei.cc @@ -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 "curl_ei.h" + +extern "C" { + +CURLcode __real_curl_global_init(long flags); +CELIX_EI_DEFINE(curl_global_init, CURLcode) +CURLcode __wrap_curl_global_init(long flags) { + CELIX_EI_IMPL(curl_global_init); + return __real_curl_global_init(flags); +} + +} // extern "C" diff --git a/libs/framework/gtest/CMakeLists.txt b/libs/framework/gtest/CMakeLists.txt index 08e0c4038..662af0984 100644 --- a/libs/framework/gtest/CMakeLists.txt +++ b/libs/framework/gtest/CMakeLists.txt @@ -179,4 +179,19 @@ if (EI_TESTS) add_test(NAME test_framework_with_ei COMMAND test_framework_with_ei) setup_target_for_coverage(test_framework_with_ei SCAN_DIR ..) + + if (FRAMEWORK_CURLINIT) + #Note separate target, so that call to curl_global_init() can be intercepted (depends on global atomic counter) + add_executable(test_framework_with_curl_ei + src/CelixLauncherCurlErrorInjectionTestSuite.cc + ) + target_link_libraries(test_framework_with_curl_ei PRIVATE + framework_cut + Celix::curl_ei + GTest::gtest GTest::gtest_main + ) + + add_test(NAME test_framework_with_curl_ei COMMAND test_framework_with_curl_ei) + setup_target_for_coverage(test_framework_with_curl_ei SCAN_DIR ..) + endif () endif () diff --git a/libs/framework/gtest/src/CelixLauncherCurlErrorInjectionTestSuite.cc b/libs/framework/gtest/src/CelixLauncherCurlErrorInjectionTestSuite.cc new file mode 100644 index 000000000..12aa82670 --- /dev/null +++ b/libs/framework/gtest/src/CelixLauncherCurlErrorInjectionTestSuite.cc @@ -0,0 +1,68 @@ +/* + * 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 <gtest/gtest.h> + +#include "celix_launcher.h" +#include "curl_ei.h" + +#include <future> + +#define LAUNCH_WAIT_TIMEOUT_IN_MS 100 + + +class CelixLauncherCurlErrorInjectionTestSuite : public ::testing::Test { + public: + CelixLauncherCurlErrorInjectionTestSuite() { + celix_ei_expect_curl_global_init(nullptr, 0, CURLE_OK); + } + + static void launchCelixWithCurlInitError() { + //Given an error injection for curl_global_init from celix_launcher_launchAndWait + celix_ei_expect_curl_global_init((void*)celix_launcher_launchAndWait, 1, CURLE_FAILED_INIT); + + //When calling celix_launcher_launchAndWait + auto rc = celix_launcher_launchAndWait(0, nullptr, nullptr); + + //Then the expected error code should be returned + EXPECT_EQ(rc, 1); + } +}; + +TEST_F(CelixLauncherCurlErrorInjectionTestSuite, LaunchCelixWithCurlInitError) { + launchCelixWithCurlInitError(); + + //When launcher several times, the result should be the same + for (int i = 0; i < 5; ++i) { + launchCelixWithCurlInitError(); + } + + //When launching without an error injection + celix_ei_expect_curl_global_init((void*)nullptr, 0, CURLE_OK); + std::future<int> futureRc = std::async(std::launch::async, [] { + return celix_launcher_launchAndWait(0, nullptr, nullptr); + }); + + //And framework is given time to launch + futureRc.wait_for(std::chrono::milliseconds(LAUNCH_WAIT_TIMEOUT_IN_MS)); + + //Then the framework should be launched successfully, and return a 0 code after exiting + celix_launcher_triggerStop(); + EXPECT_EQ(futureRc.get(), 0); +} diff --git a/libs/framework/gtest/src/CelixLauncherTestSuite.cc b/libs/framework/gtest/src/CelixLauncherTestSuite.cc index 4d3a69c81..de57a0211 100644 --- a/libs/framework/gtest/src/CelixLauncherTestSuite.cc +++ b/libs/framework/gtest/src/CelixLauncherTestSuite.cc @@ -32,7 +32,7 @@ #include "celix_stdlib_cleanup.h" #include "celix_utils.h" -#define LAUNCH_WAIT_TIMEOUT 100 +#define LAUNCH_WAIT_TIMEOUT 200 class CelixLauncherTestSuite : public ::testing::Test { public: diff --git a/libs/framework/include/celix/Trackers.h b/libs/framework/include/celix/Trackers.h index 1521757e2..bccea25c1 100644 --- a/libs/framework/include/celix/Trackers.h +++ b/libs/framework/include/celix/Trackers.h @@ -383,7 +383,7 @@ namespace celix { */ template<typename F> size_t useServices(const F& f) { - return this->template useServicesInternal( + return this->useServicesInternal( [&f](I& svc, const celix::Properties&, const celix::Bundle&) { f(svc); }); } @@ -398,7 +398,7 @@ namespace celix { */ template<typename F> size_t useServicesWithProperties(const F& f) { - return this->template useServicesInternal( + return this->useServicesInternal( [&f](I& svc, const celix::Properties& props, const celix::Bundle&) { f(svc, props); }); } @@ -414,7 +414,7 @@ namespace celix { */ template<typename F> size_t useServicesWithOwner(const F& f) { - return this->template useServicesInternal( + return this->useServicesInternal( [&f](I& svc, const celix::Properties& props, const celix::Bundle& bnd) { f(svc, props, bnd); }); } @@ -429,7 +429,7 @@ namespace celix { */ template<typename F> bool useService(const F& f) { - return this->template useServiceInternal( + return this->useServiceInternal( [&f](I& svc, const celix::Properties&, const celix::Bundle&) { f(svc); }); } @@ -444,7 +444,7 @@ namespace celix { */ template<typename F> bool useServiceWithProperties(const F& f) { - return this->template useServiceInternal( + return this->useServiceInternal( [&f](I& svc, const celix::Properties& props, const celix::Bundle&) { f(svc, props); }); } @@ -460,7 +460,7 @@ namespace celix { */ template<typename F> bool useServiceWithOwner(const F& f) { - return this->template useServiceInternal( + return this->useServiceInternal( [&f](I& svc, const celix::Properties& props, const celix::Bundle& bnd) { f(svc, props, bnd); }); } protected: diff --git a/libs/framework/include/celix/dm/Component_Impl.h b/libs/framework/include/celix/dm/Component_Impl.h index 284f44660..f6b50ad6f 100644 --- a/libs/framework/include/celix/dm/Component_Impl.h +++ b/libs/framework/include/celix/dm/Component_Impl.h @@ -385,7 +385,8 @@ Component<T>& Component<T>::removeCallbacks() { template<class T> Component<T>& Component<T>::addContext(std::shared_ptr<void> context) { std::lock_guard<std::mutex> lock{mutex}; - componentContexts.template emplace_back(std::move(context)); + //Removed superfluous 'template' keyword which triggered '-Wmissing-template-arg-list-after-template-kw' + componentContexts.emplace_back(std::move(context)); return *this; } diff --git a/libs/framework/include/celix/dm/ServiceDependency_Impl.h b/libs/framework/include/celix/dm/ServiceDependency_Impl.h index 297e81fcd..7af3c1978 100644 --- a/libs/framework/include/celix/dm/ServiceDependency_Impl.h +++ b/libs/framework/include/celix/dm/ServiceDependency_Impl.h @@ -521,7 +521,7 @@ void ServiceDependency<T,I>::setupCallbacks() { auto svc = std::shared_ptr<I>{static_cast<I*>(rawSvc), [](I*){/*nop*/}}; auto svcId = props->getAsLong(celix::SERVICE_ID, -1); dep->addFpUsingSharedPtr(svc, props); - dep->addedServices.template emplace(svcId, std::make_pair(std::move(svc), std::move(props))); + dep->addedServices.emplace(svcId, std::make_pair(std::move(svc), std::move(props))); } return rc; }; @@ -541,8 +541,9 @@ void ServiceDependency<T,I>::setupCallbacks() { std::weak_ptr<const celix::Properties> removedProps = it->second.second; dep->removeFpUsingSharedPtr(it->second.first, it->second.second); dep->addedServices.erase(it); - dep->template waitForExpired(removedSvc, svcId, "service pointer"); - dep->template waitForExpired(removedProps, svcId, "service properties"); + //Removed superfluous 'template' keyword (no explicit template args supplied) + dep->waitForExpired(removedSvc, svcId, "service pointer"); + dep->waitForExpired(removedProps, svcId, "service properties"); } } return rc; diff --git a/libs/framework/src/celix_launcher.c b/libs/framework/src/celix_launcher.c index ab6832943..7966fedcd 100644 --- a/libs/framework/src/celix_launcher.c +++ b/libs/framework/src/celix_launcher.c @@ -28,6 +28,7 @@ #include <libgen.h> #ifndef CELIX_NO_CURLINIT +#include <stdbool.h> #include <curl/curl.h> #endif @@ -127,6 +128,33 @@ static celix_status_t celix_launcher_createBundleCache(celix_properties_t* embed */ static celix_status_t celix_launcher_loadRuntimeProperties(const char* configFile, celix_properties_t** outConfigProperties); +#ifndef CELIX_NO_CURLINIT +/** + * @brief Initializes the CURL library if it has not been initialized yet. + * + * This function ensures that the CURL initialization function is + * called only once, regardless of how many times a celix framework is launched. + * + * @return CELIX_SUCCESS if CURL was initialized successfully, or + * CELIX_ILLEGAL_STATE if curl initialization failed. + */ +static celix_status_t celix_launcher_initializeCurl(); + +/** + * @brief Cleans up the CURL library if it was previously initialized. + * + * This function is called with __attribute__(destructor) to ensure that the + * CURL cleanup function is called only once, regardless of how many times + * a (global) launched celix framework is stopped and started again. + */ +static void celix_launcher_cleanupCurl() __attribute__((destructor)); + +/** + * @brief CURL initialization bool, used to check if CURL has been initialized. + */ +static bool g_curl_initialized = 0; +#endif + /** * @brief Set the global framework instance. */ @@ -186,12 +214,18 @@ int celix_launcher_launchAndWait(int argc, char* argv[], const char* embeddedCon celix_bundleContext_log(celix_framework_getFrameworkContext(framework), CELIX_LOG_LEVEL_WARNING, "Failed to schedule celix_shutdown_check"); } - celix_framework_waitForStop(framework); - celix_launcher_resetLauncher(); + #ifndef CELIX_NO_CURLINIT - // Cleanup Curl - curl_global_cleanup(); + status = celix_launcher_initializeCurl(); + if (status != CELIX_SUCCESS) { + celix_launcher_resetLauncher(); + return CELIX_LAUNCHER_ERROR_EXIT_CODE; + } #endif + + celix_framework_waitForStop(framework); + celix_launcher_resetLauncher(); + return CELIX_LAUNCHER_OK_EXIT_CODE; } @@ -249,11 +283,6 @@ static celix_status_t celix_launcher_createFramework(celix_properties_t* embedde sigaction(SIGUSR1, &sigact, NULL); sigaction(SIGUSR2, &sigact, NULL); -#ifndef CELIX_NO_CURLINIT - // Before doing anything else, lets setup Curl - curl_global_init(CURL_GLOBAL_ALL); -#endif - *frameworkOut = celix_frameworkFactory_createFramework(embeddedProps); return *frameworkOut != NULL ? CELIX_SUCCESS : CELIX_FRAMEWORK_EXCEPTION; } @@ -449,3 +478,27 @@ static void celix_launcher_resetLauncher() { } g_launcher.launched = false; } + +#ifndef CELIX_NO_CURLINIT +celix_status_t celix_launcher_initializeCurl() { + bool alreadyInitialized = __atomic_exchange_n(&g_curl_initialized, true, __ATOMIC_SEQ_CST); + if (alreadyInitialized) { + return CELIX_SUCCESS; + } + CURLcode cc = curl_global_init(CURL_GLOBAL_DEFAULT); + if (cc != CURLE_OK) { + fprintf(stderr, "Failed to initialize Curl: %s\n", curl_easy_strerror(cc)); + //note only 1 framework can be launcher with celix launcher and the launcher is set, so no startup race + __atomic_store_n(&g_curl_initialized, false, __ATOMIC_SEQ_CST); + return CELIX_ILLEGAL_STATE; + } + return CELIX_SUCCESS; +} + +static void celix_launcher_cleanupCurl() { + bool wasInitialized = __atomic_load_n(&g_curl_initialized, __ATOMIC_SEQ_CST); + if (wasInitialized) { + curl_global_cleanup(); + } +} +#endif diff --git a/libs/framework/src/celix_libloader.c b/libs/framework/src/celix_libloader.c index f11053bff..e0d539c01 100644 --- a/libs/framework/src/celix_libloader.c +++ b/libs/framework/src/celix_libloader.c @@ -24,11 +24,12 @@ celix_library_handle_t* celix_libloader_open(celix_bundle_context_t *ctx, const char *libPath) { bool defaultNoDelete = true; -#if defined(NDEBUG) +#if defined(NDEBUG) && !defined(CELIX_ASAN_ENABLED) defaultNoDelete = false; #endif celix_library_handle_t* handle = NULL; bool noDelete = celix_bundleContext_getPropertyAsBool(ctx, CELIX_LOAD_BUNDLES_WITH_NODELETE, defaultNoDelete); + int flags = RTLD_NOW|RTLD_LOCAL; if (noDelete) { flags = RTLD_NOW|RTLD_LOCAL|RTLD_NODELETE; diff --git a/libs/promises/api/celix/Promise.h b/libs/promises/api/celix/Promise.h index f16999243..e4481ce51 100644 --- a/libs/promises/api/celix/Promise.h +++ b/libs/promises/api/celix/Promise.h @@ -659,13 +659,15 @@ inline celix::Promise<void> celix::Promise<void>::timeout(std::chrono::duration< template<typename T> template<typename Rep, typename Period> inline celix::Promise<T>& celix::Promise<T>::setTimeout(std::chrono::duration<Rep, Period> duration) { - state->template setTimeout(duration); + //Removed superfluous 'template' keyword (no explicit template args supplied) + state->setTimeout(duration); return *this; } template<typename Rep, typename Period> inline celix::Promise<void>& celix::Promise<void>::setTimeout(std::chrono::duration<Rep, Period> duration) { - state->template setTimeout(duration); + //Removed superfluous 'template' keyword (no explicit template args supplied) + state->setTimeout(duration); return *this; } diff --git a/libs/utils/benchmark/src/StringHashmapBenchmark.cc b/libs/utils/benchmark/src/StringHashmapBenchmark.cc index 4552dd044..7ecfb81e0 100644 --- a/libs/utils/benchmark/src/StringHashmapBenchmark.cc +++ b/libs/utils/benchmark/src/StringHashmapBenchmark.cc @@ -100,7 +100,7 @@ public: return valDistribution(generator); } - const int MAX_LEN = 100; + static constexpr int MAX_LEN = 100; std::default_random_engine generator{}; std::uniform_int_distribution<int> lenDistribution{1,MAX_LEN}; std::uniform_int_distribution<int> charDistribution{'a','z'};
