diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 86105bfb..d493bdab 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -101,6 +101,13 @@ test_guidelines: - docker run -e CCACHE_DIR=/ccache -v /mnt/ccache:/ccache netgen_${CI_BUILD_REF_NAME}:${UBUNTU_VERSION} bash /root/src/netgen/tests/build_guidelines.sh when: always +# check if it compiles without spdlog +test_noSpdlog: + <<: *ubuntu + stage: test + script: + - docker run -e CCACHE_DIR=/ccache -v /mnt/ccache:/ccache netgen_${CI_BUILD_REF_NAME}:${UBUNTU_VERSION} bash /root/src/netgen/tests/build_nospdlog.sh + cleanup_ubuntu: stage: cleanup tags: diff --git a/CMakeLists.txt b/CMakeLists.txt index 14931c4d..5d2e6a9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,8 @@ option( USE_CCACHE "use ccache") option( USE_INTERNAL_TCL "Compile tcl files into the code and don't install them" ON) option( ENABLE_UNIT_TESTS "Enable Catch unit tests") option( ENABLE_CPP_CORE_GUIDELINES_CHECK "Enable cpp core guideline checks on ngcore" OFF) +option( USE_SPDLOG "Enable spd log logging" ON) +option( DEBUG_LOG "Enable more debug output (may increase computation time) - only works with USE_SPDLOG=ON" OFF) option( USE_SUPERBUILD "use ccache" ON) @@ -258,6 +260,7 @@ endif (USE_GUI) ####################################################################### if (USE_PYTHON) + add_subdirectory(external_dependencies/pybind11) add_definitions(-DNG_PYTHON) find_path(PYBIND_INCLUDE_DIR pybind11/pybind11.h HINTS ${PYTHON_INCLUDE_DIR}) if( PYBIND_INCLUDE_DIR ) @@ -350,6 +353,11 @@ endif(ENABLE_UNIT_TESTS) ####################################################################### +if(USE_SPDLOG) + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/external_projects/spdlog.cmake) + include_directories(${SPDLOG_INCLUDE_DIR}) +endif(USE_SPDLOG) + if(ENABLE_CPP_CORE_GUIDELINES_CHECK) find_program( CLANG_TIDY_EXE diff --git a/cmake/SuperBuild.cmake b/cmake/SuperBuild.cmake index 36ea3fcb..6e7fabd7 100644 --- a/cmake/SuperBuild.cmake +++ b/cmake/SuperBuild.cmake @@ -142,6 +142,8 @@ set_vars( NETGEN_CMAKE_ARGS CMAKE_INSTALL_PREFIX ENABLE_UNIT_TESTS ENABLE_CPP_CORE_GUIDELINES_CHECK + USE_SPDLOG + DEBUG_LOG ) # propagate all variables set on the command line using cmake -DFOO=BAR diff --git a/cmake/external_projects/spdlog.cmake b/cmake/external_projects/spdlog.cmake new file mode 100644 index 00000000..d932ebef --- /dev/null +++ b/cmake/external_projects/spdlog.cmake @@ -0,0 +1,18 @@ +include(ExternalProject) +find_program(GIT_EXECUTABLE git) + +ExternalProject_Add( + project_spdlog + PREFIX ${CMAKE_BINARY_DIR}/spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG v1.2.1 + TIMEOUT 01 + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + ) + +ExternalProject_Get_Property(project_spdlog source_dir) +set(SPDLOG_INCLUDE_DIR ${source_dir}/include) diff --git a/libsrc/core/CMakeLists.txt b/libsrc/core/CMakeLists.txt index 92f2e8b7..16cec927 100644 --- a/libsrc/core/CMakeLists.txt +++ b/libsrc/core/CMakeLists.txt @@ -1,16 +1,41 @@ -add_library(ngcore SHARED archive.cpp) -target_compile_definitions(ngcore PRIVATE -DNGCORE_EXPORTS) +add_library(ngcore SHARED archive.cpp logging.cpp) + +target_compile_definitions(ngcore PRIVATE NGCORE_EXPORTS) +if(NOT WIN32) + target_compile_options(ngcore PRIVATE -fvisibility=hidden) +endif(NOT WIN32) + +if(USE_SPDLOG) + include_directories(${SPDLOG_INCLUDE_DIR}) + install(DIRECTORY ${SPDLOG_INCLUDE_DIR} + DESTINATION ${NG_INSTALL_DIR_INCLUDE} + ) + add_dependencies(ngcore project_spdlog) + target_compile_definitions(ngcore PUBLIC NETGEN_USE_SPDLOG) + if(DEBUG_LOG) + target_compile_definitions(ngcore PUBLIC NETGEN_LOG_DEBUG) + endif(DEBUG_LOG) +endif(USE_SPDLOG) install(TARGETS ngcore DESTINATION ${NG_INSTALL_DIR} COMPONENT netgen) if(USE_PYTHON) + target_compile_definitions(ngcore PUBLIC NETGEN_PYTHON) target_include_directories(ngcore PUBLIC ${PYTHON_INCLUDE_DIRS}) endif(USE_PYTHON) -install(FILES ngcore.hpp archive.hpp type_traits.hpp version.hpp ngcore_api.hpp +install(FILES ngcore.hpp archive.hpp type_traits.hpp version.hpp ngcore_api.hpp logging.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE}/core COMPONENT netgen_devel) if(ENABLE_CPP_CORE_GUIDELINES_CHECK) set_target_properties(ngcore PROPERTIES CXX_CLANG_TIDY "${DO_CLANG_TIDY}") endif(ENABLE_CPP_CORE_GUIDELINES_CHECK) + +if(USE_PYTHON) + pybind11_add_module(pyngcore SHARED python_ngcore.cpp) + target_link_libraries(pyngcore PUBLIC ngcore) + set_target_properties(pyngcore PROPERTIES INSTALL_RPATH "${NG_RPATH_TOKEN}/${NETGEN_PYTHON_RPATH}") + install(TARGETS pyngcore DESTINATION ${NG_INSTALL_DIR_PYTHON} COMPONENT netgen) +endif(USE_PYTHON) + diff --git a/libsrc/core/archive.hpp b/libsrc/core/archive.hpp index fd2ee275..aa6537d2 100644 --- a/libsrc/core/archive.hpp +++ b/libsrc/core/archive.hpp @@ -3,24 +3,25 @@ #include // for complex #include // for size_t, strlen -#include // for operator<<, ifstream, ofstream, basic... +#include // for ifstream, ofstream #include // for function -#include // for map, _Rb_tree_iterator -#include // for __shared_ptr_access, __shared_ptr_acc... +#include // for map +#include // for shared_ptr #include // for runtime_error -#include // for string, operator+ +#include // for string #include // for declval, enable_if, false_type, is_co... #include // for type_info #include // for move, swap, pair #include // for vector +#include "logging.hpp" // for logger #include "ngcore_api.hpp" // for NGCORE_API, unlikely #include "type_traits.hpp" // for all_of_tmpl #include "version.hpp" // for VersionInfo -#ifdef NG_PYTHON +#ifdef NETGEN_PYTHON #include -#endif // NG_PYTHON +#endif // NETGEN_PYTHON namespace ngcore { @@ -104,20 +105,28 @@ namespace ngcore std::vector nr2ptr; protected: bool shallow_to_python = false; + // version map is only used in InArchives + std::map version_map; + std::shared_ptr logger; public: Archive() = delete; Archive(const Archive&) = delete; Archive(Archive&&) = delete; - Archive (bool ais_output) : - is_output(ais_output), shared_ptr_count(0), ptr_count(0) { ; } + Archive (bool ais_output) : is_output(ais_output), shared_ptr_count(0), + ptr_count(0), logger(GetLogger("Archive")) { ; } virtual ~Archive() { ; } + // If the object is pickled, all shallow archived objects will be pickled as a list, + // instead of written as a binary archive. This allows pickle to serialize every object only + // once and put them together correctly afterwards. Therefore all objects that may live in + // Python should be archived using this Shallow function. If Shallow is called from C++ code + // it archives the object normally. template Archive& Shallow(T& val) { static_assert(detail::is_any_pointer, "ShallowArchive must be given pointer type!"); -#ifdef NG_PYTHON +#ifdef NETGEN_PYTHON if(shallow_to_python) { if(is_output) @@ -126,17 +135,17 @@ namespace ngcore val = pybind11::cast(ShallowInPython()); } else -#endif // NG_PYTHON +#endif // NETGEN_PYTHON *this & val; return *this; } -#ifdef NG_PYTHON - virtual void ShallowOutPython(pybind11::object /*unused*/) // NOLINT (copy by val is ok for this virt func) +#ifdef NETGEN_PYTHON + virtual void ShallowOutPython(const pybind11::object& /*unused*/) { throw std::runtime_error("Should not get in ShallowToPython base class implementation!"); } virtual pybind11::object ShallowInPython() { throw std::runtime_error("Should not get in ShallowFromPython base class implementation!"); } -#endif // NG_PYTHON +#endif // NETGEN_PYTHON Archive& operator=(const Archive&) = delete; Archive& operator=(Archive&&) = delete; @@ -261,28 +270,39 @@ namespace ngcore { if(Output()) { + NETGEN_DEBUG_LOG(logger, "Store shared ptr of type " + Demangle(typeid(T).name())); // save -2 for nullptr if(!ptr) - return (*this) << -2; + { + NETGEN_DEBUG_LOG(logger, "Storing nullptr"); + return (*this) << -2; + } void* reg_ptr = ptr.get(); bool neededDowncast = false; // Downcasting is only possible for our registered classes if(typeid(T) != typeid(*ptr)) { + NETGEN_DEBUG_LOG(logger, "Typids are different: " + Demangle(typeid(T).name()) + " vs. " + + Demangle(typeid(*ptr).name())); if(!IsRegistered(Demangle(typeid(*ptr).name()))) throw std::runtime_error(std::string("Archive error: Polymorphic type ") + Demangle(typeid(*ptr).name()) + " not registered for archive"); reg_ptr = GetArchiveRegister(Demangle(typeid(*ptr).name())).downcaster(typeid(T), ptr.get()); // if there was a true downcast we have to store more information - if(reg_ptr != static_cast(ptr.get()) ) + if(reg_ptr != static_cast(ptr.get())) + { + NETGEN_DEBUG_LOG(logger, "Multiple/Virtual inheritance involved, need to cast pointer"); neededDowncast = true; + } } auto pos = shared_ptr2nr.find(reg_ptr); // if not found store -1 and the pointer if(pos == shared_ptr2nr.end()) { + NETGEN_DEBUG_LOG(logger, "Didn't find the shared_ptr, create new registry entry at " + + std::to_string(shared_ptr_count)); auto p = ptr.get(); (*this) << -1; (*this) & neededDowncast & p; @@ -293,23 +313,27 @@ namespace ngcore return *this; } // if found store the position and if it has to be downcasted and how + NETGEN_DEBUG_LOG(logger, "Found shared_ptr at position " + std::to_string(pos->second)); (*this) << pos->second << neededDowncast; if(neededDowncast) (*this) << Demangle(typeid(*ptr).name()); } else // Input { + NETGEN_DEBUG_LOG(logger, "Reading shared_ptr of type " + Demangle(typeid(T).name())); int nr; (*this) & nr; // -2 restores a nullptr if(nr == -2) { + NETGEN_DEBUG_LOG(logger, "Reading a nullptr"); ptr = nullptr; return *this; } // -1 restores a new shared ptr by restoring the inner pointer and creating a shared_ptr to it if (nr == -1) { + NETGEN_DEBUG_LOG(logger, "Createing new shared_ptr"); T* p = nullptr; bool neededDowncast; (*this) & neededDowncast & p; @@ -317,6 +341,7 @@ namespace ngcore // if we did downcast we need to store a shared_ptr to the true object if(neededDowncast) { + NETGEN_DEBUG_LOG(logger, "Shared pointer needed downcasting"); std::string name; (*this) & name; auto info = GetArchiveRegister(name); @@ -327,15 +352,20 @@ namespace ngcore ptr.get()))); } else - nr2shared_ptr.push_back(ptr); + { + NETGEN_DEBUG_LOG(logger, "Shared pointer didn't need downcasting"); + nr2shared_ptr.push_back(ptr); + } } else { + NETGEN_DEBUG_LOG(logger, "Reading already existing pointer at entry " + std::to_string(nr)); auto other = nr2shared_ptr[nr]; bool neededDowncast; (*this) & neededDowncast; if(neededDowncast) { + NETGEN_DEBUG_LOG(logger, "Shared pointer needed pointer downcast"); // if there was a downcast we can expect the class to be registered (since archiving // wouldn't have worked else) std::string name; @@ -348,7 +378,10 @@ namespace ngcore other.get()))); } else - ptr = std::static_pointer_cast(other); + { + NETGEN_DEBUG_LOG(logger, "Shared pointer didn't need pointer casts"); + ptr = std::static_pointer_cast(other); + } } } return *this; @@ -360,26 +393,39 @@ namespace ngcore { if (Output()) { + NETGEN_DEBUG_LOG(logger, "Store pointer of type " + Demangle(typeid(T).name())); // if the pointer is null store -2 if (!p) - return (*this) << -2; + { + NETGEN_DEBUG_LOG(logger, "Storing nullptr"); + return (*this) << -2; + } auto reg_ptr = static_cast(p); if(typeid(T) != typeid(*p)) { + NETGEN_DEBUG_LOG(logger, "Typeids are different: " + Demangle(typeid(T).name()) + " vs. " + + Demangle(typeid(*p).name())); if(!IsRegistered(Demangle(typeid(*p).name()))) throw std::runtime_error(std::string("Archive error: Polymorphic type ") + Demangle(typeid(*p).name()) + " not registered for archive"); reg_ptr = GetArchiveRegister(Demangle(typeid(*p).name())).downcaster(typeid(T), static_cast(p)); + if(reg_ptr != static_cast(p)) + { + NETGEN_DEBUG_LOG(logger, "Multiple/Virtual inheritance involved, need to cast pointer"); + } } auto pos = ptr2nr.find(reg_ptr); // if the pointer is not found in the map create a new entry if (pos == ptr2nr.end()) { + NETGEN_DEBUG_LOG(logger, "Didn't find pointer, create new registry entry at " + + std::to_string(ptr_count)); ptr2nr[reg_ptr] = ptr_count++; if(typeid(*p) == typeid(T)) if (std::is_constructible::value) { + NETGEN_DEBUG_LOG(logger, "Store standard class pointer (no virt. inh,...)"); return (*this) << -1 & (*p); } else @@ -395,6 +441,7 @@ namespace ngcore throw std::runtime_error(std::string("Archive error: Polymorphic type ") + Demangle(typeid(*p).name()) + " not registered for archive"); + NETGEN_DEBUG_LOG(logger, "Store a possibly more complicated pointer"); return (*this) << -3 << Demangle(typeid(*p).name()) & (*p); } } @@ -402,27 +449,39 @@ namespace ngcore { (*this) & pos->second; bool downcasted = !(reg_ptr == static_cast(p) ); + NETGEN_DEBUG_LOG(logger, "Store a the existing position in registry at " + + std::to_string(pos->second)); + NETGEN_DEBUG_LOG(logger, std::string("Pointer ") + (downcasted ? "needs " : "doesn't need ") + + "downcasting"); // store if the class has been downcasted and the name (*this) << downcasted << Demangle(typeid(*p).name()); } } else { + NETGEN_DEBUG_LOG(logger, "Reading pointer of type " + Demangle(typeid(T).name())); int nr; (*this) & nr; if (nr == -2) // restore a nullptr + { + NETGEN_DEBUG_LOG(logger, "Loading a nullptr"); p = nullptr; + } else if (nr == -1) // create a new pointer of standard type (no virtual or multiple inheritance,...) { + NETGEN_DEBUG_LOG(logger, "Load a new pointer to a simple class"); p = detail::constructIfPossible(); nr2ptr.push_back(p); (*this) & *p; } else if(nr == -3) // restore one of our registered classes that can have multiple inheritance,... { + NETGEN_DEBUG_LOG(logger, "Load a new pointer to a potentially more complicated class " + "(allows for multiple/virtual inheritance,...)"); // As stated above, we want this special behaviour only for our classes that implement DoArchive std::string name; (*this) & name; + NETGEN_DEBUG_LOG(logger, "Name = " + name); auto info = GetArchiveRegister(name); // the creator creates a new object of type name, and returns a void* pointing // to T (which may have an offset) @@ -434,9 +493,13 @@ namespace ngcore } else { + NETGEN_DEBUG_LOG(logger, "Restoring pointer to already existing object at registry position " + + std::to_string(nr)); bool downcasted; std::string name; (*this) & downcasted & name; + NETGEN_DEBUG_LOG(logger, std::string(downcasted ? "Downcasted" : "Not downcasted") + + " object of type " + name); if(downcasted) { // if the class has been downcasted we can assume it is in the register @@ -536,7 +599,7 @@ namespace ngcore { static_assert(detail::all_of_tmpl::value...>, "Variadic template arguments must be base classes of T"); - detail::ClassArchiveInfo info; + detail::ClassArchiveInfo info {}; info.creator = [this,&info](const std::type_info& ti) -> void* { return typeid(T) == ti ? detail::constructIfPossible() : Archive::Caster::tryUpcast(ti, detail::constructIfPossible()); }; @@ -643,6 +706,9 @@ namespace ngcore BinaryInArchive (const std::string& filename) : BinaryInArchive(std::make_shared(filename)) { ; } + const VersionInfo& GetVersion(const std::string& library) override + { return version_map[library]; } + using Archive::operator&; Archive & operator & (double & d) override { Read(d); return *this; } @@ -758,6 +824,9 @@ namespace ngcore TextInArchive (const std::string& filename) : TextInArchive(std::make_shared(filename)) {} + const VersionInfo& GetVersion(const std::string& library) override + { return version_map[library]; } + using Archive::operator&; Archive & operator & (double & d) override { *stream >> d; return *this; } @@ -805,7 +874,7 @@ namespace ngcore } }; -#ifdef NG_PYTHON +#ifdef NETGEN_PYTHON template class PyArchive : public ARCHIVE @@ -813,7 +882,11 @@ namespace ngcore private: pybind11::list lst; size_t index = 0; + protected: using ARCHIVE::stream; + using ARCHIVE::version_map; + using ARCHIVE::logger; + using ARCHIVE::GetLibraryVersions; public: PyArchive(const pybind11::object& alst = pybind11::none()) : ARCHIVE(std::make_shared()), @@ -821,8 +894,13 @@ namespace ngcore { ARCHIVE::shallow_to_python = true; if(Input()) - stream = std::make_shared - (pybind11::cast(lst[pybind11::len(lst)-1])); + { + stream = std::make_shared + (pybind11::cast(lst[pybind11::len(lst)-1])); + *this & version_map; + stream = std::make_shared + (pybind11::cast(lst[pybind11::len(lst)-2])); + } } using ARCHIVE::Output; @@ -831,39 +909,45 @@ namespace ngcore using ARCHIVE::operator&; using ARCHIVE::operator<<; using ARCHIVE::GetVersion; - void ShallowOutPython(pybind11::object val) override { lst.append(val); } + void ShallowOutPython(const pybind11::object& val) override { lst.append(val); } pybind11::object ShallowInPython() override { return lst[index++]; } pybind11::list WriteOut() { FlushBuffer(); lst.append(pybind11::bytes(std::static_pointer_cast(stream)->str())); + stream = std::make_shared(); + *this & GetLibraryVersions(); return lst; } }; template - auto NGSPickle(bool printoutput=false) + auto NGSPickle() { - return pybind11::pickle([printoutput](T* self) + return pybind11::pickle([](T* self) { PyArchive ar; ar & self; auto output = pybind11::make_tuple(ar.WriteOut()); - if(printoutput) - pybind11::print("pickle output of", Demangle(typeid(T).name()),"=", output); + NETGEN_DEBUG_LOG(GetLogger("Archive"), "pickling output for object of type " + + Demangle(typeid(T).name()) + " = " + + std::string(pybind11::str(output))); return output; }, [](pybind11::tuple state) { T* val = nullptr; + NETGEN_DEBUG_LOG(GetLogger("Archive"), "State for unpickling of object of type " + + Demangle(typeid(T).name()) + " = " + + std::string(pybind11::str(state[0]))); PyArchive ar(state[0]); ar & val; return val; }); } -#endif // NG_PYTHON +#endif // NETGEN_PYTHON } // namespace ngcore #endif // NETGEN_CORE_ARCHIVE_HPP diff --git a/libsrc/core/logging.cpp b/libsrc/core/logging.cpp new file mode 100644 index 00000000..467255cd --- /dev/null +++ b/libsrc/core/logging.cpp @@ -0,0 +1,109 @@ + +#include "logging.hpp" + +#ifdef NETGEN_USE_SPDLOG + +#include +#include + +#endif // NETGEN_USE_SPDLOG + +namespace ngcore +{ +#ifdef NETGEN_USE_SPDLOG + namespace detail + { + std::vector>& GetDefaultSinks() + { + static std::vector> sinks = + { std::make_shared() }; + return sinks; + } + std::shared_ptr CreateDefaultLogger(const std::string& name) + { + auto& default_sinks = GetDefaultSinks(); + auto logger = std::make_shared(name, default_sinks.begin(), default_sinks.end()); + spdlog::details::registry::instance().register_and_init(logger); + return logger; + } + } // namespace detail + + std::shared_ptr GetLogger(const std::string& name) + { + auto logger = spdlog::get(name); + if(!logger) + logger = detail::CreateDefaultLogger(name); + return logger; + } + + void SetLoggingLevel(spdlog::level::level_enum level, const std::string& name) + { + if(!name.empty()) + spdlog::get(name)->set_level(level); + else + spdlog::set_level(level); + } + + void AddFileSink(const std::string& filename, spdlog::level::level_enum level, const std::string& logger) + { + auto sink = std::make_shared(filename); + sink->set_level(level); + if(!logger.empty()) + GetLogger(logger)->sinks().push_back(sink); + else + { + detail::GetDefaultSinks().push_back(sink); + spdlog::details::registry::instance().apply_all([sink](auto logger) { logger->sinks().push_back(sink); }); + } + } + + void AddConsoleSink(spdlog::level::level_enum level, const std::string& logger) + { + auto sink = std::make_shared(); + sink->set_level(level); + if(!logger.empty()) + GetLogger(logger)->sinks().push_back(sink); + else + { + detail::GetDefaultSinks().push_back(sink); + spdlog::details::registry::instance().apply_all([sink](auto logger) { logger->sinks().push_back(sink); }); + } + } + + void ClearLoggingSinks(const std::string& logger) + { + if(!logger.empty()) + GetLogger(logger)->sinks().clear(); + else + { + detail::GetDefaultSinks().clear(); + spdlog::details::registry::instance().apply_all([](auto logger) { logger->sinks().clear(); }); + } + } + + void FlushOnLoggingLevel(spdlog::level::level_enum level, const std::string& logger) + { + if(!logger.empty()) + GetLogger(logger)->flush_on(level); + else + spdlog::flush_on(level); + } + +#else // NETGEN_USE_SPDLOG + + // Dummy functions if no spdlog is available + + std::shared_ptr GetLogger(const std::string& /*unused*/) + { + return std::make_shared(); + } + void SetLoggingLevel(spdlog::level::level_enum /*unused*/, const std::string& /*unused*/) {} + void AddFileSink(const std::string& /*unused*/, spdlog::level::level_enum /*unused*/, + const std::string& /*unused*/) + {} + void AddConsoleSink(spdlog::level::level_enum /*unused*/, const std::string& /*unused*/) {} + void ClearLoggingSinks(const std::string& /*unused*/) {} + void FlushOnLoggingLevel(spdlog::level::level_enum /*unused*/, const std::string& /*unused*/) {} + +#endif // NETGEN_USE_SPDLOG +} //namespace ngcore diff --git a/libsrc/core/logging.hpp b/libsrc/core/logging.hpp new file mode 100644 index 00000000..8952a5f6 --- /dev/null +++ b/libsrc/core/logging.hpp @@ -0,0 +1,87 @@ +#ifndef NETGEN_CORE_LOGGING_HPP +#define NETGEN_CORE_LOGGING_HPP + +#include +#include +#include + +#include "ngcore_api.hpp" + +#ifdef NETGEN_USE_SPDLOG + +#ifdef NETGEN_LOG_DEBUG +#define SPDLOG_DEBUG_ON +#endif // NETGEN_LOG_DEBUG + +#include +#include +#include + +#define NETGEN_DEBUG_LOG(logger, ...) SPDLOG_DEBUG(logger, __VA_ARGS__) + +#else // NETGEN_USE_SPDLOG + +#include + +namespace spdlog +{ + // Dummys if Netgen is compiled with USE_SPDLOG=OFF. + class logger + { + public: + template + void trace(const T& /*unused*/) {} + template + void debug(const T& /*unused*/) {} + template + void info(const T& text) { std::cout << text << std::endl; } + template + void warn(const T& text) { std::cout << text << std::endl; } + template + void error(const T& text) { std::cout << text << std::endl; } + template + void critical(const T& text) { std::cout << text << std::endl; } + }; + + namespace level + { + enum level_enum + { + trace = 0, + debug = 1, + info = 2, + warn = 3, + err = 4, + critical = 5, + off = 6 + }; + } // namespace level + + namespace sinks + { + class sink {}; + } // namespace sinks + +} //namespace spdlog + +#define NETGEN_DEBUG_LOG(logger, ...) + +#endif // NETGEN_USE_SPDLOG + +namespace ngcore +{ + namespace detail + { + std::vector>& GetDefaultSinks(); + inline std::shared_ptr CreateDefaultLogger(const std::string& name); + } //namespace detail + + NGCORE_API std::shared_ptr GetLogger(const std::string& name); + NGCORE_API void SetLoggingLevel(spdlog::level::level_enum level, const std::string& name); + NGCORE_API void AddFileSink(const std::string& filename, spdlog::level::level_enum level, const std::string& logger); + NGCORE_API void AddConsoleSink(spdlog::level::level_enum level, const std::string& logger); + NGCORE_API void ClearLoggingSinks(const std::string& logger); + NGCORE_API void FlushOnLoggingLevel(spdlog::level::level_enum level, const std::string& logger); +} // namespace ngcore + +#endif // NETGEN_CORE_LOGGING_HPP diff --git a/libsrc/core/ngcore.hpp b/libsrc/core/ngcore.hpp index 1c8fa591..ea7f8eab 100644 --- a/libsrc/core/ngcore.hpp +++ b/libsrc/core/ngcore.hpp @@ -2,6 +2,7 @@ #define NETGEN_CORE_NGCORE_HPP #include "archive.hpp" +#include "logging.hpp" #include "version.hpp" #endif // NETGEN_CORE_NGCORE_HPP diff --git a/libsrc/core/ngcore_api.hpp b/libsrc/core/ngcore_api.hpp index c08d0ac9..5c65cf3a 100644 --- a/libsrc/core/ngcore_api.hpp +++ b/libsrc/core/ngcore_api.hpp @@ -5,8 +5,8 @@ #define NGCORE_API_EXPORT __declspec(dllexport) #define NGCORE_API_IMPORT __declspec(dllimport) #else - #define NGCORE_API_EXPORT - #define NGCORE_API_IMPORT + #define NGCORE_API_EXPORT __attribute__((visibility("default"))) + #define NGCORE_API_IMPORT __attribute__((visibility("default"))) #endif #ifdef NGCORE_EXPORTS diff --git a/libsrc/core/python_ngcore.cpp b/libsrc/core/python_ngcore.cpp new file mode 100644 index 00000000..b7624e43 --- /dev/null +++ b/libsrc/core/python_ngcore.cpp @@ -0,0 +1,30 @@ + +#include + +#include "logging.hpp" + +namespace py = pybind11; +using namespace ngcore; + +PYBIND11_MODULE(pyngcore, m) // NOLINT +{ + py::enum_(m, "LOG_LEVEL", "Logging level") + .value("Trace", spdlog::level::trace) + .value("Debug", spdlog::level::debug) + .value("Info", spdlog::level::info) + .value("Warn", spdlog::level::warn) + .value("Error", spdlog::level::err) + .value("Critical", spdlog::level::critical) + .value("Off", spdlog::level::off); + + m.def("SetLoggingLevel", &SetLoggingLevel, py::arg("level"), py::arg("logger")="", + "Set logging level, if name is given only to the specific logger, else set the global logging level"); + m.def("AddFileSink", &AddFileSink, py::arg("filename"), py::arg("level"), py::arg("logger")="", + "Add File sink, either only to logger specified or globally to all loggers"); + m.def("AddConsoleSink", &AddConsoleSink, py::arg("level"), py::arg("logger")="", + "Add console output for specific logger or all if none given"); + m.def("ClearLoggingSinks", &ClearLoggingSinks, py::arg("logger")="", + "Clear sinks of specific logger, or all if none given"); + m.def("FlushOnLoggingLevel", &FlushOnLoggingLevel, py::arg("level"), py::arg("logger")="", + "Flush every message with level at least `level` for specific logger or all loggers if none given."); +} diff --git a/libsrc/general/CMakeLists.txt b/libsrc/general/CMakeLists.txt index 00c59ea2..39b63d4c 100644 --- a/libsrc/general/CMakeLists.txt +++ b/libsrc/general/CMakeLists.txt @@ -6,6 +6,10 @@ add_library(gen OBJECT mpi_interface.cpp gzstream.cpp ) +if(NOT WIN32) + target_link_libraries(gen ngcore) +endif(NOT WIN32) + set_target_properties( gen PROPERTIES POSITION_INDEPENDENT_CODE ON ) install( FILES ngexception.hpp DESTINATION ${NG_INSTALL_DIR_INCLUDE} COMPONENT netgen_devel ) diff --git a/libsrc/gprim/CMakeLists.txt b/libsrc/gprim/CMakeLists.txt index 3957476a..ac4b8f11 100644 --- a/libsrc/gprim/CMakeLists.txt +++ b/libsrc/gprim/CMakeLists.txt @@ -3,6 +3,9 @@ add_library(gprim OBJECT adtree.cpp geom2d.cpp geom3d.cpp geomfuncs.cpp geomtest3d.cpp transform3d.cpp spline.cpp splinegeometry.cpp ) +if(NOT WIN32) + target_link_libraries(gprim ngcore) +endif(NOT WIN32) set_target_properties( gprim PROPERTIES POSITION_INDEPENDENT_CODE ON ) diff --git a/libsrc/linalg/CMakeLists.txt b/libsrc/linalg/CMakeLists.txt index a5a5a4f2..9f35d751 100644 --- a/libsrc/linalg/CMakeLists.txt +++ b/libsrc/linalg/CMakeLists.txt @@ -4,6 +4,10 @@ add_library( la OBJECT set_target_properties(la PROPERTIES POSITION_INDEPENDENT_CODE ON ) +if(NOT WIN32) + target_link_libraries(la ngcore) +endif(NOT WIN32) + install(FILES densemat.hpp linalg.hpp opti.hpp polynomial.hpp vector.hpp diff --git a/libsrc/meshing/python_mesh.cpp b/libsrc/meshing/python_mesh.cpp index 6360174a..d349dfcf 100644 --- a/libsrc/meshing/python_mesh.cpp +++ b/libsrc/meshing/python_mesh.cpp @@ -506,7 +506,6 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m) ) */ - .def("__str__", &ToString) .def_property_readonly("_timestamp", &Mesh::GetTimeStamp) .def("Load", FunctionPointer ([](Mesh & self, const string & filename) diff --git a/libsrc/occ/CMakeLists.txt b/libsrc/occ/CMakeLists.txt index 831a3875..a244e85b 100644 --- a/libsrc/occ/CMakeLists.txt +++ b/libsrc/occ/CMakeLists.txt @@ -9,7 +9,7 @@ if(USE_GUI) endif(USE_GUI) if(NOT WIN32) - target_link_libraries( occ ${OCC_LIBRARIES} ${PYTHON_LIBRARIES}) + target_link_libraries( occ ngcore ${OCC_LIBRARIES} ${PYTHON_LIBRARIES}) install( TARGETS occ ${NG_INSTALL_DIR}) if (USE_GUI) target_link_libraries( occvis occ ) diff --git a/libsrc/stlgeom/CMakeLists.txt b/libsrc/stlgeom/CMakeLists.txt index 3620c496..7b87a649 100644 --- a/libsrc/stlgeom/CMakeLists.txt +++ b/libsrc/stlgeom/CMakeLists.txt @@ -4,8 +4,7 @@ add_library(stl ${NG_LIB_TYPE} ) if(NOT WIN32) - target_link_libraries( stl mesh ${PYTHON_LIBRARIES}) - target_link_libraries( stl ${PYTHON_LIBRARIES}) + target_link_libraries( stl ngcore mesh ${PYTHON_LIBRARIES}) install( TARGETS stl ${NG_INSTALL_DIR}) endif(NOT WIN32) diff --git a/libsrc/visualization/CMakeLists.txt b/libsrc/visualization/CMakeLists.txt index 8f803d92..f1c82976 100644 --- a/libsrc/visualization/CMakeLists.txt +++ b/libsrc/visualization/CMakeLists.txt @@ -10,7 +10,7 @@ endif(USE_GUI) add_library(visual ${NG_LIB_TYPE} ${LIB_VISUAL_SOURCES}) if(NOT WIN32) - target_link_libraries( visual ${PYTHON_LIBRARIES} ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} ) + target_link_libraries( visual ngcore ${PYTHON_LIBRARIES} ${MPI_CXX_LIBRARIES} ${OPENGL_LIBRARIES} ) install( TARGETS visual ${NG_INSTALL_DIR}) endif(NOT WIN32) diff --git a/tests/build_nospdlog.sh b/tests/build_nospdlog.sh new file mode 100644 index 00000000..2fedbef2 --- /dev/null +++ b/tests/build_nospdlog.sh @@ -0,0 +1,8 @@ +cd +mkdir -p build/netgen +cd build/netgen +cmake ../../src/netgen -DUSE_CCACHE=ON -DUSE_SPDLOG=OFF -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_C_COMPILER=clang +make -j12 +make install + diff --git a/tests/catch/CMakeLists.txt b/tests/catch/CMakeLists.txt index fc0ebe41..58386884 100644 --- a/tests/catch/CMakeLists.txt +++ b/tests/catch/CMakeLists.txt @@ -3,8 +3,7 @@ if(ENABLE_UNIT_TESTS) add_custom_target(unit_tests) # Build catch_main test object -message("netgen include dir = ${NETGEN_INCLUDE_DIR_ABSOLUTE} --------------------------------------") -include_directories(${CATCH_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libsrc/include}) +include_directories(${CATCH_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libsrc/include ${SPDLOG_INCLUDE_DIR}) add_library(catch_main STATIC main.cpp) set_target_properties(catch_main PROPERTIES CXX_STANDARD 17) add_dependencies(unit_tests catch_main) @@ -15,11 +14,7 @@ add_test(NAME unit_tests_built COMMAND ${CMAKE_COMMAND} --build . --target unit_ macro(add_unit_test name sources) add_executable(test_${name} ${sources} ) - if (WIN32) - target_link_libraries(test_${name} ngcore catch_main) - else(WIN32) - target_link_libraries(test_${name} ngcore catch_main) - endif(WIN32) + target_link_libraries(test_${name} ngcore catch_main) add_dependencies(unit_tests test_${name}) add_test(NAME unit_${name} COMMAND test_${name})