diff --git a/ProjectMacros.cmake b/ProjectMacros.cmake index 9a06bdec2c8..b02c5f25969 100644 --- a/ProjectMacros.cmake +++ b/ProjectMacros.cmake @@ -281,6 +281,7 @@ macro(MAKE_SWIG_TARGET NAME SIMPLENAME KEY_I_FILE I_FILES PARENT_TARGET PARENT_S set_target_properties(${swig_target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ruby/") target_link_libraries(${swig_target} ${${PARENT_TARGET}_depends}) target_include_directories(${swig_target} SYSTEM PRIVATE ${RUBY_INCLUDE_DIRS}) + target_compile_definitions(${swig_target} PRIVATE SHARED_OS_LIBS) add_dependencies(${swig_target} ${PARENT_TARGET}) execute_process(COMMAND \"${CMAKE_COMMAND}\" -E copy \"\${resolved_item_var}\" \"\${CMAKE_INSTALL_PREFIX}/Ruby/openstudio/\") @@ -355,6 +356,7 @@ macro(MAKE_SWIG_TARGET NAME SIMPLENAME KEY_I_FILE I_FILES PARENT_TARGET PARENT_S ${SWIG_WRAPPER} ) + target_compile_definitions(${swig_target} PRIVATE SHARED_OS_LIBS) set_target_properties(${swig_target} PROPERTIES OUTPUT_NAME _${LOWER_NAME}) set_target_properties(${swig_target} PROPERTIES PREFIX "") set_target_properties(${swig_target} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/python/") diff --git a/python/engine/CMakeLists.txt b/python/engine/CMakeLists.txt index 1343becdea3..346a0e62a08 100644 --- a/python/engine/CMakeLists.txt +++ b/python/engine/CMakeLists.txt @@ -30,8 +30,8 @@ target_include_directories(pythonengine target_link_libraries( pythonengine PRIVATE + openstudiolib openstudio_scriptengine - openstudio_utilities_minimal CONAN_PKG::fmt Python::Python ) diff --git a/src/airflow/Airflow.i b/src/airflow/Airflow.i index ddbe67984e2..c2147d18c92 100644 --- a/src/airflow/Airflow.i +++ b/src/airflow/Airflow.i @@ -5,7 +5,7 @@ %module openstudioairflow #endif -#define UTILITIES_API +%include #define AIRFLOW_API %include diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index b4777f3b707..6a0315f1247 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -82,6 +82,15 @@ endif() ############################################################################### if(BUILD_TESTING) + # openstudio_tests is a test target for C++ based tests that extend across multiple + # compiled units such as the case for openstudio cli. + # For example, here is a good place to test issues that are concerned with + # the complexities of sharing data between the Python and Ruby script engines. + set(openstudio_tests_src + "test/Logger_GTest.cpp" + ) + CREATE_TEST_TARGETS(openstudio "${openstudio_tests_src}" openstudiolib openstudio_workflow) + add_dependencies(openstudio_tests openstudio) # Just for convenience: having to VT the seb model which is at 1.11.5 can be very time consuming, especially on debug builds add_custom_target(update_seb_osm_in_build_dir diff --git a/src/cli/main.cpp b/src/cli/main.cpp index a4453fc6301..0763eaf614a 100644 --- a/src/cli/main.cpp +++ b/src/cli/main.cpp @@ -80,14 +80,6 @@ int main(int argc, char* argv[]) { "└{0:─^{2}}┘", "", "The `classic` command is deprecated and will be removed in a future release", 80); fmt::print("\n"); -#if defined _WIN32 - // Poor man's hack #4847 - // Disable this logger, we have a duplicate in the ruby shared lib - openstudio::Logger::instance().standardOutLogger().disable(); - openstudio::Logger::instance().standardErrLogger().disable(); - // Avoid getting some messages during getOpenStudioModule() when we locate the DLL - openstudio::StringStreamLogSink sink; -#endif result = openstudio::rubyCLI(rubyEngine); } else { CLI::App app{"openstudio"}; diff --git a/src/cli/test/Logger_GTest.cpp b/src/cli/test/Logger_GTest.cpp new file mode 100644 index 00000000000..3fa6a48449e --- /dev/null +++ b/src/cli/test/Logger_GTest.cpp @@ -0,0 +1,24 @@ +#include "../../scriptengine/ScriptEngine.hpp" +#include "../../utilities/core/Logger.hpp" + +#include + +// cppcheck-suppress syntaxError +TEST(OpenStudioTest, LoggerGlobal) { + auto isEnabled = openstudio::Logger::instance().standardOutLogger().isEnabled(); + EXPECT_TRUE(isEnabled); + + openstudio::ScriptEngineInstance pythonEngine("pythonengine", {}); + openstudio::ScriptEngineInstance rubyEngine("rubyengine", {}); + + pythonEngine->exec("openstudio.Logger.instance().standardOutLogger().disable()"); + isEnabled = openstudio::Logger::instance().standardOutLogger().isEnabled(); + EXPECT_FALSE(isEnabled); + + rubyEngine->exec("OpenStudio::Logger::instance().standardOutLogger().enable()"); + isEnabled = openstudio::Logger::instance().standardOutLogger().isEnabled(); + EXPECT_TRUE(isEnabled); + + pythonEngine.reset(); + rubyEngine.reset(); +} diff --git a/src/energyplus/EnergyPlus.i b/src/energyplus/EnergyPlus.i index fdafa8f52da..fd7b3c7c80d 100644 --- a/src/energyplus/EnergyPlus.i +++ b/src/energyplus/EnergyPlus.i @@ -6,7 +6,7 @@ #endif -#define UTILITIES_API +%include #define MODEL_API #define ENERGYPLUS_API diff --git a/src/epjson/epJSON.i b/src/epjson/epJSON.i index 3a142272df4..e90de710cdc 100644 --- a/src/epjson/epJSON.i +++ b/src/epjson/epJSON.i @@ -6,7 +6,7 @@ #endif -#define UTILITIES_API +%include #define EPJSON_API // You're better off just loading the json directly in the target language, so ignore diff --git a/src/gbxml/gbXML.i b/src/gbxml/gbXML.i index bafe91eccd1..42523eadb38 100644 --- a/src/gbxml/gbXML.i +++ b/src/gbxml/gbXML.i @@ -6,7 +6,7 @@ #endif -#define UTILITIES_API +%include #define GBXML_API %include diff --git a/src/gltf/Gltf.i b/src/gltf/Gltf.i index fddf3ddf86a..a4b8d69afcd 100644 --- a/src/gltf/Gltf.i +++ b/src/gltf/Gltf.i @@ -5,7 +5,7 @@ %module openstudiogltf #endif -#define UTILITIES_API +%include #define MODEL_API #define GLTF_API diff --git a/src/isomodel/ISOModel.i b/src/isomodel/ISOModel.i index a55f8206f29..95451791acc 100644 --- a/src/isomodel/ISOModel.i +++ b/src/isomodel/ISOModel.i @@ -5,7 +5,7 @@ %module openstudioisomodel #endif -#define UTILITIES_API +%include #define ISOMODEL_API %include diff --git a/src/measure/Measure.i b/src/measure/Measure.i index a3cedd922cd..604b70f1381 100644 --- a/src/measure/Measure.i +++ b/src/measure/Measure.i @@ -6,7 +6,7 @@ #endif -#define UTILITIES_API +%include #define MODEL_API #define STANDARDSINTERFACE_API #define MEASURE_API diff --git a/src/model/ModelGeometry.i b/src/model/ModelGeometry.i index b2113b882dc..94bb4a01921 100644 --- a/src/model/ModelGeometry.i +++ b/src/model/ModelGeometry.i @@ -6,6 +6,7 @@ #endif +%include %include %import %import diff --git a/src/model/Model_Common_Include.i b/src/model/Model_Common_Include.i index da4d4b71f73..783ce4cb130 100644 --- a/src/model/Model_Common_Include.i +++ b/src/model/Model_Common_Include.i @@ -1,7 +1,7 @@ #ifndef MODEL_COMMON_INCLUDE_I #define MODEL_COMMON_INCLUDE_I -#define UTILITIES_API +%include #define MODEL_API %include diff --git a/src/osversion/OSVersion.i b/src/osversion/OSVersion.i index 79757ea46b3..d8889b91404 100644 --- a/src/osversion/OSVersion.i +++ b/src/osversion/OSVersion.i @@ -6,7 +6,7 @@ #endif -#define UTILITIES_API +%include #define MODEL_API #define OSVERSION_API diff --git a/src/radiance/AnnualIlluminanceMap.i b/src/radiance/AnnualIlluminanceMap.i index 7f2b70a2c53..17e0d62fe28 100644 --- a/src/radiance/AnnualIlluminanceMap.i +++ b/src/radiance/AnnualIlluminanceMap.i @@ -1,7 +1,7 @@ #ifndef RADIANCE_ANNUALILLUMINANCEMAP_I #define RADIANCE_ANNUALILLUMINANCEMAP_I -#define UTILITIES_API +%include #define RADIANCE_API %include diff --git a/src/radiance/HeaderInfo.i b/src/radiance/HeaderInfo.i index d16280700b7..399c38753d8 100644 --- a/src/radiance/HeaderInfo.i +++ b/src/radiance/HeaderInfo.i @@ -1,7 +1,7 @@ #ifndef RADIANCE_HEADERINFO_I #define RADIANCE_HEADERINFO_I -#define UTILITIES_API +%include #define RADIANCE_API %include diff --git a/src/radiance/Radiance.i b/src/radiance/Radiance.i index c5307866fe8..860defa3fb4 100644 --- a/src/radiance/Radiance.i +++ b/src/radiance/Radiance.i @@ -6,7 +6,7 @@ #endif -#define UTILITIES_API +%include #define RADIANCE_API %include diff --git a/src/sdd/SDD.i b/src/sdd/SDD.i index 4eb762bc14e..e61ea4a0122 100644 --- a/src/sdd/SDD.i +++ b/src/sdd/SDD.i @@ -6,7 +6,7 @@ #endif -#define UTILITIES_API +%include #define SDD_API %include diff --git a/src/utilities/Utilities.i b/src/utilities/Utilities.i index 048a129feba..a43f8906e38 100644 --- a/src/utilities/Utilities.i +++ b/src/utilities/Utilities.i @@ -6,7 +6,7 @@ #endif -#define UTILITIES_API +%include #define UTILITIES_TEMPLATE_EXT %include diff --git a/src/utilities/UtilitiesBCL.i b/src/utilities/UtilitiesBCL.i index c88f9f4afbb..eaec52d9742 100644 --- a/src/utilities/UtilitiesBCL.i +++ b/src/utilities/UtilitiesBCL.i @@ -7,8 +7,7 @@ #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT +%include %include %import diff --git a/src/utilities/UtilitiesCore.i b/src/utilities/UtilitiesCore.i index 1317a820269..894c99670c0 100644 --- a/src/utilities/UtilitiesCore.i +++ b/src/utilities/UtilitiesCore.i @@ -5,9 +5,7 @@ %module openstudioutilitiescore #endif - -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT +%include %include %include // CommonImport is included in this module diff --git a/src/utilities/UtilitiesData.i b/src/utilities/UtilitiesData.i index e94231ea114..9e64e1740d3 100644 --- a/src/utilities/UtilitiesData.i +++ b/src/utilities/UtilitiesData.i @@ -6,9 +6,7 @@ #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT - +%include %include %import %import diff --git a/src/utilities/UtilitiesFileTypes.i b/src/utilities/UtilitiesFileTypes.i index 9e48bae453e..a5643b46e13 100644 --- a/src/utilities/UtilitiesFileTypes.i +++ b/src/utilities/UtilitiesFileTypes.i @@ -5,9 +5,7 @@ %module openstudioutilitiesfiletypes #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT - +%include %include %import %import diff --git a/src/utilities/UtilitiesGeometry.i b/src/utilities/UtilitiesGeometry.i index 2f858b12b6b..51256a926d0 100644 --- a/src/utilities/UtilitiesGeometry.i +++ b/src/utilities/UtilitiesGeometry.i @@ -6,9 +6,7 @@ #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT - +%include %include %import %import diff --git a/src/utilities/UtilitiesIdd.i b/src/utilities/UtilitiesIdd.i index a138515bc30..025afc2a4ea 100644 --- a/src/utilities/UtilitiesIdd.i +++ b/src/utilities/UtilitiesIdd.i @@ -6,9 +6,7 @@ #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT - +%include %include %import %import diff --git a/src/utilities/UtilitiesIdf.i b/src/utilities/UtilitiesIdf.i index 0a593175449..b7a6ac8c574 100644 --- a/src/utilities/UtilitiesIdf.i +++ b/src/utilities/UtilitiesIdf.i @@ -6,9 +6,7 @@ #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT - +%include %include %import %import diff --git a/src/utilities/UtilitiesPlot.i b/src/utilities/UtilitiesPlot.i index b897c4c6d66..3395c8fc00a 100644 --- a/src/utilities/UtilitiesPlot.i +++ b/src/utilities/UtilitiesPlot.i @@ -6,9 +6,7 @@ #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT - +%include %include %import %import diff --git a/src/utilities/UtilitiesSql.i b/src/utilities/UtilitiesSql.i index 5509fb2fb45..948e467c9d4 100644 --- a/src/utilities/UtilitiesSql.i +++ b/src/utilities/UtilitiesSql.i @@ -6,9 +6,7 @@ #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT - +%include %include %import %import diff --git a/src/utilities/UtilitiesTime.i b/src/utilities/UtilitiesTime.i index 71e7c237bc1..6ebea993931 100644 --- a/src/utilities/UtilitiesTime.i +++ b/src/utilities/UtilitiesTime.i @@ -6,9 +6,7 @@ #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT - +%include %include %import %import diff --git a/src/utilities/UtilitiesUnits.i b/src/utilities/UtilitiesUnits.i index 2a87b1eabcb..744bbefe8f9 100644 --- a/src/utilities/UtilitiesUnits.i +++ b/src/utilities/UtilitiesUnits.i @@ -6,9 +6,7 @@ #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT - +%include %include %import %import diff --git a/src/utilities/UtilitiesXML.i b/src/utilities/UtilitiesXML.i index af242d86922..21566eb9ca0 100644 --- a/src/utilities/UtilitiesXML.i +++ b/src/utilities/UtilitiesXML.i @@ -6,9 +6,7 @@ #endif -#define UTILITIES_API -#define UTILITIES_TEMPLATE_EXT - +%include %include %import %import diff --git a/src/utilities/core/LogSink.hpp b/src/utilities/core/LogSink.hpp index e871fe6e072..df737967bf8 100644 --- a/src/utilities/core/LogSink.hpp +++ b/src/utilities/core/LogSink.hpp @@ -18,9 +18,11 @@ namespace openstudio { +class Logger; + namespace detail { class LogSink_Impl; -} +} // namespace detail /// LogSink is a class for managing sinks for log messages, e.g. files, streams, etc. class UTILITIES_API LogSink @@ -76,7 +78,7 @@ class UTILITIES_API LogSink void setFormatter(const boost::log::formatter& fmter); protected: - friend class LoggerSingleton; + friend class Logger; // does not register in the global logger LogSink(); diff --git a/src/utilities/core/Logger.cpp b/src/utilities/core/Logger.cpp index 4967ebfb934..020f70b4135 100644 --- a/src/utilities/core/Logger.cpp +++ b/src/utilities/core/Logger.cpp @@ -11,8 +11,7 @@ #include -namespace sinks = boost::log::sinks; -namespace keywords = boost::log::keywords; +#include namespace openstudio { @@ -21,7 +20,13 @@ void logFree(LogLevel level, const std::string& channel, const std::string& mess BOOST_LOG_SEV(openstudio::Logger::instance().loggerFromChannel(channel), level) << message; } -LoggerSingleton::LoggerSingleton() { +// Meyers' singleton +Logger& Logger::instance() { + static Logger instance; + return instance; +} + +Logger::Logger() { // Make current thread id attribute available to logging boost::log::core::get()->add_global_attribute("ThreadId", boost::log::attributes::make_function(&std::this_thread::get_id)); @@ -38,47 +43,39 @@ LoggerSingleton::LoggerSingleton() { //this->addSink(m_standardErrLogger.sink()); } -LoggerSingleton::~LoggerSingleton() { - // unregister Qt message handler - //qInstallMsgHandler(consoleLogQtMessage); -} - -LogSink LoggerSingleton::standardOutLogger() const { +LogSink Logger::standardOutLogger() const { std::shared_lock l{m_mutex}; return m_standardOutLogger; } -LogSink LoggerSingleton::standardErrLogger() const { +LogSink Logger::standardErrLogger() const { std::shared_lock l{m_mutex}; return m_standardErrLogger; } -LoggerType& LoggerSingleton::loggerFromChannel(const LogChannel& logChannel) { +LoggerType& Logger::loggerFromChannel(const LogChannel& logChannel) { std::shared_lock l{m_mutex}; auto it = m_loggerMap.find(logChannel); if (it == m_loggerMap.end()) { - //LoggerType newLogger(keywords::channel = logChannel, keywords::severity = Debug); - LoggerType newLogger(keywords::channel = logChannel); - - std::pair newPair(logChannel, newLogger); - // Drop the read lock and grab a write lock - we need to add the new file to the map // this will reduce contention when multiple threads trying to log at once. l.unlock(); std::unique_lock l2{m_mutex}; - std::pair inserted = m_loggerMap.insert(newPair); + //LoggerType newLogger(boost::log::keywords::channel = logChannel, boost::log::keywords::severity = Debug); + // No need for try_emplace, this we checked it wasn't in the map + auto [it2, inserted] = m_loggerMap.emplace(logChannel, LoggerType(boost::log::keywords::channel = logChannel)); - return inserted.first->second; + return it2->second; } return it->second; } -bool LoggerSingleton::findSink(boost::shared_ptr sink) { +bool Logger::findSink(boost::shared_ptr sink) { std::unique_lock l{m_mutex}; auto it = m_sinks.find(sink); @@ -86,7 +83,7 @@ bool LoggerSingleton::findSink(boost::shared_ptr sink) { return (it != m_sinks.end()); } -void LoggerSingleton::addSink(boost::shared_ptr sink) { +void Logger::addSink(boost::shared_ptr sink) { std::shared_lock l{m_mutex}; auto it = m_sinks.find(sink); @@ -104,7 +101,7 @@ void LoggerSingleton::addSink(boost::shared_ptr sink) { } } -void LoggerSingleton::removeSink(boost::shared_ptr sink) { +void Logger::removeSink(boost::shared_ptr sink) { std::shared_lock l{m_mutex}; auto it = m_sinks.find(sink); @@ -122,7 +119,7 @@ void LoggerSingleton::removeSink(boost::shared_ptr sink) { } } -void LoggerSingleton::addTimeStampToLogger() { +void Logger::addTimeStampToLogger() { std::unique_lock l{m_mutex}; // Add a TimeStamp attribute, same as boost::log::add_common_attributes() would do diff --git a/src/utilities/core/Logger.hpp b/src/utilities/core/Logger.hpp index dceab417755..6c848a472fa 100644 --- a/src/utilities/core/Logger.hpp +++ b/src/utilities/core/Logger.hpp @@ -8,17 +8,18 @@ #include "../UtilitiesAPI.hpp" -#include "Singleton.hpp" -#include "Exception.hpp" #include "Compare.hpp" +#include "LogMessage.hpp" #include "LogSink.hpp" +#include "Exception.hpp" #include -#include -#include #include +#include +#include #include +#include /// defines method logChannel() to get a logger for a class #define REGISTER_LOGGER(__logChannel__) \ @@ -52,21 +53,23 @@ namespace openstudio { class OSWorkflow; +class LogSink; +namespace detail { + class LogSink_Impl; +} // namespace detail /// convenience function for SWIG, prefer macros in C++ UTILITIES_API void logFree(LogLevel level, const std::string& channel, const std::string& message); -/** Singleton logger class. Singleton Logger object maintains logging state throughout - * program execution. - */ -class UTILITIES_API LoggerSingleton +class UTILITIES_API Logger { - - friend class Singleton; - public: - /// destructor, cleans up, writes xml file footers, etc - ~LoggerSingleton(); + static Logger& instance(); + + Logger(const Logger& other) = delete; + Logger(Logger&& other) = delete; + Logger& operator=(const Logger&) = delete; + Logger& operator=(Logger&&) = delete; /// get logger for standard out LogSink standardOutLogger() const; @@ -95,8 +98,8 @@ class UTILITIES_API LoggerSingleton void addTimeStampToLogger(); private: - /// private constructor - LoggerSingleton(); + Logger(); + ~Logger() = default; mutable std::shared_mutex m_mutex; @@ -115,15 +118,6 @@ class UTILITIES_API LoggerSingleton SinkSetType m_sinks; }; -#if _WIN32 || _MSC_VER - -/// Explicitly instantiate and export LoggerSingleton Singleton template instance -/// so that the same instance is shared between the DLL's that link to Utilities.dll -UTILITIES_TEMPLATE_EXT template class UTILITIES_API openstudio::Singleton; - -#endif - -using Logger = openstudio::Singleton; } // namespace openstudio #endif // UTILITIES_CORE_LOGGER_HPP diff --git a/src/utilities/core/Logger.i b/src/utilities/core/Logger.i index 4178110f9f7..cb0961b79ac 100644 --- a/src/utilities/core/Logger.i +++ b/src/utilities/core/Logger.i @@ -22,14 +22,12 @@ %ignore std::vector::vector(size_type); %ignore std::vector::resize(size_type); -%ignore openstudio::LoggerSingleton::loggerFromChannel; +%ignore openstudio::Logger::loggerFromChannel; %ignore openstudio::LogSink::setFormatter; %template(LogMessageVector) std::vector; %template(OptionalLogMessage) boost::optional; -%template(Logger) openstudio::Singleton; - // Ignore std::thread::id stuff %ignore openstudio::LogSink::threadId; %ignore openstudio::LogSink::setThreadId;