From 32f42837b77707416c0e4d24413d518f2b37cbf2 Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Mon, 3 Mar 2025 09:47:21 +0200 Subject: [PATCH 01/12] Use repository and release tag for googletest --- googletest/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/googletest/CMakeLists.txt b/googletest/CMakeLists.txt index f3e8ec3..f53cf85 100644 --- a/googletest/CMakeLists.txt +++ b/googletest/CMakeLists.txt @@ -12,7 +12,9 @@ include(FetchContent) set(FETCHCONTENT_QUIET FALSE) FetchContent_Declare( googletest - URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG release-1.16.0 + # URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip ) # For Windows: Prevent overriding the parent project's compiler/linker settings From 5301cf02d4ca0a3453f9fb336afd6ca47efac955 Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Mon, 3 Mar 2025 09:48:48 +0200 Subject: [PATCH 02/12] Move googletest CMakeLists.txt to cmake folder --- {googletest => cmake}/CMakeLists.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {googletest => cmake}/CMakeLists.txt (100%) diff --git a/googletest/CMakeLists.txt b/cmake/CMakeLists.txt similarity index 100% rename from googletest/CMakeLists.txt rename to cmake/CMakeLists.txt From 77d8cc10f742bdd32f970c7539168a579c013d40 Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Mon, 3 Mar 2025 09:49:46 +0200 Subject: [PATCH 03/12] Rename googletest cmake --- cmake/{CMakeLists.txt => googletest.cmake} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cmake/{CMakeLists.txt => googletest.cmake} (100%) diff --git a/cmake/CMakeLists.txt b/cmake/googletest.cmake similarity index 100% rename from cmake/CMakeLists.txt rename to cmake/googletest.cmake From e5258f58da86fd33c7a968a81d5eb572c8d6ea3a Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Tue, 11 Mar 2025 08:54:39 +0200 Subject: [PATCH 04/12] Refactoring for Windows MSBuild Use generic template to support wchar_t via UNICODE definition. Reorganize cmake into scripts. Add options to build example and tests. Build googletest as static library. Remove install step for log4cplus by copying dll to target dir. --- CMakeLists.txt | 35 ++++------- cmake/common.cmake | 15 +++++ cmake/googletest.cmake | 15 +++-- cmake/log4cplus.cmake | 38 ++++++++++++ example/CMakeLists.txt | 32 ++++++++++ {src => example}/log.conf | 0 example/main.cpp | 40 +++++++++++++ interfaces/ILogger.h | 24 ++++---- interfaces/LoggerLog4Cplus.h | 26 +++++---- log4cplus/CMakeLists.txt | 26 --------- src/CMakeLists.txt | 43 ++++---------- src/Factory.cpp | 23 +++++++- src/Factory.h | 2 +- src/Logger.h | 66 +++++++++++++++------ src/main.cpp | 24 -------- test/LoggerTest.cpp | 109 ----------------------------------- tests/CMakeLists.txt | 26 +++++++++ tests/LoggerTest.cpp | 108 ++++++++++++++++++++++++++++++++++ {test => tests}/TestMain.cpp | 2 - 19 files changed, 388 insertions(+), 266 deletions(-) create mode 100644 cmake/common.cmake create mode 100644 cmake/log4cplus.cmake create mode 100644 example/CMakeLists.txt rename {src => example}/log.conf (100%) create mode 100644 example/main.cpp delete mode 100644 log4cplus/CMakeLists.txt delete mode 100644 src/main.cpp delete mode 100644 test/LoggerTest.cpp create mode 100644 tests/CMakeLists.txt create mode 100644 tests/LoggerTest.cpp rename {test => tests}/TestMain.cpp (88%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46dd6ef..dc71cf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.3.2) +cmake_minimum_required(VERSION 3.5) project(skeleton VERSION 0.1 @@ -6,29 +6,18 @@ project(skeleton LANGUAGES CXX ) -include(CTest) +option(SKELETON_BUILD_TESTS "Build test program" ON) +option(SKELETON_BUILD_EXAMPLE "Build example" ON) -set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") +# Override - Build with tchar = wchar_t +set(UNICODE TRUE) -include(FetchContent) -set(FETCHCONTENT_QUIET FALSE) -FetchContent_Declare( - googletest - URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip -) - -# For Windows: Prevent overriding the parent project's compiler/linker settings -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +add_subdirectory(src) -FetchContent_MakeAvailable(googletest) +if (SKELETON_BUILD_EXAMPLE) + add_subdirectory(example) +endif (SKELETON_BUILD_EXAMPLE) -#include(DownloadProject.cmake) -#download_project(PROJ googletest -# GIT_TAG release-1.10.0 -# GIT_REPOSITORY https://github.com/google/googletest -# PREFIX .cmakeDownload -# BINARY_DIR ${CMAKE_BINARY_DIR}/googletest-build -# ${UPDATE_DISCONNECTED_IF_AVAILABLE} -#) -add_subdirectory(log4cplus) -add_subdirectory(src) +if (SKELETON_BUILD_TESTS) + add_subdirectory(tests) +endif (SKELETON_BUILD_TESTS) \ No newline at end of file diff --git a/cmake/common.cmake b/cmake/common.cmake new file mode 100644 index 0000000..64ae71b --- /dev/null +++ b/cmake/common.cmake @@ -0,0 +1,15 @@ +include(CMakePrintHelpers) + +macro (print_target_properties NAME) + cmake_print_properties( + TARGETS ${NAME} + PROPERTIES ${ARGN} + ) +endmacro() + +macro(copy_target_runtime_dlls NAME) + add_custom_command(TARGET ${NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ $ + COMMAND_EXPAND_LISTS + ) +endmacro() \ No newline at end of file diff --git a/cmake/googletest.cmake b/cmake/googletest.cmake index f53cf85..c6f80bb 100644 --- a/cmake/googletest.cmake +++ b/cmake/googletest.cmake @@ -1,23 +1,22 @@ -cmake_minimum_required(VERSION 3.3.2) +cmake_minimum_required(VERSION 3.5) # A separate subdirectory for building Google Test so that C++ is not # globally enabled enable_language(CXX) -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED YES) -set(CMAKE_CXX_EXTENSIONS OFF) include(FetchContent) set(FETCHCONTENT_QUIET FALSE) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.16.0 + GIT_TAG v1.16.0 # URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip ) -# For Windows: Prevent overriding the parent project's compiler/linker settings -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +set (BUILD_SHARED_LIBS FALSE) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED YES) +set(CMAKE_CXX_EXTENSIONS OFF) -FetchContent_MakeAvailable(googletest) \ No newline at end of file +FetchContent_MakeAvailable(googletest) diff --git a/cmake/log4cplus.cmake b/cmake/log4cplus.cmake new file mode 100644 index 0000000..0a4888c --- /dev/null +++ b/cmake/log4cplus.cmake @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.5) + +set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") + +include(FetchContent) +set(FETCHCONTENT_QUIET FALSE) + +FetchContent_Declare(log4cplus + GIT_REPOSITORY https://github.com/log4cplus/log4cplus.git + GIT_TAG 2.1.x + GIT_PROGRESS TRUE +) + +# TODO: Check does the linux build need this. +# macro(FetchContent_MakeAvailableWithConfigure NAME) +# FetchContent_GetProperties(${NAME}) +# if(NOT ${NAME}_POPULATED) +# FetchContent_Populate(${NAME}) +# add_custom_command ( +# OUTPUT ${${NAME}_SOURCE_DIR}/include/log4cplus/config/defines.hxx +# COMMAND ${${NAME}_SOURCE_DIR}/configure +# WORKING_DIRECTORY ${${NAME}_SOURCE_DIR}/) +# add_custom_target(${NAME}_configure ALL DEPENDS ${${NAME}_SOURCE_DIR}/include/log4cplus/config/defines.hxx) +# add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR}) +# endif() +# endmacro() + +FetchContent_MakeAvailable(log4cplus) +# if (UNICODE) +# add_compile_definitions (UNICODE _UNICODE) +# add_definitions (-UMBCS -U_MBCS) +# endif (UNICODE) + +# add_compile_definitions (log4cplus_EXPORTS) +print_target_properties(log4cplus::log4cplus + LINK_LIBRARIES + COMPILE_DEFINITIONS + ) \ No newline at end of file diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..7742258 --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,32 @@ +# cmake list called from root/CMakeLists.txt +# requires: skeleton-lib to be available +cmake_minimum_required(VERSION 3.5) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) + +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/common.cmake) + +# TODO: relocate includes to appropriate folders +include_directories(${CMAKE_CURRENT_LIST_DIR}/.. ${CMAKE_CURRENT_LIST_DIR}/../src) + +# create custom target copying the logger configuration +# to the binary folder +set (conf_FILE "log.conf") + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${conf_FILE}" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_LIST_DIR}/${conf_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${conf_FILE}" + DEPENDS "${CMAKE_CURRENT_LIST_DIR}/${conf_FILE}" +) + +add_custom_target(configuration ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${conf_FILE}") + +# skeleton example executable +add_executable(${PROJECT_NAME}-example ${CMAKE_CURRENT_LIST_DIR}/main.cpp) +target_include_directories(${PROJECT_NAME}-example PUBLIC ${log4cplus_SOURCE_DIR}/include) +target_link_libraries(${PROJECT_NAME}-example ${log4cplus} ${PROJECT_NAME}-lib) +copy_target_runtime_dlls(${PROJECT_NAME}-example) +install(TARGETS ${PROJECT_NAME}-example RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +print_target_properties(${PROJECT_NAME}-example COMPILE_DEFINITIONS) + diff --git a/src/log.conf b/example/log.conf similarity index 100% rename from src/log.conf rename to example/log.conf diff --git a/example/main.cpp b/example/main.cpp new file mode 100644 index 0000000..c199879 --- /dev/null +++ b/example/main.cpp @@ -0,0 +1,40 @@ +/** + * @file main.cpp + * @author Tuomas Lahtinen (tuomas123lahtinen@gmail.com) + * @brief + * @version 0.1 + * @date 2023-04-03 + * + * @copyright Copyright (c) 2023 + * + */ + +#include "Factory.h" +#include "Logger.h" + +#include +#include +#include +#include + +#include + +int main(int argc, char **argv) +{ + #ifdef UNICODE + std::cout << __builtin_FILE() << " - UNICODE" << std::endl; + #else + std::cout << __builtin_FILE() << " - NOT UNICODE" << std::endl; + #endif + + auto logger = skeleton::factory::getLogger("main"); + std::cout << "logger instance @" << logger.get() << std::endl; + auto same = skeleton::factory::getLogger("main"); + std::cout << "logger instance @" << logger.get() << "==" << same.get() << std::endl; + logger->configure("../log.conf"); + logger->debug("debug message"); + logger->warn("warning message"); + logger->info("info message"); + + return 0; +} diff --git a/interfaces/ILogger.h b/interfaces/ILogger.h index e9739ad..1467e3d 100644 --- a/interfaces/ILogger.h +++ b/interfaces/ILogger.h @@ -14,17 +14,19 @@ namespace skeleton::interfaces { +enum class LogLevels +{ + TRACE=0, + DEBUG, + INFO, + WARN, + ERROR, + COUNT +}; + +template struct ILogger { - enum class LogLevels - { - TRACE=0, - DEBUG, - INFO, - WARN, - ERROR, - COUNT - }; virtual ~ILogger() = default; /// @brief log function prototype /// @param logger logger name (facility) @@ -33,12 +35,12 @@ struct ILogger /// @param file grabbed log location details /// @param line grabbed log location details /// @param function grabbed log location details - virtual void log(const char* logger, LogLevels level, const std::string &message, const char* file, int line, const char* function) = 0; + virtual void log(const TChar* logger, LogLevels level, const std::basic_string, std::allocator> &message, const char* file, int line, const char* function) = 0; /// @brief configure functionality is implementation dependent /// e.g. log4cplus it is required that it is called at least once /// @param filename - virtual void configure(const std::string &filename) = 0; + virtual void configure(const TChar* const filename) = 0; }; } \ No newline at end of file diff --git a/interfaces/LoggerLog4Cplus.h b/interfaces/LoggerLog4Cplus.h index e59af73..f7e201f 100644 --- a/interfaces/LoggerLog4Cplus.h +++ b/interfaces/LoggerLog4Cplus.h @@ -19,32 +19,38 @@ namespace skeleton::implementations { - -class Log4CplusLogger: public interfaces::ILogger +template +class Log4CplusLogger: public interfaces::ILogger { + log4cplus::Initializer initializer; + using LogLevels = interfaces::LogLevels; + using _Mystr = std::basic_string, std::allocator>; + using _Myss = std::basic_stringstream, std::allocator>; public: - Log4CplusLogger() {} + Log4CplusLogger() { } - ~Log4CplusLogger() {std::cout << "destroyed" << std::endl;} + ~Log4CplusLogger() + { + std::cout << "destroyed" << std::endl; + } - void log(const char* logger, interfaces::ILogger::LogLevels level, const std::string &message, const char* file, int line, const char* function) override + void log(const TChar* logger, LogLevels level, const _Mystr &message, const char* file, int line, const char* function) override { - log4cplus::Logger instance(log4cplus::Logger::getInstance(LOG4CPLUS_TEXT(logger))); + log4cplus::Logger instance(log4cplus::Logger::getInstance(_Mystr(logger))); instance.log(m_logLevels.at(level), message, file, line, function); } - void configure(const std::string &file) + void configure(const TChar* const file) { - using namespace log4cplus; std::ifstream ifs(file); if (ifs.good()) { - PropertyConfigurator config(file); + log4cplus::PropertyConfigurator config(file); config.configure(); } else { - BasicConfigurator config; + log4cplus::BasicConfigurator config; config.configure(); } } diff --git a/log4cplus/CMakeLists.txt b/log4cplus/CMakeLists.txt deleted file mode 100644 index e28f098..0000000 --- a/log4cplus/CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -cmake_minimum_required(VERSION 3.3.2) - -set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") - -include(FetchContent) -set(FETCHCONTENT_QUIET FALSE) - -FetchContent_Declare(log4cplus - GIT_REPOSITORY https://github.com/log4cplus/log4cplus.git - GIT_TAG REL_2_0_6 - GIT_PROGRESS TRUE -) - -macro(FetchContent_MakeAvailableWithConfigure NAME) - FetchContent_GetProperties(${NAME}) - if(NOT ${NAME}_POPULATED) - FetchContent_Populate(${NAME}) - add_custom_command ( OUTPUT ${${NAME}_SOURCE_DIR}/include/log4cplus/config/defines.hxx - COMMAND ${${NAME}_SOURCE_DIR}/configure - WORKING_DIRECTORY ${${NAME}_SOURCE_DIR}/) - add_custom_target(${NAME}_configure ALL DEPENDS ${${NAME}_SOURCE_DIR}/include/log4cplus/config/defines.hxx -) - add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR}) - endif() -endmacro() -FetchContent_MakeAvailableWithConfigure(log4cplus) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 367d2c2..6d12b12 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,45 +1,22 @@ -cmake_minimum_required(VERSION 3.3.2) +cmake_minimum_required(VERSION 3.5) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED YES) -# Main source directory -include_directories(${CMAKE_CURRENT_LIST_DIR}/..) - -# create custom target copying the logger configuration -# to the binary folder -set (conf_FILE log.conf) -add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${conf_FILE}" - COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_LIST_DIR}/${conf_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${conf_FILE}" - DEPENDS "${CMAKE_CURRENT_LIST_DIR}/${conf_FILE}" -) -add_custom_target(configuration ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/log.conf") +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/common.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/log4cplus.cmake) list(APPEND SourceFiles ${CMAKE_CURRENT_LIST_DIR}/Factory.cpp ) -list(APPEND TestFiles - ${CMAKE_CURRENT_LIST_DIR}/../test/TestLogging.cpp -) - -# skeleton main executable -add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR}/main.cpp ${SourceFiles}) -message(${log4cplus_SOURCE_DIR}/include) -target_include_directories(${PROJECT_NAME} PUBLIC ${log4cplus_SOURCE_DIR}/include) -target_link_libraries(${PROJECT_NAME} PRIVATE log4cplus) - # skeleton sources as library -add_library(${PROJECT_NAME}-lib ${SourceFiles}) -target_include_directories(${PROJECT_NAME}-lib PUBLIC ${CMAKE_CURRENT_LIST_DIR}/.. ${log4cplus_SOURCE_DIR}/include) -target_link_libraries(${PROJECT_NAME}-lib PRIVATE log4cplus) +add_library(${PROJECT_NAME}-lib STATIC ${SourceFiles}) -# skeleton test executable -add_executable(${PROJECT_NAME}-test ${CMAKE_CURRENT_LIST_DIR}/../test/TestMain.cpp) -target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}-lib gtest gmock_main) -target_include_directories(${PROJECT_NAME}-test PUBLIC ${log4cplus_SOURCE_DIR}/include) +if (MSVC) + target_compile_definitions(${PROJECT_NAME}-lib PRIVATE "" _CRT_SECURE_NO_WARNINGS) +endif() -add_test(NAME ${PROJECT_NAME}-test COMMAND ${PROJECT_NAME}-test) - -install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +target_include_directories(${PROJECT_NAME}-lib PUBLIC ${CMAKE_CURRENT_LIST_DIR}/.. ${log4cplus_SOURCE_DIR}/include) +target_link_libraries(${PROJECT_NAME}-lib log4cplus::log4cplus) +print_target_properties(${PROJECT_NAME}-lib COMPILE_DEFINITIONS) diff --git a/src/Factory.cpp b/src/Factory.cpp index cc23645..7309f9b 100644 --- a/src/Factory.cpp +++ b/src/Factory.cpp @@ -15,9 +15,28 @@ namespace skeleton::factory { -template <> std::unique_ptr getDefault(const char* str) +template <> std::shared_ptr getLogger(const char* name) { - return std::unique_ptr(new logging::Logger(str, std::make_unique())); + static std::map> loggers; + std::shared_ptr p = nullptr; + + for (auto [key, value] : loggers) + { + if (strcmp(name, key) == 0) + { + std::cout << "use found logger" << std::endl; + p = value.lock(); + } + } + + if (p == nullptr) + { + std::cout << "create logger" << std::endl; + p = std::make_shared(name, std::make_unique>()); + loggers.insert_or_assign(name, std::weak_ptr(p)); + } + + return p; } } // skeleton::factory diff --git a/src/Factory.h b/src/Factory.h index e95e743..8912c16 100644 --- a/src/Factory.h +++ b/src/Factory.h @@ -14,5 +14,5 @@ namespace skeleton::factory { // Get a default implementation of class - template std::unique_ptr getDefault(D); + template std::shared_ptr getLogger(D); } // namespace skeleton::factory diff --git a/src/Logger.h b/src/Logger.h index 27e470a..474d532 100644 --- a/src/Logger.h +++ b/src/Logger.h @@ -11,31 +11,53 @@ #pragma once #include "interfaces/ILogger.h" #include +#include #include namespace skeleton::logging { -#if USE_LOG_MACRO - // LOG macro - // declare 'const char* logger = "somemodule"' in the user file - #define LOG(level, logEvent) \ - do { std::stringstream _s; _s << logEvent; log4cplus::Logger::getInstance(LOG4CPLUS_TEXT(logger)).log(level, _s.str()); } while(false) -#endif +#if (UNICODE) +# define TCHAR wchar_t +# define TOLOGSTR(a) (const TCHAR*)L""##a +#else (UNICODE) +# define TCHAR char +# define TOLOGSTR(a) a +#endif (UNICODE) + +#define TSTR std::basic_string, std::allocator> +#define TOLOGTSTR(a) TSTR(TOLOGSTR(a)) /// @brief Logger class provides functions for application logging class Logger { #define logwrapper_ARGS_CAPTURE const char *file = __builtin_FILE(), int line = __builtin_LINE(), const char *function = __builtin_FUNCTION() #define logwrapper_CAPTURED_ARGS file, line, function - using Levels = interfaces::ILogger::LogLevels; + using Levels = interfaces::LogLevels; + using _Mystr = TSTR; + using _Myss = std::basic_stringstream, std::allocator>; public: - Logger(const char *name, std::unique_ptr logger): - m_facility(name), - m_logimpl(std::move(logger)) - {} + Logger(const char *name, std::shared_ptr> logger): + m_logimpl(logger) + { + + size_t newsize = strlen(name) + 1; + #ifdef UNICODE + m_facility = std::make_unique(newsize); + // Convert char* string to a wchar_t* string. + size_t convertedChars = 0; + mbstowcs_s(&convertedChars, m_facility.get(), newsize, name, _TRUNCATE); + #else + m_facility = std::make_unique(newsize); + strcpy(m_facility.get(), name); + #endif + } + ~Logger() + { + m_logimpl.reset(); + } Logger() = delete; Logger(const Logger&) = delete; Logger& operator=(const Logger&) = delete; @@ -82,20 +104,30 @@ class Logger template void log(Levels level, const T &output, logwrapper_ARGS_CAPTURE) { - std::stringstream ss(output); - m_logimpl->log(m_facility, level, ss.str().c_str(), file, line, function); + _Myss ss; + ss << output; + m_logimpl->log(m_facility.get(), level, ss.str(), file, line, function); } /// @brief implementation dependent configure /// @param configfile - void configure(const std::string &configfile = "") + void configure(const char* const configFile = "") { - m_logimpl->configure(configfile); + #ifdef UNICODE + size_t newsize = strlen(configFile) + 1; + std::unique_ptr converted = std::make_unique(newsize); + // Convert char* string to a wchar_t* string. + size_t convertedChars = 0; + mbstowcs_s(&convertedChars, converted.get(), newsize, configFile, _TRUNCATE); + m_logimpl->configure(converted.get()); + #else + m_logimpl->configure(configFile); + #endif } private: - const char *m_facility{nullptr}; - std::unique_ptr m_logimpl{nullptr}; + std::shared_ptr m_facility{nullptr}; + std::shared_ptr> m_logimpl{nullptr}; }; } // namespace skeleton::logging \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 8b61bbc..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @file main.cpp - * @author Tuomas Lahtinen (tuomas123lahtinen@gmail.com) - * @brief - * @version 0.1 - * @date 2023-04-03 - * - * @copyright Copyright (c) 2023 - * - */ - - -#include "Factory.h" -#include "Logger.h" - -int main(int argc, char **argv) -{ - auto logger = skeleton::factory::getDefault("main"); - logger->configure("log.conf"); - logger->debug("debug message"); - logger->warn("warning message"); - logger->info("info message"); - return 0; -} diff --git a/test/LoggerTest.cpp b/test/LoggerTest.cpp deleted file mode 100644 index ab769a3..0000000 --- a/test/LoggerTest.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/** - * @file LoggerTest.cpp - * @author Tuomas Lahtinen (tuomas123lahtinen@gmail.com) - * @brief - * @version 0.1 - * @date 2023-04-03 - * - * @copyright Copyright (c) 2023 - * - */ - -#include "../interfaces/ILogger.h" -#include "../src/Logger.h" - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include -#include - -namespace skeleton::testing -{ -class MockLogger : public interfaces::ILogger -{ -public: - MOCK_METHOD(void, configure, (const std::string& filename), (override)); - MOCK_METHOD(void, log, ( - const char* logger, - interfaces::ILogger::LogLevels level, - const std::string &message, - const char* file, - int line, - const char* function - ), (override)); -}; - -class LoggerTest: public ::testing::Test -{ - protected: - void SetUp() override - { - auto mock = std::unique_ptr(new MockLogger()); - p_mock = static_cast(mock.get()); - m_logger = std::unique_ptr(new logging::Logger("mocked", std::move(mock))); - } - - void TearDown() override - { - m_logger.reset(); - } - - std::unique_ptr m_logger; - MockLogger* p_mock{nullptr}; -}; - -TEST_F(LoggerTest, LogAllLevels_ExpectLocationAndMessage) -{ - using ::testing::_; - using ::testing::InSequence; - using levels = skeleton::interfaces::ILogger::LogLevels; - InSequence seq; - std::stringstream ss(); - auto file = __builtin_FILE(); - auto function = __builtin_FUNCTION(); - auto line = __builtin_LINE(); - EXPECT_CALL(*p_mock, log("mocked", levels::TRACE, "trace", file, ++line + 5, function)).Times(1); - EXPECT_CALL(*p_mock, log("mocked", levels::DEBUG, "debug", file, ++line + 5, function)).Times(1); - EXPECT_CALL(*p_mock, log("mocked", levels::INFO, "info", file, ++line + 5, function)).Times(1); - EXPECT_CALL(*p_mock, log("mocked", levels::WARN, "warn", file, ++line + 5, function)).Times(1); - EXPECT_CALL(*p_mock, log("mocked", levels::ERROR, "error", file, ++line + 5, function)).Times(1); - m_logger->trace("trace"); - m_logger->debug("debug"); - m_logger->info("info"); - m_logger->warn("warn"); - m_logger->error("error"); -} - -TEST_F(LoggerTest, LogAllLevels_ExpectEachLevelCalledOnce) -{ - using ::testing::_; - using ::testing::InSequence; - using levels = skeleton::interfaces::ILogger::LogLevels; - InSequence seq; - EXPECT_CALL(*p_mock, log("mocked", levels::TRACE, _, _, _, _)).Times(1); - EXPECT_CALL(*p_mock, log("mocked", levels::DEBUG, _, _, _, _)).Times(1); - EXPECT_CALL(*p_mock, log("mocked", levels::INFO, _, _, _, _)).Times(1); - EXPECT_CALL(*p_mock, log("mocked", levels::WARN, _, _, _, _)).Times(1); - EXPECT_CALL(*p_mock, log("mocked", levels::ERROR, _, _, _, _)).Times(1); - for (int i = 0; i < static_cast(levels::COUNT); i++) - { - auto level = static_cast(i); - m_logger->log(level, "log"); - } -} - -TEST_F(LoggerTest, ConfigureWithFilename_ExpectConfigureCallWithFilename) -{ - const char* filename = "filename"; - EXPECT_CALL(*p_mock, configure(filename)).Times(1); - m_logger->configure(filename); -} - -TEST_F(LoggerTest, Configure_ExpectConfigureCall) -{ - using ::testing::_; - EXPECT_CALL(*p_mock, configure("")).Times(1); - m_logger->configure(); -} - -} \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..959b756 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,26 @@ +include(CTest) + +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/googletest.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/common.cmake) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED YES) + +list(APPEND TestFiles + ${CMAKE_CURRENT_LIST_DIR}/TestMain.cpp + ${CMAKE_CURRENT_LIST_DIR}/LoggerTest.cpp +) + +add_executable(${PROJECT_NAME}-test ${TestFiles}) + +target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}-lib gtest gmock gmock_main) + +target_include_directories(${PROJECT_NAME}-test PUBLIC ${log4cplus_SOURCE_DIR}/include) + +add_test(NAME ${PROJECT_NAME}-test COMMAND ${PROJECT_NAME}-test) + +copy_target_runtime_dlls(${PROJECT_NAME}-test) +print_target_properties(${PROJECT_NAME}-test LINK_LIBRARIES) \ No newline at end of file diff --git a/tests/LoggerTest.cpp b/tests/LoggerTest.cpp new file mode 100644 index 0000000..c44f6bd --- /dev/null +++ b/tests/LoggerTest.cpp @@ -0,0 +1,108 @@ +/** + * @file LoggerTest.cpp + * @author Tuomas Lahtinen (tuomas123lahtinen@gmail.com) + * @brief + * @version 0.1 + * @date 2023-04-03 + * + * @copyright Copyright (c) 2023 + * + */ + +#include "../interfaces/ILogger.h" +#include "../src/Logger.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include +#include + +namespace skeleton::testing +{ + +class MockLogger : public interfaces::ILogger +{ +public: + MOCK_METHOD(void, configure, (const TCHAR* const filename), (override)); + MOCK_METHOD(void, log, ( + const TCHAR* logger, + interfaces::LogLevels level, + (const TSTR &message), + const char* file, + int line, + const char* function + ), (override)); +}; + +class LoggerTest: public ::testing::Test +{ + protected: + void SetUp() override + { + } + + void TearDown() override + { + } +}; + +TEST_F(LoggerTest, LogAllLevels_ExpectLocationAndMessage) +{ + auto m_mock = std::make_shared(); + auto m_logger = std::unique_ptr(new logging::Logger("mocked", m_mock)); + using ::testing::_; + using levels = skeleton::interfaces::LogLevels; + + auto line = __builtin_LINE() + 6; + ::testing::InSequence seq; + EXPECT_CALL(*m_mock, log(_, levels::TRACE, TOLOGTSTR("trace"), ::testing::StrEq(__builtin_FILE()), ++line, ::testing::StrEq(__builtin_FUNCTION()))).Times(1); + EXPECT_CALL(*m_mock, log(_, levels::DEBUG, TOLOGTSTR("debug"), ::testing::StrEq(__builtin_FILE()), ++line, ::testing::StrEq(__builtin_FUNCTION()))).Times(1); + EXPECT_CALL(*m_mock, log(_, levels::INFO, TOLOGTSTR("info"), ::testing::StrEq(__builtin_FILE()), ++line, ::testing::StrEq(__builtin_FUNCTION()))).Times(1); + EXPECT_CALL(*m_mock, log(_, levels::WARN, TOLOGTSTR("warn"), ::testing::StrEq(__builtin_FILE()), ++line, ::testing::StrEq(__builtin_FUNCTION()))).Times(1); + EXPECT_CALL(*m_mock, log(_, levels::ERROR, TOLOGTSTR("error"), ::testing::StrEq(__builtin_FILE()), ++line, ::testing::StrEq(__builtin_FUNCTION()))).Times(1); + m_logger->trace("trace"); + m_logger->debug("debug"); + m_logger->info("info"); + m_logger->warn("warn"); + m_logger->error("error"); +} + +TEST_F(LoggerTest, LogAllLevels_ExpectEachLevelCalledOnce) +{ + auto m_mock = std::make_shared(); + auto m_logger = std::unique_ptr(new logging::Logger("mocked", m_mock)); + using ::testing::_; + using ::testing::InSequence; + using levels = skeleton::interfaces::LogLevels; + InSequence seq; + + EXPECT_CALL(*m_mock, log(_, levels::TRACE, _, _, _, _)).Times(1); + EXPECT_CALL(*m_mock, log(_, levels::DEBUG, _, _, _, _)).Times(1); + EXPECT_CALL(*m_mock, log(_, levels::INFO, _, _, _, _)).Times(1); + EXPECT_CALL(*m_mock, log(_, levels::WARN, _, _, _, _)).Times(1); + EXPECT_CALL(*m_mock, log(_, levels::ERROR, _, _, _, _)).Times(1); + for (int i = 0; i < static_cast(levels::COUNT); i++) + { + auto level = static_cast(i); + m_logger->log(level, "log"); + } +} + +TEST_F(LoggerTest, ConfigureWithFilename_ExpectConfigureCallWithFilename) +{ + auto m_mock = std::make_shared(); + auto m_logger = std::unique_ptr(new logging::Logger("mocked", m_mock)); + EXPECT_CALL(*m_mock, configure(::testing::StrEq(TOLOGTSTR("filename")))).Times(1); + m_logger->configure("filename"); +} + +TEST_F(LoggerTest, Configure_ExpectConfigureCall) +{ + auto m_mock = std::make_shared(); + auto m_logger = std::unique_ptr(new logging::Logger("mocked", m_mock)); + using ::testing::_; + EXPECT_CALL(*m_mock, configure(_)).Times(1); + m_logger->configure(); +} + +} \ No newline at end of file diff --git a/test/TestMain.cpp b/tests/TestMain.cpp similarity index 88% rename from test/TestMain.cpp rename to tests/TestMain.cpp index 769774a..cfab4bb 100644 --- a/test/TestMain.cpp +++ b/tests/TestMain.cpp @@ -8,8 +8,6 @@ * @copyright Copyright (c) 2023 * */ -// test includes -#include "LoggerTest.cpp" // googletest #include "gtest/gtest.h" From bb130036bf600375160eb753fb0c7822d7136e53 Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Tue, 11 Mar 2025 16:06:03 +0200 Subject: [PATCH 05/12] Add workflow to build PRs --- .github/workflows/cmake-multi-platform.yml | 83 ++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 .github/workflows/cmake-multi-platform.yml diff --git a/.github/workflows/cmake-multi-platform.yml b/.github/workflows/cmake-multi-platform.yml new file mode 100644 index 0000000..82bf9d6 --- /dev/null +++ b/.github/workflows/cmake-multi-platform.yml @@ -0,0 +1,83 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: CMake on multiple platforms + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + # Set up a matrix to run the following 3 configurations: + # 1. + # 2. + # 3. + # + # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. + matrix: + os: [ubuntu-latest, windows-latest] + build_type: [Release] + c_compiler: [gcc, clang, cl] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + exclude: + - os: windows-latest + c_compiler: gcc + - os: windows-latest + c_compiler: clang + - os: ubuntu-latest + c_compiler: cl + + steps: + - uses: actions/checkout@v4 + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -S ${{ github.workspace }} + + - name: Build + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} + + - name: Test + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-config ${{ matrix.build_type }} + + - name: Upload results to path + uses: actions/upload-artifact@v4 + if: failure() + with: + name: error-log + path: ${{ steps.strings.outputs.build-output-dir }}/Testing/Temporary/LastTest.log + if-no-files-found: ignore From 5413e7d9b2c5819a67aafdb93ac6fd9bb7a16479 Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Tue, 11 Mar 2025 16:10:21 +0200 Subject: [PATCH 06/12] Default build without UNICODE --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dc71cf5..d57e3cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,8 @@ project(skeleton option(SKELETON_BUILD_TESTS "Build test program" ON) option(SKELETON_BUILD_EXAMPLE "Build example" ON) -# Override - Build with tchar = wchar_t -set(UNICODE TRUE) +# Build without tchar = wchar_t +set(UNICODE FALSE) add_subdirectory(src) From 415356d67d60a9a210c56943f413e0324591f89a Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Tue, 11 Mar 2025 16:16:49 +0200 Subject: [PATCH 07/12] Fix gcc and clang build --- example/CMakeLists.txt | 5 +++-- src/Logger.h | 9 +++++---- tests/CMakeLists.txt | 1 - 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 7742258..96beec3 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -26,7 +26,8 @@ add_custom_target(configuration ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${conf_ add_executable(${PROJECT_NAME}-example ${CMAKE_CURRENT_LIST_DIR}/main.cpp) target_include_directories(${PROJECT_NAME}-example PUBLIC ${log4cplus_SOURCE_DIR}/include) target_link_libraries(${PROJECT_NAME}-example ${log4cplus} ${PROJECT_NAME}-lib) -copy_target_runtime_dlls(${PROJECT_NAME}-example) -install(TARGETS ${PROJECT_NAME}-example RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +if (MSVC) + copy_target_runtime_dlls(${PROJECT_NAME}-example) +endif() print_target_properties(${PROJECT_NAME}-example COMPILE_DEFINITIONS) diff --git a/src/Logger.h b/src/Logger.h index 474d532..bde221f 100644 --- a/src/Logger.h +++ b/src/Logger.h @@ -10,20 +10,21 @@ */ #pragma once #include "interfaces/ILogger.h" +#include +#include #include #include -#include namespace skeleton::logging { -#if (UNICODE) +#ifdef UNICODE # define TCHAR wchar_t # define TOLOGSTR(a) (const TCHAR*)L""##a -#else (UNICODE) +#else // (UNICODE) # define TCHAR char # define TOLOGSTR(a) a -#endif (UNICODE) +#endif // (UNICODE) #define TSTR std::basic_string, std::allocator> #define TOLOGTSTR(a) TSTR(TOLOGSTR(a)) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 959b756..2185c4e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,5 +22,4 @@ target_include_directories(${PROJECT_NAME}-test PUBLIC ${log4cplus_SOURCE_DIR}/i add_test(NAME ${PROJECT_NAME}-test COMMAND ${PROJECT_NAME}-test) -copy_target_runtime_dlls(${PROJECT_NAME}-test) print_target_properties(${PROJECT_NAME}-test LINK_LIBRARIES) \ No newline at end of file From 3d661bae7a458978623823f9f4081df18f75bf2e Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Tue, 11 Mar 2025 16:48:50 +0200 Subject: [PATCH 08/12] Discover tests for workflow --- CMakeLists.txt | 5 +++-- tests/CMakeLists.txt | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d57e3cb..68fbcd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,9 +15,10 @@ set(UNICODE FALSE) add_subdirectory(src) if (SKELETON_BUILD_EXAMPLE) - add_subdirectory(example) + add_subdirectory(example) endif (SKELETON_BUILD_EXAMPLE) if (SKELETON_BUILD_TESTS) - add_subdirectory(tests) + enable_testing() + add_subdirectory(tests) endif (SKELETON_BUILD_TESTS) \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2185c4e..39e2702 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -20,6 +20,7 @@ target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}-lib gtest gmock gmock target_include_directories(${PROJECT_NAME}-test PUBLIC ${log4cplus_SOURCE_DIR}/include) -add_test(NAME ${PROJECT_NAME}-test COMMAND ${PROJECT_NAME}-test) +print_target_properties(${PROJECT_NAME}-test LINK_LIBRARIES) -print_target_properties(${PROJECT_NAME}-test LINK_LIBRARIES) \ No newline at end of file +include(GoogleTest) +gtest_discover_tests(${PROJECT_NAME}-test) \ No newline at end of file From c635863ad673ab9d12a277f4337e03400095990b Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Tue, 11 Mar 2025 20:00:05 +0200 Subject: [PATCH 09/12] Fixes from running linux example Fix log.conf copy command. Add logging message after configure. --- example/CMakeLists.txt | 17 ++++++++++++----- example/log.conf | 2 +- example/main.cpp | 2 +- interfaces/ILogger.h | 11 +++++++++++ interfaces/LoggerLog4Cplus.h | 3 +++ src/Logger.h | 11 ----------- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 96beec3..cdb9827 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -12,15 +12,20 @@ include_directories(${CMAKE_CURRENT_LIST_DIR}/.. ${CMAKE_CURRENT_LIST_DIR}/../sr # create custom target copying the logger configuration # to the binary folder -set (conf_FILE "log.conf") +set (LOG_CONF "log.conf") +if (MSVC) + set (LOG_PFX "$/") +endif() + +set (TARGET_LOG_CONF "${LOG_PFX}${LOG_CONF}") add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${conf_FILE}" - COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_LIST_DIR}/${conf_FILE}" "${CMAKE_CURRENT_BINARY_DIR}/${conf_FILE}" - DEPENDS "${CMAKE_CURRENT_LIST_DIR}/${conf_FILE}" + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_LOG_CONF}" + COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_LIST_DIR}/${LOG_CONF}" "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_LOG_CONF}" + DEPENDS "${CMAKE_CURRENT_LIST_DIR}/${LOG_CONF}" ) -add_custom_target(configuration ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${conf_FILE}") +add_custom_target(configuration ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_LOG_CONF}") # skeleton example executable add_executable(${PROJECT_NAME}-example ${CMAKE_CURRENT_LIST_DIR}/main.cpp) @@ -31,3 +36,5 @@ if (MSVC) endif() print_target_properties(${PROJECT_NAME}-example COMPILE_DEFINITIONS) +cmake_print_variables(CMAKE_CURRENT_BINARY_DIR LOG_CONF TARGET_LOG_CONF) + diff --git a/example/log.conf b/example/log.conf index daa1bd1..914eab3 100644 --- a/example/log.conf +++ b/example/log.conf @@ -1,7 +1,7 @@ log4cplus.rootLogger=INFO, Main, Log4jUdp log4cplus.appender.Main=log4cplus::ConsoleAppender log4cplus.appender.Main.layout=log4cplus::PatternLayout -log4cplus.appender.Main.layout.ConversionPattern=%20.20F(%3L): %-5p %c %x - %m%n +log4cplus.appender.Main.layout.ConversionPattern=[%.15F:%L] %.5p [%.-5c] - %m%n log4cplus.appender.Log4jUdp=log4cplus::Log4jUdpAppender log4cplus.appender.Log4jUdp.host=127.0.0.1 diff --git a/example/main.cpp b/example/main.cpp index c199879..b83ac50 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -31,7 +31,7 @@ int main(int argc, char **argv) std::cout << "logger instance @" << logger.get() << std::endl; auto same = skeleton::factory::getLogger("main"); std::cout << "logger instance @" << logger.get() << "==" << same.get() << std::endl; - logger->configure("../log.conf"); + logger->configure("./log.conf"); logger->debug("debug message"); logger->warn("warning message"); logger->info("info message"); diff --git a/interfaces/ILogger.h b/interfaces/ILogger.h index 1467e3d..2a0898c 100644 --- a/interfaces/ILogger.h +++ b/interfaces/ILogger.h @@ -11,6 +11,17 @@ #pragma once #include +#ifdef UNICODE +# define TCHAR wchar_t +# define TOLOGSTR(a) (const TCHAR*)L""##a +#else // (UNICODE) +# define TCHAR char +# define TOLOGSTR(a) a +#endif // (UNICODE) + +#define TSTR std::basic_string, std::allocator> +#define TOLOGTSTR(a) TSTR(TOLOGSTR(a)) + namespace skeleton::interfaces { diff --git a/interfaces/LoggerLog4Cplus.h b/interfaces/LoggerLog4Cplus.h index f7e201f..1e5756e 100644 --- a/interfaces/LoggerLog4Cplus.h +++ b/interfaces/LoggerLog4Cplus.h @@ -42,16 +42,19 @@ class Log4CplusLogger: public interfaces::ILogger void configure(const TChar* const file) { + log4cplus::Logger instance(log4cplus::Logger::getInstance(TOLOGTSTR("init"))); std::ifstream ifs(file); if (ifs.good()) { log4cplus::PropertyConfigurator config(file); config.configure(); + instance.log(log4cplus::INFO_LOG_LEVEL, TOLOGSTR("configure() - file OK")); } else { log4cplus::BasicConfigurator config; config.configure(); + instance.log(log4cplus::INFO_LOG_LEVEL, TOLOGSTR("Configure() - no file - BasicConfigurator")); } } diff --git a/src/Logger.h b/src/Logger.h index bde221f..8bd6c83 100644 --- a/src/Logger.h +++ b/src/Logger.h @@ -18,17 +18,6 @@ namespace skeleton::logging { -#ifdef UNICODE -# define TCHAR wchar_t -# define TOLOGSTR(a) (const TCHAR*)L""##a -#else // (UNICODE) -# define TCHAR char -# define TOLOGSTR(a) a -#endif // (UNICODE) - -#define TSTR std::basic_string, std::allocator> -#define TOLOGTSTR(a) TSTR(TOLOGSTR(a)) - /// @brief Logger class provides functions for application logging class Logger { From 08f71afba6a67e4aee8266e420537d90730989f7 Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Wed, 12 Mar 2025 12:17:53 +0200 Subject: [PATCH 10/12] Cmake improvements Cleanup obsolete code. Force log4cplus build testing off. Fix option usage warnings. Silence msvc build (_CRT_SECURE_NO_WARNINGS). --- CMakeLists.txt | 3 ++- cmake/common.cmake | 4 ++++ cmake/log4cplus.cmake | 21 +-------------------- example/CMakeLists.txt | 1 + src/CMakeLists.txt | 2 +- tests/CMakeLists.txt | 4 ++++ 6 files changed, 13 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 68fbcd4..26ac1a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.5) +cmake_policy(SET CMP0077 NEW) project(skeleton VERSION 0.1 @@ -10,7 +11,7 @@ option(SKELETON_BUILD_TESTS "Build test program" ON) option(SKELETON_BUILD_EXAMPLE "Build example" ON) # Build without tchar = wchar_t -set(UNICODE FALSE) +set(UNICODE OFF CACHE INTERNAL "" FORCE) add_subdirectory(src) diff --git a/cmake/common.cmake b/cmake/common.cmake index 64ae71b..9b10631 100644 --- a/cmake/common.cmake +++ b/cmake/common.cmake @@ -12,4 +12,8 @@ macro(copy_target_runtime_dlls NAME) COMMAND ${CMAKE_COMMAND} -E copy $ $ COMMAND_EXPAND_LISTS ) +endmacro() + +macro(silence_msvc_warnings NAME) + target_compile_definitions(${NAME} PRIVATE "" _CRT_SECURE_NO_WARNINGS) endmacro() \ No newline at end of file diff --git a/cmake/log4cplus.cmake b/cmake/log4cplus.cmake index 0a4888c..849753f 100644 --- a/cmake/log4cplus.cmake +++ b/cmake/log4cplus.cmake @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 3.5) set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1") +set(LOG4CPLUS_BUILD_TESTING OFF CACHE INTERNAL "" FORCE) include(FetchContent) -set(FETCHCONTENT_QUIET FALSE) FetchContent_Declare(log4cplus GIT_REPOSITORY https://github.com/log4cplus/log4cplus.git @@ -11,27 +11,8 @@ FetchContent_Declare(log4cplus GIT_PROGRESS TRUE ) -# TODO: Check does the linux build need this. -# macro(FetchContent_MakeAvailableWithConfigure NAME) -# FetchContent_GetProperties(${NAME}) -# if(NOT ${NAME}_POPULATED) -# FetchContent_Populate(${NAME}) -# add_custom_command ( -# OUTPUT ${${NAME}_SOURCE_DIR}/include/log4cplus/config/defines.hxx -# COMMAND ${${NAME}_SOURCE_DIR}/configure -# WORKING_DIRECTORY ${${NAME}_SOURCE_DIR}/) -# add_custom_target(${NAME}_configure ALL DEPENDS ${${NAME}_SOURCE_DIR}/include/log4cplus/config/defines.hxx) -# add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR}) -# endif() -# endmacro() - FetchContent_MakeAvailable(log4cplus) -# if (UNICODE) -# add_compile_definitions (UNICODE _UNICODE) -# add_definitions (-UMBCS -U_MBCS) -# endif (UNICODE) -# add_compile_definitions (log4cplus_EXPORTS) print_target_properties(log4cplus::log4cplus LINK_LIBRARIES COMPILE_DEFINITIONS diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index cdb9827..026aab9 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -29,6 +29,7 @@ add_custom_target(configuration ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${TARGE # skeleton example executable add_executable(${PROJECT_NAME}-example ${CMAKE_CURRENT_LIST_DIR}/main.cpp) +silence_msvc_warnings(${PROJECT_NAME}-example) target_include_directories(${PROJECT_NAME}-example PUBLIC ${log4cplus_SOURCE_DIR}/include) target_link_libraries(${PROJECT_NAME}-example ${log4cplus} ${PROJECT_NAME}-lib) if (MSVC) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6d12b12..a0a2e2a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,7 +14,7 @@ list(APPEND SourceFiles add_library(${PROJECT_NAME}-lib STATIC ${SourceFiles}) if (MSVC) - target_compile_definitions(${PROJECT_NAME}-lib PRIVATE "" _CRT_SECURE_NO_WARNINGS) + silence_msvc_warnings(${PROJECT_NAME}-lib) endif() target_include_directories(${PROJECT_NAME}-lib PUBLIC ${CMAKE_CURRENT_LIST_DIR}/.. ${log4cplus_SOURCE_DIR}/include) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 39e2702..b4db068 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,10 @@ list(APPEND TestFiles add_executable(${PROJECT_NAME}-test ${TestFiles}) +if (MSVC) + silence_msvc_warnings(${PROJECT_NAME}-test) +endif() + target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME}-lib gtest gmock gmock_main) target_include_directories(${PROJECT_NAME}-test PUBLIC ${log4cplus_SOURCE_DIR}/include) From 54a08e87f7a8710a783714454273b2a9fc29c087 Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Wed, 12 Mar 2025 12:51:40 +0200 Subject: [PATCH 11/12] Add docker build --- .dockerignore | 8 ++++++++ Dockerfile | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..38bb9ef --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +* +!src +!interfaces +!src +!tests +!example +!cmake +!CMakeLists.txt \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..22b8c99 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM ubuntu:22.04 + +RUN apt-get update +RUN apt-get install build-essential -y +RUN apt-get install cmake -y +RUN apt-get install git -y + +LABEL org.opencontainers.image.title="Ubuntu cmake build" \ + org.opencontainers.image.description="Build cmake based application" \ + org.opencontainers.image.authors="@justtuomas" + +# Create directory in container image for app code +RUN mkdir -p /usr/src/app/build + +# Copy app code (..) to /usr/src/app in container image +COPY . /usr/src/app + +# Set working directory context +WORKDIR /usr/src/app/build + +RUN cmake .. + +RUN cmake --build . From 787216848076f4362c8da590aff954bfda0e434a Mon Sep 17 00:00:00 2001 From: Tuomas Lahtinen Date: Wed, 12 Mar 2025 12:52:05 +0200 Subject: [PATCH 12/12] Update readme with up-to-date instructions --- README.md | 111 +++++++++++++++++++++++++----------------------------- 1 file changed, 52 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 089634a..22d3bad 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,62 @@ -# Preparations -This guideline was written on Ubuntu 20.04 TLS. -The example uses ~/build as the base path. +# cpp-skeleton +The initial purpose of this project was to produce working example of building a cpp application with logging (l0g4cplus). -## Required build tools -The cpp-skeleton application software requires cmake v3.3.2 to build. -During initial testing g++-10 compiler was used but the ubuntu default g++-9 should work just fine. +The future vision is to serve as a skeleton for cpp application with configurable modules. -Following is the list of tool identified reconstruct the build setup on stock ubuntu. +The modules could be something like: +
+SKELETON_USE_LOG4CPLUS +
+SKELETON_USE_SOMEMODULE -```bash -sudo apt-get update -sudo apt-get install autoconf autogen automake cmake curl git libtool g++ make unzip -``` -### About Used DownloadProject -The https://github.com/Crascit/DownloadProject cmake files are used to download googletest at CMake's configure step. +## Build instructions +You can build any tool supporting digesting the `CMakeLists.txt` including vscode or vstudio but the workflow and configuration is tool specific. -And I quote from the repository README.md: -> The primary advantage of this is that the project's source code can then be included directly in the main CMake build using the add_subdirectory() command, making all of the external project's targets, etc. available without any further effort. +The following is the instructions on how to build using docker and ubuntu. +The ubuntu example is based on the docker build and is basically just overview of it. -## Build log4cplus (REL_2_1_0) -Opting to use version tagged `REL_2_1_0` as the master head version (3.0.0) requires a compiler with c++20 features and is a bit picky while at it. -For example the g++-10 (10.3) compiler accepts the flag -std=c++20 and __cplusplus evaluates to 201707L. -The log4cplus compiler test still fails while processing following version checking line. -``` -#https://github.com/log4cplus/log4cplus/blob/d9521ad97ba781b8b97f5aa29b0f4476074db866/m4/ax_cxx_compile_stdcxx.m4#L990 -#elif __cplusplus < 202002L && !defined _MSVER -``` -#### Run the following commands: -```bash -mkdir -p ~/build/3rdparty && cd ~/build/3rdparty -git clone --branch REL_2_1_0 https://github.com/log4cplus/log4cplus.git -cd log4cplus -git submodule update --init --recursive -./configure -# running the make twice due to error on the first run -# configure.ac:453: error: possibly undefined macro: AC_CHECK_INCLUDES_DEFAULT -make -j4 || make -j4 -sudo make install -sudo ldconfig -# verify installed -pkg-config --debug log4cplus -``` -# Skeleton application and tests -### Downloading the source -```bash -mkdir -p ~/build/application-software && cd ~/build/application-software +## Preparations +This guideline was written on Windows 10. + +### Required build tools +Docker installed. +[docker-windows](https://docs.docker.com/desktop/setup/install/windows-install/) +Git installed +[git-windows](https://git-scm.com/downloads/win) +#### Docker build and run example: +```shell git clone https://github.com/head5man/cpp-skeleton.git cd cpp-skeleton +# Build the image defined in ./Dockerfile +docker build -t build_image_name . +# Run container with interactive terminal +docker run -it --name=build_container_name build_image_name /bin/bash +# To run test executable +build_container_name:/usr/src/app/build# ./tests/skeleton-test +# To run application example +build_container_name:/usr/src/app/build# cd example && ./skeleton-example +# Exit and delete container and image +build_container_name:/usr/src/app/build# exit +docker rm build_container_name +docker rmi build_image_name ``` -### Running the build: -```bash -mkdir -p build && cd build -cmake .. -make -``` -### Running the application -```bash -#Run the skeleton executable -src/skeleton -``` -### Running the tests + +#### Ubuntu 22.04 build and run example ```bash -#Run the skeleton-test executable -src/skeleton-test -#Run CTest alternative reporting only module level results -make test -``` +apt-get update +apt-get install build-essential -y +apt-get install cmake -y +apt-get install git -y +# Clone repository to home and build +cd ~ +git clone https://github.com/head5man/cpp-skeleton.git +cmake -S cpp-skeleton -B cpp-skelton/build +cmake --build cpp-skeleton/build + +# To run test executable +cpp-skeleton/build/tests/skeleton-test + +# To run application example +cd cpp-skeleton/build/example && ./skeleton-example +```