diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cf0c54da69..55b64cc442d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1005,6 +1005,7 @@ set(project_directories sdd lib install_utility + scriptengine ) # resources must be added after find EnergyPlus diff --git a/ruby/CMakeLists.txt b/ruby/CMakeLists.txt index 951372199d4..ab63ca7a73e 100644 --- a/ruby/CMakeLists.txt +++ b/ruby/CMakeLists.txt @@ -1,191 +1,8 @@ -if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(Apple)?Clang$") - # using Clang - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -undefined dynamic_lookup") -elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # using GCC - #set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --unresolved-symbols=ignore-all") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --unresolved-symbols=ignore-all") -endif() - - -include_directories(${CMAKE_CURRENT_BINARY_DIR} ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) - -# common library for openstudio.so and openstudio.exe - -add_library( init_openstudio OBJECT - init_openstudio.cpp - RubyAPI.hpp -) -target_include_directories(init_openstudio SYSTEM PRIVATE ${RUBY_INCLUDE_DIRS}) - -if(MSVC) - # TODO: JM 2019-01-04: @macumber it seems like all three targets use the same COMPILE_FLAGS, - # so instead of defining that three times, perhaps modify CMAKE_CXX_FLAGS? - # wd4996=no deprecated warnings ; wd5033=register - set_target_properties(init_openstudio PROPERTIES COMPILE_FLAGS "/bigobj /wd4996 /wd5033") -elseif (UNIX) - # Disable register warnings - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=register -Wno-register") -endif() - - -# openstudio ruby bindings to load into normal ruby - -add_library( - openstudio_rb - MODULE - RubyAPI.hpp - openstudio_rb.cpp -) -set_target_properties(openstudio_rb PROPERTIES PREFIX "") -set_target_properties(openstudio_rb PROPERTIES OUTPUT_NAME openstudio) -target_include_directories(openstudio_rb SYSTEM PRIVATE ${RUBY_INCLUDE_DIRS}) - -# change output directory so the openstudio.pdb file does not conflict between this target and the CLI -set_target_properties(openstudio_rb PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/ruby/") -set_target_properties(openstudio_rb PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ruby/") -set_target_properties(openstudio_rb PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ruby/") - -if(APPLE) - set_target_properties(openstudio_rb PROPERTIES SUFFIX ".bundle" ) -else() - set_target_properties(openstudio_rb PROPERTIES SUFFIX ".so" ) -endif() - -if(MSVC) - set_target_properties(openstudio_rb PROPERTIES COMPILE_FLAGS "/bigobj /wd4996 /wd5033") -endif() - -add_custom_command(TARGET openstudio_rb - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/openstudio.rb $/openstudio.rb -) - -file(GLOB_RECURSE OPENSTUDIO_FILES FOLLOW_SYMLINKS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/openstudio/" "${CMAKE_CURRENT_SOURCE_DIR}/openstudio/**/*.*") -foreach(OPENSTUDIO_FILE ${OPENSTUDIO_FILES}) - add_custom_command(TARGET openstudio_rb - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/openstudio/${OPENSTUDIO_FILE}" "$/openstudio/${OPENSTUDIO_FILE}" - ) -endforeach() - - -target_link_libraries( - openstudio_rb - PRIVATE - init_openstudio - ${ALL_RUBY_BINDING_TARGETS} -) - -if( WIN32 ) - # Dynamically link to openstudiolib.dll on Windows only - target_link_libraries( - openstudio_rb - PUBLIC - openstudiolib - ) - target_link_libraries(openstudio_rb PRIVATE ${RUBY_MINGW_STUB_LIB}) -else() - # statically link everywhere else - target_link_libraries( - openstudio_rb - PRIVATE - openstudio_utilities - openstudio_airflow - openstudio_model - openstudio_energyplus - openstudio_epjson - openstudio_measure - openstudio_osversion - openstudio_sdd - openstudio_isomodel - openstudio_gbxml - openstudio_gltf - openstudio_radiance - ) -endif() - -if (APPLE) - target_link_libraries(openstudio_rb - PRIVATE - ${OPENSSL_CRYPTO_LIBRARY} - ${OPENSSL_SSL_LIBRARY} - ) -endif() - - -install(TARGETS openstudio_rb - EXPORT openstudio - DESTINATION Ruby - COMPONENT "RubyAPI" - INCLUDES DESTINATION include include/openstudio -) -install(FILES openstudio.rb DESTINATION Ruby COMPONENT "RubyAPI") -install(DIRECTORY openstudio DESTINATION Ruby COMPONENT "RubyAPI") - - -############################################################################### -# T E S T I N G: C T E S T S # -############################################################################### - -# find all tests -file(GLOB RUBY_TEST_SRC "test/*.rb") - -# TODO: It doesn't not work with this executable that's in build/Ruby-install/bin -# message("RUBY_EXECUTABLE=${RUBY_EXECUTABLE}") - -set(_RUBY_POSSIBLE_EXECUTABLE_NAMES - ruby - ruby2.7 - ruby2.7.2 - ruby27 - ruby272) - -# TODO: this isn't great but I haven't found a better way to locate the system ruby (and avoid the one in build/Ruby-install/) -find_program(SYSTEM_RUBY_EXECUTABLE NAMES ${_RUBY_POSSIBLE_EXECUTABLE_NAMES} - HINTS - "/usr/local/rvm/rubies/ruby-2.7.2/bin/" - "/usr/local/rvm/rubies/ruby-2.7.2/bin/ruby" - "/usr/share/rvm/rubies/ruby-2.7.2/bin/" - "$ENV{HOME}/.rvm/rubies/ruby-2.7.2/bin/" - - "C:/Ruby27-x64/bin/" - - "/usr/local/ruby272/bin/" - "/usr/local/ruby27/bin/" - "/usr/bin/" - "/usr/local/bin/" - - - NO_DEFAULT_PATH) - -if(NOT SYSTEM_RUBY_EXECUTABLE) - message(WARNING "Your system ruby wasn't found, you won't be able to run the `ctest -R RubyTest` command and the tests won't be created at all.") -else() - message(STATUS "Found SYSTEM_RUBY_EXECUTABLE=${SYSTEM_RUBY_EXECUTABLE}") - - # add a test for each unit test - set(RUBY_TEST_REQUIRES "#include test files") - foreach(f ${RUBY_TEST_SRC}) - - file(READ "${f}" CONTENTS) - string(REGEX MATCHALL "def +test_([A-Za-z_0-9 ]+)" FOUND_TESTS ${CONTENTS}) - - foreach(HIT ${FOUND_TESTS}) - string(REGEX REPLACE "def +test_([A-Za-z_0-9]+)" "\\1" TEST_NAME ${HIT}) - string(REGEX MATCH "/?([A-Za-z_0-9 ]+)\\.rb" FILE_NAME ${f}) - string(REGEX REPLACE "/?([A-Za-z_0-9 ]+)\\.rb" "\\1" FILE_NAME ${FILE_NAME}) - if(BUILD_TESTING) - - # Call with Ruby itself - add_test(NAME "RubyTest-${FILE_NAME}-${TEST_NAME}" - COMMAND "${CMAKE_COMMAND}" -E chdir "${CMAKE_CURRENT_BINARY_DIR}" - "${SYSTEM_RUBY_EXECUTABLE}" "-I" "$" "${f}" "--name=test_${TEST_NAME}" - ) - - set_tests_properties("RubyTest-${FILE_NAME}-${TEST_NAME}" PROPERTIES TIMEOUT 660 ) - endif() - endforeach() - endforeach() - -endif() +# OpenStudio Ruby components + +add_subdirectory(interpreter) +add_subdirectory(bindings) +add_subdirectory(module) +add_subdirectory(engine) +# TODO: Enable this +#add_subdirectory(test) diff --git a/ruby/README.md b/ruby/README.md new file mode 100644 index 00000000000..a08e601d524 --- /dev/null +++ b/ruby/README.md @@ -0,0 +1,19 @@ +# OpenStudio Ruby + +OpenStudio's Ruby implementation is divided into several pieces + +* `interpreter`: This is the bare bones Ruby interpreter with only some very minimal OpenStudio +initializations and abstractions. There are two separate cmake targets related to the interpreter. + +** The target `rubyinterpreter-delcarations` is an interface library that merely adds Ruby to the include path +This is used by the standalone ruby bindings. +** The target `rubyinterpreter` adds Ruby to the include paths, but also links to the Ruby library. +This is used by the OpenStudio Ruby engine. + +* `bindings`: This contains an interface to initialize the OpenStudio API bindings for Ruby. (e.g. `init_openstudio_model`) +The `module` and `engine` components consume this. + +* `module`: This is the conentional OpenStudio Ruby bindings, for loading into a standalone Ruby interpreter. + +* `engine`: This is a combination of the OpenStudio Ruby bindings, the Ruby interpreter, and embedded files +to be used by the OpenStudio cli to run Ruby based workflows. diff --git a/ruby/bindings/CMakeLists.txt b/ruby/bindings/CMakeLists.txt new file mode 100644 index 00000000000..622309d1899 --- /dev/null +++ b/ruby/bindings/CMakeLists.txt @@ -0,0 +1,15 @@ +add_library( rubybindings OBJECT + InitRubyBindings.hpp + InitRubyBindings.cpp +) + +target_include_directories(rubybindings PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(rubybindings PUBLIC ${ALL_RUBY_BINDING_TARGETS} rubyinterpreter-delcarations) + +if(MSVC) + # wd4996=no deprecated warnings ; wd5033=register + set_target_properties(rubybindings PROPERTIES COMPILE_FLAGS "/bigobj /wd4996 /wd5033") +elseif (UNIX) + # Disable register warnings + set_target_properties(rubybindings PROPERTIES COMPILE_FLAGS " -Wno-error=register -Wno-register") +endif() diff --git a/ruby/init_openstudio.cpp b/ruby/bindings/InitRubyBindings.cpp similarity index 93% rename from ruby/init_openstudio.cpp rename to ruby/bindings/InitRubyBindings.cpp index 20a94ad7a00..a282d892598 100644 --- a/ruby/init_openstudio.cpp +++ b/ruby/bindings/InitRubyBindings.cpp @@ -27,7 +27,8 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************************************************************/ -#include "init_openstudio.hpp" +#include "InitRubyBindings.hpp" +#include "RubyEval.hpp" #include #include #include @@ -74,7 +75,10 @@ extern "C" ////void Init_openstudiomodeleditor(void); # happens separately in openstudio.so only, for SketchUp plug-in } -void init_openstudio_internal_basic() { +namespace openstudio { +namespace ruby { + +void initBasicRubyBindings() { rb_provide("openstudio"); rb_provide("openstudio.so"); @@ -119,7 +123,7 @@ void init_openstudio_internal_basic() { rb_provide("openstudioutilities.so"); } -void init_openstudio_internal_extended() { +void initExtendedRubyBindings() { Init_openstudiomodel(); rb_provide("openstudiomodel"); rb_provide("openstudiomodel.so"); @@ -366,49 +370,13 @@ end # module OpenStudio )END"; - evalString(ruby_typedef_script); -} - -void init_openstudio_internal() { - init_openstudio_internal_basic(); - init_openstudio_internal_extended(); + openstudio::evalString(ruby_typedef_script); } -class RubyException : public std::runtime_error -{ - public: - RubyException(const std::string& msg, const std::string& location) : std::runtime_error(msg), m_location(location) {} - - virtual ~RubyException() throw() {} - - std::string location() const { - return m_location; - } - - private: - std::string m_location; -}; - -static VALUE evaluateSimpleImpl(VALUE arg) { - return rb_eval_string(StringValuePtr(arg)); +void initRubyBindings() { + initBasicRubyBindings(); + initExtendedRubyBindings(); } -void evalString(const std::string& t_str) { - - VALUE val = rb_str_new2(t_str.c_str()); - int error; - - rb_protect(evaluateSimpleImpl, val, &error); - - if (error != 0) { - VALUE errval = rb_eval_string("$!.to_s"); - char* str = StringValuePtr(errval); - std::string err(str); - - VALUE locval = rb_eval_string("$@.to_s"); - str = StringValuePtr(locval); - std::string loc(str); - - throw RubyException(err, loc); - } -} +} // namespace ruby +} // namespace openstudio diff --git a/ruby/bindings/InitRubyBindings.hpp b/ruby/bindings/InitRubyBindings.hpp new file mode 100644 index 00000000000..14428e537b4 --- /dev/null +++ b/ruby/bindings/InitRubyBindings.hpp @@ -0,0 +1,14 @@ +#ifndef INITRUBYBINDINGS_HPP +#define INITRUBYBINDINGS_HPP + +namespace openstudio { +namespace ruby { + +void initRubyBindings(); +void initBasicRubyBindings(); +void initExtendedRubyBindings(); + +} // namespace ruby +} // namespace openstudio + +#endif // INITRUBYBINDINGS_HPP diff --git a/ruby/engine/CMakeLists.txt b/ruby/engine/CMakeLists.txt new file mode 100644 index 00000000000..3ad7ec733c7 --- /dev/null +++ b/ruby/engine/CMakeLists.txt @@ -0,0 +1,275 @@ +include("${OPENSTUDIO_GEMS_DIR}/export-extensions.cmake") + +set(INIT_CALLS "") +set(INIT_DECLARATIONS "") +foreach(LIB ${EXTENSION_LIBS_LIST}) + get_filename_component(LIB_NAME ${LIB} NAME_WE) + list (FIND LIBS "${LIB_NAME}" _index) + if( ${_index} GREATER -1 ) + set(INIT_DECLARATIONS "${INIT_DECLARATIONS} void Init_${LIB_NAME}();\\\n" ) + set(INIT_CALLS "${INIT_CALLS} Init_${LIB_NAME}();\\\n" ) + set(INIT_CALLS "${INIT_CALLS} rb_provide(\"${LIB_NAME}.so\");\\\n" ) + endif() +endforeach() + +CONFIGURE_FILE_WITH_CHECKSUM("InitMacros.hxx.in" "${CMAKE_CURRENT_BINARY_DIR}/InitMacros.hxx") + +set(MODULE_ROOT "${CONAN_OPENSTUDIO_RUBY_ROOT}") +if ("${MODULE_ROOT}" STREQUAL "") + set(MODULE_ROOT "${CONAN_OPENSTUDIO_RUBY_ROOT_DEBUG}") +endif() + +message("Searching for ruby modules in '${MODULE_ROOT}/lib/**/*.rb'") +file(GLOB_RECURSE EXTENSION_RB FOLLOW_SYMLINKS "${MODULE_ROOT}/lib/**/*.rb") + +foreach( _FILE ${EXTENSION_RB} ) + file(RELATIVE_PATH LOCATION ${MODULE_ROOT}/lib ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +set(GEMFILE_FILES "${OPENSTUDIO_GEMS_DIR}/openstudio-gems.gemspec" "${PROJECT_BINARY_DIR}/openstudio-gems/Gemfile" "${PROJECT_BINARY_DIR}/openstudio-gems/Gemfile.lock") +file(GLOB_RECURSE RB_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.rb") +file(GLOB_RECURSE ERB_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.erb") +file(GLOB_RECURSE JS_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.js") +file(GLOB_RECURSE CSS_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.css") +file(GLOB_RECURSE GIF_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.gif") +file(GLOB_RECURSE PNG_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.png") +file(GLOB_RECURSE HTML_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.html") +file(GLOB_RECURSE IDF_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.idf") +file(GLOB_RECURSE OSM_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.osm") +file(GLOB_RECURSE EPW_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.epw") +file(GLOB_RECURSE DDY_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.ddy") +file(GLOB_RECURSE STAT_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.stat") +file(GLOB_RECURSE CSV_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.csv") +file(GLOB_RECURSE JSON_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.json") +file(GLOB_RECURSE SPEC_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.gemspec") +file(GLOB_RECURSE GZ_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.gz") +file(GLOB_RECURSE YML_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.yml") +file(GLOB_RECURSE OS_FILES FOLLOW_SYMLINKS "${CMAKE_CURRENT_SOURCE_DIR}/openstudio/**/*.rb") # DLM: this is temporary, these files should live somewhere else and be included in the shared Ruby lib + +foreach( _FILE ${GEMFILE_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${RB_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${ERB_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${JS_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${CSS_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${GIF_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${PNG_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${HTML_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${IDF_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${OSM_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${EPW_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${DDY_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${STAT_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${CSV_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${JSON_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${SPEC_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${GZ_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +foreach( _FILE ${YML_FILES} ) + file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +# DLM: this is temporary, these files should live somewhere else and be included in the shared Ruby lib +foreach( _FILE ${OS_FILES} ) + file(RELATIVE_PATH LOCATION "${CMAKE_CURRENT_SOURCE_DIR}" ${_FILE}) + list(APPEND FILES ${_FILE}) + list(APPEND EMBEDDED_PATHS ${LOCATION}) +endforeach() + +list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/embedded_help.rb") +list(APPEND EMBEDDED_PATHS "embedded_help.rb") + +list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/openstudio_cli.rb") +list(APPEND EMBEDDED_PATHS "openstudio_cli.rb") + +list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/openstudio_init_extended.rb") +list(APPEND EMBEDDED_PATHS "openstudio_init_extended.rb") + + +list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/measure_manager.rb") +list(APPEND EMBEDDED_PATHS "measure_manager.rb") + +list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/measure_manager_server.rb") +list(APPEND EMBEDDED_PATHS "measure_manager_server.rb") + +embed_files("${FILES}" "${EMBEDDED_PATHS}" OUTPUT) + +set_source_files_properties(EmbeddedScripting.i + PROPERTIES CPLUSPLUS true +) + +include_directories(${CMAKE_CURRENT_BINARY_DIR} + ${PROJECT_BINARY_DIR} + ${PROJECT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${OPENSTUDIO_GEMS_DIR} +) + +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" + COMMAND ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_LIB}" + "${SWIG_EXECUTABLE}" + "-ruby" + "-c++" + -o "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" + "-fvirtual" + "-I${PROJECT_SOURCE_DIR}/src" + "-I${PROJECT_BINARY_DIR}/src" + "-I${OPENSTUDIO_GEMS_DIR}" + "-D_WINDOWS" + "-Fmicrosoft" + "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedScripting.i" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedScripting.i" + "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.hxx" + "EmbeddedHelp.hpp" +) + +set_source_files_properties(${EMBED_SOURCE_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) +source_group(embedded_files FILES ${OUTPUT}) + +add_library(rubyengine + SHARED + RubyEngine.hpp + RubyEngine.cpp + InitRubyEngine.cpp + GC_Value.hpp + ${OUTPUT} + "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" + #"${CMAKE_CURRENT_BINARY_DIR}/SWIGRubyRuntime.hxx" +) + +# -Wno-deprecated-declaration, /wd4996: suppresses deprecated warning +# -Wno-register, /wd5033: ISO C++1z does not allow ‘registerÂ’ storage class specifier +if(MSVC) + set_target_properties(rubyengine PROPERTIES COMPILE_FLAGS "/bigobj /wd4996 /wd5033") + set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" PROPERTIES COMPILE_FLAGS "-DRUBY_EMBEDDED" ) +else() + set_target_properties(rubyengine PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-register") + set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" PROPERTIES COMPILE_FLAGS "-DRUBY_EMBEDDED -Wno-unused-variable" ) +endif() + +#add_definitions(-DRUBY_EXTCONF_H="osruby_config.h") + +get_target_property(SCRIPT_ENGINE_INCLUDE_DIRS scriptengine INTERFACE_INCLUDE_DIRECTORIES) +message("SCRIPT_ENGINE_INCLUDE_DIRS: ${SCRIPT_ENGINE_INCLUDE_DIRS}") + +#target_link_libraries(rubyengine PRIVATE osrubyinit CONAN_PKG::openstudio_ruby ${ALL_RUBY_BINDING_TARGETS}) + +if( WIN32 ) + # Dynamically link to openstudiolib.dll on Windows only + target_link_libraries(rubyengine + PRIVATE + openstudiolib + rubybindings + ${ALL_RUBY_BINDING_TARGETS} + ${RUBY_MINGW_STUB_LIB} + ) +else() + target_link_libraries(rubyengine + PRIVATE + rubybindings + scriptengine + rubyinterpreter + openstudio_utilities + openstudio_airflow + openstudio_model + openstudio_energyplus + openstudio_epjson + openstudio_measure + openstudio_osversion + openstudio_sdd + openstudio_isomodel + openstudio_gbxml + openstudio_gltf + openstudio_radiance + rubybindings + ${ALL_RUBY_BINDING_TARGETS} + ) +endif() diff --git a/src/cli/EmbeddedHelp.hpp b/ruby/engine/EmbeddedHelp.hpp similarity index 100% rename from src/cli/EmbeddedHelp.hpp rename to ruby/engine/EmbeddedHelp.hpp diff --git a/src/cli/EmbeddedScripting.i b/ruby/engine/EmbeddedScripting.i similarity index 100% rename from src/cli/EmbeddedScripting.i rename to ruby/engine/EmbeddedScripting.i diff --git a/src/cli/GC_Value.hpp b/ruby/engine/GC_Value.hpp similarity index 100% rename from src/cli/GC_Value.hpp rename to ruby/engine/GC_Value.hpp diff --git a/src/cli/InitMacros.hxx.in b/ruby/engine/InitMacros.hxx.in similarity index 100% rename from src/cli/InitMacros.hxx.in rename to ruby/engine/InitMacros.hxx.in diff --git a/ruby/engine/InitRuby.hpp b/ruby/engine/InitRuby.hpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/ruby/engine/InitRubyEngine.cpp b/ruby/engine/InitRubyEngine.cpp new file mode 100644 index 00000000000..e1e2eee5eda --- /dev/null +++ b/ruby/engine/InitRubyEngine.cpp @@ -0,0 +1,387 @@ +#include "./RubyEngine.hpp" +#include "./InitRubyEngine.hpp" +#include "./GC_Value.hpp" +#include +#include +#include + +namespace openstudio { + +void RubyEngine::initRubyEngine() { + RUBY_INIT_STACK; + ruby_init(); + + swig::GC_VALUE::hash_id = rb_intern("hash"); + swig::GC_VALUE::lt_id = rb_intern("<"); + swig::GC_VALUE::gt_id = rb_intern(">"); + swig::GC_VALUE::eq_id = rb_intern("=="); + swig::GC_VALUE::le_id = rb_intern("<="); + swig::GC_VALUE::ge_id = rb_intern(">="); + + swig::GC_VALUE::pos_id = rb_intern("+@"); + swig::GC_VALUE::neg_id = rb_intern("-@"); + swig::GC_VALUE::inv_id = rb_intern("~"); + + swig::GC_VALUE::add_id = rb_intern("+"); + swig::GC_VALUE::sub_id = rb_intern("-"); + swig::GC_VALUE::mul_id = rb_intern("*"); + swig::GC_VALUE::div_id = rb_intern("/"); + swig::GC_VALUE::mod_id = rb_intern("%"); + + swig::GC_VALUE::and_id = rb_intern("&"); + swig::GC_VALUE::or_id = rb_intern("|"); + swig::GC_VALUE::xor_id = rb_intern("^"); + + swig::GC_VALUE::lshift_id = rb_intern("<<"); + swig::GC_VALUE::rshift_id = rb_intern(">>"); + + // in case any further init methods try to require files, init this first + //Init_EmbeddedScripting(); + + // Need embedded_help for requiring files out of the embedded system + auto embedded_extensions_string = embedded_files::getFileAsString(":/embedded_help.rb"); + + try { + rubyInterpreter.evalString(embedded_extensions_string); + } catch (const std::exception& e) { + rubyInterpreter.evalString(R"(STDOUT.flush)"); + std::cout << "Exception in embedded_help: " << e.what() << std::endl; // endl will flush + ruby_cleanup(1); + throw; + } catch (...) { + rubyInterpreter.evalString(R"(STDOUT.flush)"); + std::cout << "Unknown Exception in embedded_help" << std::endl; // endl will flush + ruby_cleanup(1); + throw; + } + + // encodings + // Get the symbols from: `DUMPBIN /ARCHIVEMEMBERS /lib/enc/libenc.lib` + Init_encdb(); + rb_provide("enc/encdb.so"); + Init_big5(); + rb_provide("enc/big5.so"); + Init_cesu_8(); + rb_provide("enc/cesu_8.so"); + Init_cp949(); + rb_provide("enc/cp949.so"); + Init_emacs_mule(); + rb_provide("enc/emacs_mule.so"); + Init_euc_jp(); + rb_provide("enc/euc_jp.so"); + Init_euc_kr(); + rb_provide("enc/euc_kr.so"); + Init_euc_tw(); + rb_provide("enc/euc_tw.so"); + Init_gb18030(); + rb_provide("enc/gb18030.so"); + Init_gb2312(); + rb_provide("enc/gb2312.so"); + Init_gbk(); + rb_provide("enc/gbk.so"); + Init_iso_8859_1(); + rb_provide("enc/iso_8859_1.so"); + Init_iso_8859_10(); + rb_provide("enc/iso_8859_10.so"); + Init_iso_8859_11(); + rb_provide("enc/iso_8859_11.so"); + Init_iso_8859_13(); + rb_provide("enc/iso_8859_13.so"); + Init_iso_8859_14(); + rb_provide("enc/iso_8859_14.so"); + Init_iso_8859_15(); + rb_provide("enc/iso_8859_15.so"); + Init_iso_8859_16(); + rb_provide("enc/iso_8859_16.so"); + Init_iso_8859_2(); + rb_provide("enc/iso_8859_2.so"); + Init_iso_8859_3(); + rb_provide("enc/iso_8859_3.so"); + Init_iso_8859_4(); + rb_provide("enc/iso_8859_4.so"); + Init_iso_8859_5(); + rb_provide("enc/iso_8859_5.so"); + Init_iso_8859_6(); + rb_provide("enc/iso_8859_6.so"); + Init_iso_8859_7(); + rb_provide("enc/iso_8859_7.so"); + Init_iso_8859_8(); + rb_provide("enc/iso_8859_8.so"); + Init_iso_8859_9(); + rb_provide("enc/iso_8859_9.so"); + Init_koi8_r(); + rb_provide("enc/koi8_r.so"); + Init_koi8_u(); + rb_provide("enc/koi8_u.so"); + Init_shift_jis(); + rb_provide("enc/shift_jis.so"); + Init_utf_16be(); + rb_provide("enc/utf_16be.so"); + Init_utf_16le(); + rb_provide("enc/utf_16le.so"); + Init_utf_32be(); + rb_provide("enc/utf_32be.so"); + Init_utf_32le(); + rb_provide("enc/utf_32le.so"); + Init_windows_1250(); + rb_provide("enc/windows_1250.so"); + Init_windows_1251(); + rb_provide("enc/windows_1251.so"); + Init_windows_1252(); + rb_provide("enc/windows_1252.so"); + Init_windows_1253(); + rb_provide("enc/windows_1253.so"); + Init_windows_1254(); + rb_provide("enc/windows_1254.so"); + Init_windows_1257(); + rb_provide("enc/windows_1257.so"); + Init_windows_31j(); + rb_provide("enc/windows_31j.so"); + + // Get the symbols from: `DUMPBIN /ARCHIVEMEMBERS /lib/enc/libtrans.lib` + Init_transdb(); + rb_provide("enc/trans/transdb.so"); + + Init_trans_big5(); + rb_provide("enc/trans/big5.so"); + + Init_trans_cesu_8(); + rb_provide("enc/trans/cesu_8.so"); + + Init_trans_chinese(); + rb_provide("enc/trans/chinese.so"); + + Init_trans_ebcdic(); + rb_provide("enc/trans/ebcdic.so"); + + Init_trans_emoji(); + rb_provide("enc/trans/emoji.so"); + + Init_trans_emoji_iso2022_kddi(); + rb_provide("enc/trans/emoji_iso2022_kddi.so"); + + Init_trans_emoji_sjis_docomo(); + rb_provide("enc/trans/emoji_sjis_docomo.so"); + + Init_trans_emoji_sjis_kddi(); + rb_provide("enc/trans/emoji_sjis_kddi.so"); + + Init_trans_emoji_sjis_softbank(); + rb_provide("enc/trans/emoji_sjis_softbank.so"); + + Init_trans_escape(); + rb_provide("enc/trans/escape.so"); + + Init_trans_gb18030(); + rb_provide("enc/trans/gb18030.so"); + + Init_trans_gbk(); + rb_provide("enc/trans/gbk.so"); + + Init_trans_iso2022(); + rb_provide("enc/trans/iso2022.so"); + + Init_trans_japanese(); + rb_provide("enc/trans/japanese.so"); + + Init_trans_japanese_euc(); + rb_provide("enc/trans/japanese_euc.so"); + + Init_trans_japanese_sjis(); + rb_provide("enc/trans/japanese_sjis.so"); + + Init_trans_korean(); + rb_provide("enc/trans/korean.so"); + + Init_trans_single_byte(); + rb_provide("enc/trans/single_byte.so"); + + Init_trans_utf8_mac(); + rb_provide("enc/trans/utf8_mac.so"); + + Init_trans_utf_16_32(); + rb_provide("enc/trans/utf_16_32.so"); + + Init_bigdecimal(); + rb_provide("bigdecimal"); + rb_provide("bigdecimal.so"); + + Init_continuation(); + rb_provide("continuation"); + rb_provide("continuation.so"); + + Init_coverage(); + rb_provide("coverage"); + rb_provide("coverage.so"); + + Init_cparse(); + rb_provide("cparse"); + rb_provide("racc/cparse"); + rb_provide("cparse.so"); + rb_provide("racc/cparse.so"); + + Init_date_core(); + rb_provide("date_core"); + rb_provide("date_core.so"); + + Init_digest(); + rb_provide("digest"); + rb_provide("digest.so"); + + Init_escape(); + rb_provide("escape"); + rb_provide("escape.so"); + + Init_etc(); + rb_provide("etc"); + rb_provide("etc.so"); + + Init_fcntl(); + rb_provide("fcntl"); + rb_provide("fcntl.so"); + + Init_fiber(); + rb_provide("fiber"); + rb_provide("fiber.so"); + + Init_fiddle(); + rb_provide("fiddle"); + rb_provide("fiddle.so"); + + Init_generator(); + rb_provide("json/ext/generator"); + rb_provide("json/ext/generator.so"); + + Init_md5(); + rb_provide("md5"); + rb_provide("digest/md5"); + rb_provide("md5.so"); + rb_provide("digest/md5.so"); + + Init_monitor(); + rb_provide("monitor"); + rb_provide("monitor.so"); + + Init_nkf(); + rb_provide("nkf"); + rb_provide("nkf.so"); + + Init_nonblock(); + rb_provide("nonblock"); + rb_provide("nonblock.so"); + rb_provide("io/nonblock"); + rb_provide("io/nonblock.so"); + + Init_ruby_description(); + + Init_objspace(); + rb_provide("objspace"); + rb_provide("objspace.so"); + + Init_parser(); + rb_provide("json/ext/parser"); + rb_provide("json/ext/parser.so"); + + Init_pathname(); + rb_provide("pathname"); + rb_provide("pathname.so"); + + Init_psych(); + rb_provide("psych"); + rb_provide("psych.so"); + + Init_ripper(); + rb_provide("ripper"); + rb_provide("ripper.so"); + + Init_rmd160(); + rb_provide("rmd160"); + rb_provide("digest/rmd160"); + rb_provide("rmd160.so"); + rb_provide("digest/rmd160.so"); + + Init_sdbm(); + rb_provide("sdbm"); + rb_provide("sdbm.so"); + + Init_sha1(); + rb_provide("sha1"); + rb_provide("digest/sha1"); + rb_provide("sha1.so"); + rb_provide("digest/sha1.so"); + + Init_sha2(); + rb_provide("sha2"); + rb_provide("digest/sha2"); + rb_provide("sha2.so"); + rb_provide("digest/sha2.so"); + + Init_sizeof(); + rb_provide("sizeof"); + rb_provide("sizeof.so"); + + Init_socket(); + rb_provide("socket"); + rb_provide("socket.so"); + + Init_stringio(); + rb_provide("stringio"); + rb_provide("stringio.so"); + + Init_strscan(); + rb_provide("strscan"); + rb_provide("strscan.so"); + + Init_wait(); + rb_provide("wait"); + rb_provide("io/wait"); + rb_provide("wait.so"); + rb_provide("io/wait.so"); + + Init_zlib(); + rb_provide("zlib"); + rb_provide("zlib.so"); + + Init_openssl(); + rb_provide("openssl"); + rb_provide("openssl.so"); + + Init_nonblock(); + rb_provide("io/nonblock"); + rb_provide("io/nonblock.so"); + +#ifndef _WIN32 + // Init_console on Windows fails to load openssl + Init_console(); + rb_provide("console"); + rb_provide("console.so"); + + Init_dbm(); + rb_provide("dbm"); + rb_provide("dbm.so"); + + Init_gdbm(); + rb_provide("gdbm"); + rb_provide("gdbm.so"); + + Init_pty(); + rb_provide("pty"); + rb_provide("pty.so"); + + Init_readline(); + rb_provide("readline"); + rb_provide("readline.so"); + + Init_syslog(); + rb_provide("syslog"); + rb_provide("syslog.so"); +#endif + + // openstudio + //init_openstudio_internal_basic(); + + //auto module = rb_define_module("OpenStudio"); + //rb_define_module_function(module, "init_rest_of_openstudio", init_rest_of_openstudio, 0); +} + +} // namespace openstudio diff --git a/ruby/engine/InitRubyEngine.hpp b/ruby/engine/InitRubyEngine.hpp new file mode 100644 index 00000000000..e7f6ad39fec --- /dev/null +++ b/ruby/engine/InitRubyEngine.hpp @@ -0,0 +1,116 @@ +#ifndef INITRUBYENGINE_included +#define INITRUBYENGINE_included + +extern "C" +{ + void Init_encdb(); + void Init_big5(); + void Init_cesu_8(); + void Init_cp949(); + void Init_emacs_mule(); + void Init_euc_jp(); + void Init_euc_kr(); + void Init_euc_tw(); + void Init_gb18030(); + void Init_gb2312(); + void Init_gbk(); + void Init_iso_8859_1(); + void Init_iso_8859_10(); + void Init_iso_8859_11(); + void Init_iso_8859_13(); + void Init_iso_8859_14(); + void Init_iso_8859_15(); + void Init_iso_8859_16(); + void Init_iso_8859_2(); + void Init_iso_8859_3(); + void Init_iso_8859_4(); + void Init_iso_8859_5(); + void Init_iso_8859_6(); + void Init_iso_8859_7(); + void Init_iso_8859_8(); + void Init_iso_8859_9(); + void Init_koi8_r(); + void Init_koi8_u(); + void Init_shift_jis(); + void Init_utf_16be(); + void Init_utf_16le(); + void Init_utf_32be(); + void Init_utf_32le(); + void Init_windows_1250(); + void Init_windows_1251(); + void Init_windows_1252(); + void Init_windows_1253(); + void Init_windows_1254(); + void Init_windows_1257(); + void Init_windows_31j(); + + void Init_transdb(); + void Init_trans_big5(); + void Init_trans_cesu_8(); + void Init_trans_chinese(); + void Init_trans_ebcdic(); + void Init_trans_emoji(); + void Init_trans_emoji_iso2022_kddi(); + void Init_trans_emoji_sjis_docomo(); + void Init_trans_emoji_sjis_kddi(); + void Init_trans_emoji_sjis_softbank(); + void Init_trans_escape(); + void Init_trans_gb18030(); + void Init_trans_gbk(); + void Init_trans_iso2022(); + void Init_trans_japanese(); + void Init_trans_japanese_euc(); + void Init_trans_japanese_sjis(); + void Init_trans_korean(); + void Init_trans_single_byte(); + void Init_trans_utf8_mac(); + void Init_trans_utf_16_32(); + + void Init_bigdecimal(); + void Init_bigdecimal(void); + void Init_continuation(void); + void Init_coverage(void); + void Init_cparse(void); + void Init_date_core(void); + void Init_digest(void); + void Init_escape(void); + void Init_etc(void); + void Init_fcntl(void); + void Init_fiber(void); + void Init_fiddle(void); + void Init_generator(void); + void Init_md5(void); + void Init_monitor(void); + void Init_nkf(void); + void Init_nonblock(void); + void Init_objspace(void); + void Init_parser(void); + void Init_pathname(void); + void Init_psych(void); + void Init_ripper(void); + void Init_rmd160(void); + void Init_sdbm(void); + void Init_sha1(void); + void Init_sha2(void); + void Init_sizeof(void); + void Init_socket(void); + void Init_stringio(void); + void Init_strscan(void); + void Init_wait(void); + void Init_zlib(void); + + void Init_openssl(void); + void Init_ruby_description(void); + +#ifndef _WIN32 + void Init_console(void); + void Init_dbm(void); + void Init_gdbm(void); + void Init_pty(void); + void Init_readline(void); + void Init_syslog(void); +#endif + +} // extern C + +#endif // INITRUBYENGINE_included diff --git a/ruby/engine/RubyEngine.cpp b/ruby/engine/RubyEngine.cpp new file mode 100644 index 00000000000..b1468125978 --- /dev/null +++ b/ruby/engine/RubyEngine.cpp @@ -0,0 +1,107 @@ +#include "RubyEngine.hpp" +#include "InitRubyBindings.hpp" +#include "RubyEval.hpp" +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wregister" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +unsigned init() { + ruby_setup(); + return 0; +} + +static auto i = init(); + +extern "C" { +void Init_EmbeddedScripting(void); +} + +namespace openstudio { + +VALUE initRestOfOpenStudio(...) { + openstudio::ruby::initExtendedRubyBindings(); + return Qtrue; +}; + +RubyEngine::RubyEngine(int argc, char *argv[]) : ScriptEngine(argc, argv) { + Init_EmbeddedScripting(); + initRubyEngine(); + openstudio::ruby::initBasicRubyBindings(); + + auto module = rb_define_module("OpenStudio"); + rb_define_module_function(module, "init_rest_of_openstudio", initRestOfOpenStudio, 0); + + ruby_set_argv(argc, argv); + + // DLM: this will interpret any strings passed on the command line as UTF-8 + // can we be smarter and detect the correct encoding? use wmain on windows to get utf-16? + // or we might want to follow ruby and allow '--external-encoding=UTF-8' as an input argument? + rb_enc_set_default_external(rb_enc_from_encoding(rb_utf8_encoding())); +} + +RubyEngine::~RubyEngine() { ruby_finalize(); } + +ScriptObject RubyEngine::eval(std::string_view sv) { + std::string str{sv}; + return ScriptObject{openstudio::evalString(str)}; +} + +void RubyEngine::exec(std::string_view sv) { + std::string str{sv}; + [[maybe_unused]] const auto result = openstudio::evalString(str); +} + +// convert the underlying object to the correct type, then return it as a void * +// so the above template function can provide it back to the caller. +void *RubyEngine::getAs_impl(ScriptObject &obj, const std::type_info &ti) { + auto val = std::any_cast(obj.object); + + const auto &type_name = getRegisteredTypeName(ti); + + void *return_value = nullptr; + + auto *type = SWIG_TypeQuery(type_name.c_str()); + + if (!type) { + throw std::runtime_error("Unable to find type in SWIG"); + } + + const auto result = SWIG_ConvertPtr(val, &return_value, type, 0); + + if (!SWIG_IsOK(result)) { + throw std::runtime_error("Error getting object from SWIG/Ruby"); + } + + return return_value; +} + +} // namespace openstudio + +extern "C" { +openstudio::ScriptEngine *makeScriptEngine(int argc, char *argv[]) { return new openstudio::RubyEngine(argc, argv); } + +int rb_hasFile(const char *t_filename) { + // TODO Consider expanding this to use the path which we have artificially defined in embedded_help.rb + std::string expandedName = std::string(":/ruby/2.7.0/") + std::string(t_filename) + ".rb"; + return embedded_files::hasFile(expandedName); +} + +int rb_require_embedded(const char *t_filename) { + std::string require_script = R"(require ')" + std::string(t_filename) + R"(')"; + openstudio::evalString(require_script); + return 0; +} +} diff --git a/ruby/engine/RubyEngine.hpp b/ruby/engine/RubyEngine.hpp new file mode 100644 index 00000000000..ec0ea131089 --- /dev/null +++ b/ruby/engine/RubyEngine.hpp @@ -0,0 +1,35 @@ +#ifndef RUBYENGINE_included +#define RUBYENGINE_included + +#include +#include + +namespace openstudio { + +class RubyEngine final : public ScriptEngine +{ + public: + RubyEngine(int argc = 0, char* argv[] = nullptr); + ~RubyEngine() override; + + RubyEngine(const RubyEngine&) = delete; + RubyEngine(RubyEngine&&) = delete; + RubyEngine& operator=(const RubyEngine&) = delete; + RubyEngine& operator=(RubyEngine&&) = delete; + + ScriptObject eval(std::string_view sv) override; + void exec(std::string_view sv) override; + + protected: + // convert the underlying object to the correct type, then return it as a void * + // so the above template function can provide it back to the caller. + void* getAs_impl(ScriptObject& obj, const std::type_info&) override; + + void initRubyEngine(); + std::vector includePaths; + RubyInterpreter rubyInterpreter{includePaths}; +}; + +} // namespace openstudio + +#endif diff --git a/src/cli/embedded_help.rb b/ruby/engine/embedded_help.rb similarity index 100% rename from src/cli/embedded_help.rb rename to ruby/engine/embedded_help.rb diff --git a/src/cli/measure_manager.rb b/ruby/engine/measure_manager.rb similarity index 100% rename from src/cli/measure_manager.rb rename to ruby/engine/measure_manager.rb diff --git a/src/cli/measure_manager_driver.rb b/ruby/engine/measure_manager_driver.rb similarity index 100% rename from src/cli/measure_manager_driver.rb rename to ruby/engine/measure_manager_driver.rb diff --git a/src/cli/measure_manager_server.rb b/ruby/engine/measure_manager_server.rb similarity index 100% rename from src/cli/measure_manager_server.rb rename to ruby/engine/measure_manager_server.rb diff --git a/src/cli/measure_manager_test.rb b/ruby/engine/measure_manager_test.rb similarity index 100% rename from src/cli/measure_manager_test.rb rename to ruby/engine/measure_manager_test.rb diff --git a/src/cli/openstudio_cli.rb b/ruby/engine/openstudio_cli.rb similarity index 100% rename from src/cli/openstudio_cli.rb rename to ruby/engine/openstudio_cli.rb diff --git a/src/cli/openstudio_init_extended.rb b/ruby/engine/openstudio_init_extended.rb similarity index 100% rename from src/cli/openstudio_init_extended.rb rename to ruby/engine/openstudio_init_extended.rb diff --git a/ruby/init_openstudio.hpp b/ruby/init_openstudio.hpp deleted file mode 100644 index ba49458a84c..00000000000 --- a/ruby/init_openstudio.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/*********************************************************************************************************************** -* OpenStudio(R), Copyright (c) 2008-2022, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -* following conditions are met: -* -* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following -* disclaimer. -* -* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following -* disclaimer in the documentation and/or other materials provided with the distribution. -* -* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products -* derived from this software without specific prior written permission from the respective party. -* -* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works -* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior -* written permission from Alliance for Sustainable Energy, LLC. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED -* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***********************************************************************************************************************/ - -#include - - -void init_openstudio_internal(); - -void init_openstudio_internal_basic(); -void init_openstudio_internal_extended(); - -void evalString(const std::string &t_str); - diff --git a/ruby/interpreter/CMakeLists.txt b/ruby/interpreter/CMakeLists.txt new file mode 100644 index 00000000000..40d0bf4f830 --- /dev/null +++ b/ruby/interpreter/CMakeLists.txt @@ -0,0 +1,25 @@ +# "Link" to this when you need to include ruby.h, +# but don't want to link to libruby. +add_library(rubyinterpreter-delcarations INTERFACE) +get_target_property(RUBY_INCLUDE_DIRS CONAN_PKG::openstudio_ruby INTERFACE_INCLUDE_DIRECTORIES) +target_include_directories(rubyinterpreter-delcarations INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ${RUBY_INCLUDE_DIRS}) + +# Must call CMake itself in order to set the SWIG_LIB env var for add_custom_command +add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/SWIGRubyRuntime.hxx" + COMMAND ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_LIB}" + "${SWIG_EXECUTABLE}" + "-ruby" + -external-runtime "${CMAKE_CURRENT_BINARY_DIR}/SWIGRubyRuntime.hxx" +) + +# Link to rubyinterpreter when you need to link to OpenStudio's flavor of the Ruby interpreter +add_library(rubyinterpreter INTERFACE + RubyInterpreter.hpp + RubyEval.hpp + RubyException.hpp + "${CMAKE_CURRENT_BINARY_DIR}/SWIGRubyRuntime.hxx" +) + +target_include_directories(rubyinterpreter INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(rubyinterpreter INTERFACE CONAN_PKG::openstudio_ruby) diff --git a/ruby/interpreter/InitDeclarations.hpp b/ruby/interpreter/InitDeclarations.hpp new file mode 100644 index 00000000000..e22419eb06a --- /dev/null +++ b/ruby/interpreter/InitDeclarations.hpp @@ -0,0 +1,118 @@ + //void Init_EmbeddedScripting(void); + //INIT_DECLARATIONS; + + //VALUE init_rest_of_openstudio(...) { + // init_openstudio_internal_extended(); + // return Qtrue; + //} + +extern "C" +{ + void Init_encdb(); + void Init_big5(); + void Init_cesu_8(); + void Init_cp949(); + void Init_emacs_mule(); + void Init_euc_jp(); + void Init_euc_kr(); + void Init_euc_tw(); + void Init_gb18030(); + void Init_gb2312(); + void Init_gbk(); + void Init_iso_8859_1(); + void Init_iso_8859_10(); + void Init_iso_8859_11(); + void Init_iso_8859_13(); + void Init_iso_8859_14(); + void Init_iso_8859_15(); + void Init_iso_8859_16(); + void Init_iso_8859_2(); + void Init_iso_8859_3(); + void Init_iso_8859_4(); + void Init_iso_8859_5(); + void Init_iso_8859_6(); + void Init_iso_8859_7(); + void Init_iso_8859_8(); + void Init_iso_8859_9(); + void Init_koi8_r(); + void Init_koi8_u(); + void Init_shift_jis(); + void Init_utf_16be(); + void Init_utf_16le(); + void Init_utf_32be(); + void Init_utf_32le(); + void Init_windows_1250(); + void Init_windows_1251(); + void Init_windows_1252(); + void Init_windows_1253(); + void Init_windows_1254(); + void Init_windows_1257(); + void Init_windows_31j(); + + void Init_transdb(); + void Init_trans_big5(); + void Init_trans_cesu_8(); + void Init_trans_chinese(); + void Init_trans_ebcdic(); + void Init_trans_emoji(); + void Init_trans_emoji_iso2022_kddi(); + void Init_trans_emoji_sjis_docomo(); + void Init_trans_emoji_sjis_kddi(); + void Init_trans_emoji_sjis_softbank(); + void Init_trans_escape(); + void Init_trans_gb18030(); + void Init_trans_gbk(); + void Init_trans_iso2022(); + void Init_trans_japanese(); + void Init_trans_japanese_euc(); + void Init_trans_japanese_sjis(); + void Init_trans_korean(); + void Init_trans_single_byte(); + void Init_trans_utf8_mac(); + void Init_trans_utf_16_32(); + + void Init_bigdecimal(); + void Init_bigdecimal(void); + void Init_continuation(void); + void Init_coverage(void); + void Init_cparse(void); + void Init_date_core(void); + void Init_digest(void); + void Init_escape(void); + void Init_etc(void); + void Init_fcntl(void); + void Init_fiber(void); + void Init_fiddle(void); + void Init_generator(void); + void Init_md5(void); + void Init_monitor(void); + void Init_nkf(void); + void Init_nonblock(void); + void Init_objspace(void); + void Init_parser(void); + void Init_pathname(void); + void Init_psych(void); + void Init_ripper(void); + void Init_rmd160(void); + void Init_sdbm(void); + void Init_sha1(void); + void Init_sha2(void); + void Init_sizeof(void); + void Init_socket(void); + void Init_stringio(void); + void Init_strscan(void); + void Init_wait(void); + void Init_zlib(void); + + void Init_openssl(void); + void Init_ruby_description(void); + +#ifndef _WIN32 + void Init_console(void); + void Init_dbm(void); + void Init_gdbm(void); + void Init_pty(void); + void Init_readline(void); + void Init_syslog(void); +#endif +} diff --git a/ruby/interpreter/RubyEval.hpp b/ruby/interpreter/RubyEval.hpp new file mode 100644 index 00000000000..881a36d3b6f --- /dev/null +++ b/ruby/interpreter/RubyEval.hpp @@ -0,0 +1,33 @@ +#ifndef RUBYEVAL_included +#define RUBYEVAL_included + +#include "ruby.h" +#include "./RubyException.hpp" + +namespace openstudio { + +inline VALUE evaluateSimpleImpl(VALUE arg) { + return rb_eval_string(StringValuePtr(arg)); +} + +inline VALUE evalString(const std::string& t_str) { + VALUE val = rb_str_new2(t_str.c_str()); + int error; + // save and restore the current working directory in case the call to ruby upsets it + VALUE result = rb_protect(evaluateSimpleImpl, val, &error); + if (error != 0) { + VALUE errval = rb_eval_string("$!.to_s"); + char* str = StringValuePtr(errval); + std::string err(str); + VALUE locval = rb_eval_string("$@.to_s"); + str = StringValuePtr(locval); + std::string loc(str); + throw RubyException(err, loc); + } + + return result; +} + +} // openstudio + +#endif // RUBYEVAL_included diff --git a/src/cli/RubyException.hpp b/ruby/interpreter/RubyException.hpp similarity index 100% rename from src/cli/RubyException.hpp rename to ruby/interpreter/RubyException.hpp diff --git a/src/cli/RubyInterpreter.hpp b/ruby/interpreter/RubyInterpreter.hpp similarity index 94% rename from src/cli/RubyInterpreter.hpp rename to ruby/interpreter/RubyInterpreter.hpp index b50575a2770..84d0175bdf8 100644 --- a/src/cli/RubyInterpreter.hpp +++ b/ruby/interpreter/RubyInterpreter.hpp @@ -27,11 +27,10 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************************************************************/ -#ifndef CLI_RUBYINTERPRETER_HPP -#define CLI_RUBYINTERPRETER_HPP - -#include "RubyException.hpp" +#ifndef RUBYINTERPRETER_HPP +#define RUBYINTERPRETER_HPP +#include "RubyEval.hpp" #include #include #include @@ -686,16 +685,12 @@ #include #include +#include -#include - +// RubyInterpreter predates RubyEngine. It is possible that the two things should be merged into one. class RubyInterpreter { private: - // dummy arguments passed to ruby_options - // int m_argc; - // char** m_argv; - static void addIncludePath(std::vector& includePaths, const std::string& includePath) { includePaths.push_back("-I"); includePaths.push_back(includePath); @@ -703,33 +698,6 @@ class RubyInterpreter public: explicit RubyInterpreter(const std::vector& t_includePaths) { - - //// set load paths - //std::vector rubyArgs; - - //rubyArgs.emplace_back("-EUTF-8"); - - //for (const auto &p : t_includePaths) { - // addIncludePath(rubyArgs, p); - //} - - //// and now give the interpreter something to parse, so that it doesn't sit - //// waiting on stdin from us - //rubyArgs.emplace_back("-e"); - //rubyArgs.emplace_back(""); - - //m_argc = static_cast(rubyArgs.size()); - //m_argv = new char*[m_argc]; - - //for (int i = 0; i < m_argc; ++i){ - // m_argv[i] = new char[rubyArgs[i].size() + 1]; - // strcpy(m_argv[i], rubyArgs[i].c_str()); - //} - - //// the return value of ruby_options is the parsed node of our "script" - //// from the -e "" we passed in. This could be used to actually parse / eval something if we wanted - //ruby_options(m_argc, m_argv); - // register some default types that we want to pass in / out of the ruby system registerType("int"); registerType("long"); @@ -737,17 +705,9 @@ class RubyInterpreter registerType("double"); registerType("std::string"); registerType("float"); - - // set the script name if desired - //ruby_script("script_name"); } - ~RubyInterpreter() { - //for (int i = 0; i < m_argc; i++){ - // delete[] m_argv[i]; - //} - //delete[] m_argv; - } + ~RubyInterpreter() {} /// Register a type along with its vector versions so that it can be used /// from within the Ruby wrapper. These are necessary because there's no way to divine @@ -765,26 +725,7 @@ class RubyInterpreter // TODO: should this be static? // cppcheck-suppress functionStatic void evalString(const std::string& t_str) { - - VALUE val = rb_str_new2(t_str.c_str()); - int error; - - // save and restore the current working directory in case the call to ruby upsets it - // const auto cwd = openstudio::filesystem::current_dir(); - rb_protect(evaluateSimpleImpl, val, &error); - // openstudio::filesystem::current_dir(cwd); - - if (error != 0) { - VALUE errval = rb_eval_string("$!.to_s"); - char* str = StringValuePtr(errval); - std::string err(str); - - VALUE locval = rb_eval_string("$@.to_s"); - str = StringValuePtr(locval); - std::string loc(str); - - throw RubyException(err, loc); - } + openstudio::evalString(t_str); } /// Execute a function by name with 0 parameters and no return value @@ -880,11 +821,6 @@ class RubyInterpreter std::map m_types; - // Used for our rb_protect calls. - static VALUE evaluateSimpleImpl(VALUE arg) { - return rb_eval_string(StringValuePtr(arg)); - } - // Returns the swig_type_info for the templated type if it's been registered previously // with registerType template @@ -1518,4 +1454,4 @@ class RubyInterpreter # endif #endif -#endif // CLI_RUBYINTERPRETER_HPP +#endif // RUBYINTERPRETER_HPP diff --git a/ruby/module/CMakeLists.txt b/ruby/module/CMakeLists.txt new file mode 100644 index 00000000000..39ea2781b6f --- /dev/null +++ b/ruby/module/CMakeLists.txt @@ -0,0 +1,100 @@ +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(Apple)?Clang$") + # using Clang + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -undefined dynamic_lookup") +elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + # using GCC + #set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --unresolved-symbols=ignore-all") + #set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --unresolved-symbols=ignore-all") +endif() + + +include_directories(${CMAKE_CURRENT_BINARY_DIR} ${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) + +# openstudio ruby bindings to load into normal ruby +add_library( + openstudio_rb + MODULE + RubyAPI.hpp + openstudio_rb.cpp +) +set_target_properties(openstudio_rb PROPERTIES PREFIX "") +set_target_properties(openstudio_rb PROPERTIES OUTPUT_NAME openstudio) +target_include_directories(openstudio_rb SYSTEM PRIVATE ${RUBY_INCLUDE_DIRS}) + +# change output directory so the openstudio.pdb file does not conflict between this target and the CLI +set_target_properties(openstudio_rb PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/ruby/") +set_target_properties(openstudio_rb PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/ruby/") +set_target_properties(openstudio_rb PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/ruby/") + +if(APPLE) + set_target_properties(openstudio_rb PROPERTIES SUFFIX ".bundle" ) +else() + set_target_properties(openstudio_rb PROPERTIES SUFFIX ".so" ) +endif() + +if(MSVC) + set_target_properties(openstudio_rb PROPERTIES COMPILE_FLAGS "/bigobj /wd4996 /wd5033") +endif() + +add_custom_command(TARGET openstudio_rb + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/openstudio.rb $/openstudio.rb +) + +file(GLOB_RECURSE OPENSTUDIO_FILES FOLLOW_SYMLINKS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/openstudio/" "${CMAKE_CURRENT_SOURCE_DIR}/openstudio/**/*.*") +foreach(OPENSTUDIO_FILE ${OPENSTUDIO_FILES}) + add_custom_command(TARGET openstudio_rb + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/openstudio/${OPENSTUDIO_FILE}" "$/openstudio/${OPENSTUDIO_FILE}" + ) +endforeach() + +if( WIN32 ) + # Dynamically link to openstudiolib.dll on Windows only + target_link_libraries( + openstudio_rb + PUBLIC + openstudiolib + rubybindings + ${ALL_RUBY_BINDING_TARGETS} + ) + target_link_libraries(openstudio_rb PRIVATE ${RUBY_MINGW_STUB_LIB}) +else() + # statically link everywhere else + target_link_libraries( + openstudio_rb + PRIVATE + openstudio_utilities + openstudio_airflow + openstudio_model + openstudio_energyplus + openstudio_epjson + openstudio_measure + openstudio_osversion + openstudio_sdd + openstudio_isomodel + openstudio_gbxml + openstudio_gltf + openstudio_radiance + rubybindings + ${ALL_RUBY_BINDING_TARGETS} + ) +endif() + +if (APPLE) + target_link_libraries(openstudio_rb + PRIVATE + ${OPENSSL_CRYPTO_LIBRARY} + ${OPENSSL_SSL_LIBRARY} + ) +endif() + + +install(TARGETS openstudio_rb + EXPORT openstudio + DESTINATION Ruby + COMPONENT "RubyAPI" + INCLUDES DESTINATION include include/openstudio +) +install(FILES openstudio.rb DESTINATION Ruby COMPONENT "RubyAPI") +install(DIRECTORY openstudio DESTINATION Ruby COMPONENT "RubyAPI") diff --git a/ruby/RubyAPI.hpp b/ruby/module/RubyAPI.hpp similarity index 100% rename from ruby/RubyAPI.hpp rename to ruby/module/RubyAPI.hpp diff --git a/ruby/openstudio.rb b/ruby/module/openstudio.rb similarity index 100% rename from ruby/openstudio.rb rename to ruby/module/openstudio.rb diff --git a/ruby/openstudio/energyplus/find_energyplus.rb b/ruby/module/openstudio/energyplus/find_energyplus.rb similarity index 100% rename from ruby/openstudio/energyplus/find_energyplus.rb rename to ruby/module/openstudio/energyplus/find_energyplus.rb diff --git a/ruby/openstudio/measure/ShowRunnerOutput.rb b/ruby/module/openstudio/measure/ShowRunnerOutput.rb similarity index 100% rename from ruby/openstudio/measure/ShowRunnerOutput.rb rename to ruby/module/openstudio/measure/ShowRunnerOutput.rb diff --git a/ruby/openstudio/ruleset/ShowRunnerOutput.rb b/ruby/module/openstudio/ruleset/ShowRunnerOutput.rb similarity index 100% rename from ruby/openstudio/ruleset/ShowRunnerOutput.rb rename to ruby/module/openstudio/ruleset/ShowRunnerOutput.rb diff --git a/ruby/openstudio_rb.cpp b/ruby/module/openstudio_rb.cpp similarity index 97% rename from ruby/openstudio_rb.cpp rename to ruby/module/openstudio_rb.cpp index fb45f2c763a..bec1f527823 100644 --- a/ruby/openstudio_rb.cpp +++ b/ruby/module/openstudio_rb.cpp @@ -27,7 +27,7 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***********************************************************************************************************************/ -#include "init_openstudio.hpp" +#include "InitRubyBindings.hpp" #include #include #include @@ -36,9 +36,8 @@ extern "C" { RUBY_API void Init_openstudio(void) { - init_openstudio_internal(); + openstudio::ruby::initRubyBindings(); } } - diff --git a/ruby/test/CMakeLists.txt b/ruby/test/CMakeLists.txt new file mode 100644 index 00000000000..b86dd40991b --- /dev/null +++ b/ruby/test/CMakeLists.txt @@ -0,0 +1,61 @@ +# find all tests +file(GLOB RUBY_TEST_SRC "./*.rb") + +# TODO: It doesn't not work with this executable that's in build/Ruby-install/bin +# message("RUBY_EXECUTABLE=${RUBY_EXECUTABLE}") + +set(_RUBY_POSSIBLE_EXECUTABLE_NAMES + ruby + ruby2.7 + ruby2.7.2 + ruby27 + ruby272) + +# TODO: this isn't great but I haven't found a better way to locate the system ruby (and avoid the one in build/Ruby-install/) +find_program(SYSTEM_RUBY_EXECUTABLE NAMES ${_RUBY_POSSIBLE_EXECUTABLE_NAMES} + HINTS + "/usr/local/rvm/rubies/ruby-2.7.2/bin/" + "/usr/local/rvm/rubies/ruby-2.7.2/bin/ruby" + "/usr/share/rvm/rubies/ruby-2.7.2/bin/" + "$ENV{HOME}/.rvm/rubies/ruby-2.7.2/bin/" + + "C:/Ruby27-x64/bin/" + + "/usr/local/ruby272/bin/" + "/usr/local/ruby27/bin/" + "/usr/bin/" + "/usr/local/bin/" + + + NO_DEFAULT_PATH) + +if(NOT SYSTEM_RUBY_EXECUTABLE) + message(WARNING "Your system ruby wasn't found, you won't be able to run the `ctest -R RubyTest` command and the tests won't be created at all.") +else() + message(STATUS "Found SYSTEM_RUBY_EXECUTABLE=${SYSTEM_RUBY_EXECUTABLE}") + + # add a test for each unit test + set(RUBY_TEST_REQUIRES "#include test files") + foreach(f ${RUBY_TEST_SRC}) + + file(READ "${f}" CONTENTS) + string(REGEX MATCHALL "def +test_([A-Za-z_0-9 ]+)" FOUND_TESTS ${CONTENTS}) + + foreach(HIT ${FOUND_TESTS}) + string(REGEX REPLACE "def +test_([A-Za-z_0-9]+)" "\\1" TEST_NAME ${HIT}) + string(REGEX MATCH "/?([A-Za-z_0-9 ]+)\\.rb" FILE_NAME ${f}) + string(REGEX REPLACE "/?([A-Za-z_0-9 ]+)\\.rb" "\\1" FILE_NAME ${FILE_NAME}) + if(BUILD_TESTING) + + # Call with Ruby itself + add_test(NAME "RubyTest-${FILE_NAME}-${TEST_NAME}" + COMMAND "${CMAKE_COMMAND}" -E chdir "${CMAKE_CURRENT_BINARY_DIR}" + "${SYSTEM_RUBY_EXECUTABLE}" "-I" "$" "${f}" "--name=test_${TEST_NAME}" + ) + + set_tests_properties("RubyTest-${FILE_NAME}-${TEST_NAME}" PROPERTIES TIMEOUT 660 ) + endif() + endforeach() + endforeach() + +endif() diff --git a/ruby/openstudio_test.in b/ruby/test/openstudio_test.in similarity index 100% rename from ruby/openstudio_test.in rename to ruby/test/openstudio_test.in diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index cd437c27712..824fb8d7a3c 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -2,230 +2,6 @@ if (APPLE) find_library(COREFOUNDATION_LIBRARY CoreFoundation ) endif (APPLE) -set(INIT_CALLS "") -set(INIT_DECLARATIONS "") -foreach(LIB ${EXTENSION_LIBS_LIST}) - get_filename_component(LIB_NAME ${LIB} NAME_WE) - list (FIND LIBS "${LIB_NAME}" _index) - if( ${_index} GREATER -1 ) - set(INIT_DECLARATIONS "${INIT_DECLARATIONS} void Init_${LIB_NAME}();\\\n" ) - set(INIT_CALLS "${INIT_CALLS} Init_${LIB_NAME}();\\\n" ) - set(INIT_CALLS "${INIT_CALLS} rb_provide(\"${LIB_NAME}.so\");\\\n" ) - endif() -endforeach() - -CONFIGURE_FILE_WITH_CHECKSUM("InitMacros.hxx.in" "${CMAKE_CURRENT_BINARY_DIR}/InitMacros.hxx") - -set(MODULE_ROOT "${CONAN_OPENSTUDIO_RUBY_ROOT}") -if ("${MODULE_ROOT}" STREQUAL "") - set(MODULE_ROOT "${CONAN_OPENSTUDIO_RUBY_ROOT_DEBUG}") -endif() - -message("Searching for ruby modules in '${MODULE_ROOT}/lib/**/*.rb'") -file(GLOB_RECURSE EXTENSION_RB FOLLOW_SYMLINKS "${MODULE_ROOT}/lib/**/*.rb") - -foreach( _FILE ${EXTENSION_RB} ) - file(RELATIVE_PATH LOCATION ${MODULE_ROOT}/lib ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -set(GEMFILE_FILES "${OPENSTUDIO_GEMS_DIR}/openstudio-gems.gemspec" "${PROJECT_BINARY_DIR}/openstudio-gems/Gemfile" "${PROJECT_BINARY_DIR}/openstudio-gems/Gemfile.lock") -file(GLOB_RECURSE RB_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.rb") -file(GLOB_RECURSE ERB_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.erb") -file(GLOB_RECURSE JS_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.js") -file(GLOB_RECURSE CSS_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.css") -file(GLOB_RECURSE GIF_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.gif") -file(GLOB_RECURSE PNG_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.png") -file(GLOB_RECURSE HTML_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.html") -file(GLOB_RECURSE IDF_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.idf") -file(GLOB_RECURSE OSM_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.osm") -file(GLOB_RECURSE EPW_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.epw") -file(GLOB_RECURSE DDY_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.ddy") -file(GLOB_RECURSE STAT_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.stat") -file(GLOB_RECURSE CSV_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.csv") -file(GLOB_RECURSE JSON_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.json") -file(GLOB_RECURSE SPEC_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.gemspec") -file(GLOB_RECURSE GZ_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.gz") -file(GLOB_RECURSE YML_FILES FOLLOW_SYMLINKS "${OPENSTUDIO_GEMS_DIR}/**/*.yml") -file(GLOB_RECURSE OS_FILES FOLLOW_SYMLINKS "${CMAKE_CURRENT_SOURCE_DIR}/openstudio/**/*.rb") # DLM: this is temporary, these files should live somewhere else and be included in the shared Ruby lib - -foreach( _FILE ${GEMFILE_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${RB_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${ERB_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${JS_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${CSS_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${GIF_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${PNG_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${HTML_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${IDF_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${OSM_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${EPW_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${DDY_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${STAT_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${CSV_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${JSON_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${SPEC_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${GZ_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -foreach( _FILE ${YML_FILES} ) - file(RELATIVE_PATH LOCATION "${PROJECT_BINARY_DIR}/openstudio-gems" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -# DLM: this is temporary, these files should live somewhere else and be included in the shared Ruby lib -foreach( _FILE ${OS_FILES} ) - file(RELATIVE_PATH LOCATION "${CMAKE_CURRENT_SOURCE_DIR}" ${_FILE}) - list(APPEND FILES ${_FILE}) - list(APPEND EMBEDDED_PATHS ${LOCATION}) -endforeach() - -list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/embedded_help.rb") -list(APPEND EMBEDDED_PATHS "embedded_help.rb") - -list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/openstudio_cli.rb") -list(APPEND EMBEDDED_PATHS "openstudio_cli.rb") - -list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/openstudio_init_extended.rb") -list(APPEND EMBEDDED_PATHS "openstudio_init_extended.rb") - - -list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/measure_manager.rb") -list(APPEND EMBEDDED_PATHS "measure_manager.rb") - -list(APPEND FILES "${CMAKE_CURRENT_SOURCE_DIR}/measure_manager_server.rb") -list(APPEND EMBEDDED_PATHS "measure_manager_server.rb") - -embed_files("${FILES}" "${EMBEDDED_PATHS}" OUTPUT) - -set_source_files_properties(EmbeddedScripting.i - PROPERTIES CPLUSPLUS true -) - -include_directories(${CMAKE_CURRENT_BINARY_DIR} - ${PROJECT_BINARY_DIR} - ${PROJECT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${OPENSTUDIO_GEMS_DIR} -) - -# Must call CMake itself in order to set the SWIG_LIB env var for add_custom_command -add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/SWIGRubyRuntime.hxx" - COMMAND ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_LIB}" - "${SWIG_EXECUTABLE}" - "-ruby" - -external-runtime "${CMAKE_CURRENT_BINARY_DIR}/SWIGRubyRuntime.hxx" -) - -add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" - COMMAND ${CMAKE_COMMAND} -E env SWIG_LIB="${SWIG_LIB}" - "${SWIG_EXECUTABLE}" - "-ruby" - "-c++" - -o "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" - "-fvirtual" - "-I${PROJECT_SOURCE_DIR}/src" - "-I${PROJECT_BINARY_DIR}/src" - "-I${OPENSTUDIO_GEMS_DIR}" - "-D_WINDOWS" - "-Fmicrosoft" - "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedScripting.i" - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/EmbeddedScripting.i" - "${CMAKE_CURRENT_BINARY_DIR}/embedded_files.hxx" - "EmbeddedHelp.hpp" -) - -set_source_files_properties(${EMBED_SOURCE_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) - -source_group(embedded_files FILES ${OUTPUT}) - if(WIN32) CONFIGURE_FILE_WITH_CHECKSUM(openstudio.rc.in "${CMAKE_CURRENT_BINARY_DIR}/openstudio.rc") endif() @@ -238,87 +14,89 @@ if(WIN32) add_executable(openstudio main.cpp - "${CMAKE_CURRENT_BINARY_DIR}/SWIGRubyRuntime.hxx" - RubyException.hpp - RubyInterpreter.hpp - "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" - ${OUTPUT} - "${CMAKE_CURRENT_BINARY_DIR}/openstudio.rc" + ### "${CMAKE_CURRENT_BINARY_DIR}/SWIGRubyRuntime.hxx" + ### RubyException.hpp + ### RubyInterpreter.hpp + ### "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" + ### ${OUTPUT} + ### "${CMAKE_CURRENT_BINARY_DIR}/openstudio.rc" ) else() add_executable(openstudio main.cpp - "${CMAKE_CURRENT_BINARY_DIR}/SWIGRubyRuntime.hxx" - RubyException.hpp - RubyInterpreter.hpp - "${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" - ${OUTPUT} + ####"${CMAKE_CURRENT_BINARY_DIR}/SWIGRubyRuntime.hxx" + ####RubyException.hpp + ####RubyInterpreter.hpp + ####"${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" + ####${OUTPUT} ) endif() -# -Wno-deprecated-declaration, /wd4996: suppresses deprecated warning -# -Wno-register, /wd5033: ISO C++1z does not allow ‘register’ storage class specifier -if(MSVC) - set_target_properties(openstudio PROPERTIES COMPILE_FLAGS "/bigobj /wd4996 /wd5033") - set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" PROPERTIES COMPILE_FLAGS "-DRUBY_EMBEDDED" ) -else() - set_target_properties(openstudio PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-register") - set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" PROPERTIES COMPILE_FLAGS "-DRUBY_EMBEDDED -Wno-unused-variable" ) -endif() - -add_definitions(-DRUBY_EXTCONF_H="osruby_config.h") - -# We are setting up a set of artificial dependencies here so that -# cmake knows how to order the libraries properly -# -# Better: properly specifying these libs in the conan recipe -# At the moment this is not possible due to some of them coming from the OS and some -# being built by this project -# TODO: Continue to make the conan deps more robust and get this sorted out -add_library(additional_ruby_libs empty.cpp) -add_library(ruby_libs empty.cpp) -target_link_libraries(ruby_libs PUBLIC CONAN_PKG::openstudio_ruby) -target_link_libraries(ruby_libs PUBLIC additional_ruby_libs) - -target_link_libraries(openstudio PRIVATE init_openstudio ruby_libs ${ALL_RUBY_BINDING_TARGETS}) - -include("${OPENSTUDIO_GEMS_DIR}/export-extensions.cmake") +##### -Wno-deprecated-declaration, /wd4996: suppresses deprecated warning +##### -Wno-register, /wd5033: ISO C++1z does not allow ‘register’ storage class specifier +####if(MSVC) +#### set_target_properties(openstudio PROPERTIES COMPILE_FLAGS "/bigobj /wd4996 /wd5033") +#### set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" PROPERTIES COMPILE_FLAGS "-DRUBY_EMBEDDED" ) +####else() +#### set_target_properties(openstudio PROPERTIES COMPILE_FLAGS "-Wno-deprecated-declarations -Wno-register") +#### set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/embedded_scripting_wrap.cxx" PROPERTIES COMPILE_FLAGS "-DRUBY_EMBEDDED -Wno-unused-variable" ) +####endif() +#### +####add_definitions(-DRUBY_EXTCONF_H="osruby_config.h") +#### +##### We are setting up a set of artificial dependencies here so that +##### cmake knows how to order the libraries properly +##### +##### Better: properly specifying these libs in the conan recipe +##### At the moment this is not possible due to some of them coming from the OS and some +##### being built by this project +##### TODO: Continue to make the conan deps more robust and get this sorted out +####add_library(additional_ruby_libs empty.cpp) +####add_library(ruby_libs empty.cpp) +####target_link_libraries(ruby_libs PUBLIC CONAN_PKG::openstudio_ruby) +####target_link_libraries(ruby_libs PUBLIC additional_ruby_libs) +#### +####target_link_libraries(openstudio PRIVATE init_openstudio ruby_libs ${ALL_RUBY_BINDING_TARGETS}) +#### +####include("${OPENSTUDIO_GEMS_DIR}/export-extensions.cmake") target_link_libraries(openstudio PRIVATE openstudio_utilities - openstudio_airflow - openstudio_model - openstudio_energyplus - openstudio_epjson - openstudio_measure - openstudio_osversion - openstudio_sdd - openstudio_isomodel - openstudio_gbxml - openstudio_gltf - openstudio_radiance - ${ruby_extension_libs} + # openstudio_airflow + # openstudio_model + # openstudio_energyplus + # openstudio_epjson + # openstudio_measure + # openstudio_osversion + # openstudio_sdd + # openstudio_isomodel + # openstudio_gbxml + # openstudio_gltf + # openstudio_radiance + # ${ruby_extension_libs} ) -if (UNIX AND NOT APPLE) - target_link_libraries(additional_ruby_libs dl crypt anl) - target_link_libraries(openstudio PRIVATE "icui18n" "icuuc" "gmp") -elseif(WIN32) - target_link_libraries(openstudio PRIVATE wsock32 ws2_32 Dbghelp Shlwapi Iphlpapi) - set_target_properties(openstudio PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:MSVCRT") -else() -endif() +add_dependencies(openstudio rubyengine) + +####if (UNIX AND NOT APPLE) +#### target_link_libraries(additional_ruby_libs dl crypt anl) +#### target_link_libraries(openstudio PRIVATE "icui18n" "icuuc" "gmp") +####elseif(WIN32) +#### target_link_libraries(openstudio PRIVATE wsock32 ws2_32 Dbghelp Shlwapi Iphlpapi) +#### set_target_properties(openstudio PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:MSVCRT") +####else() +####endif() if( APPLE ) target_link_libraries(openstudio PRIVATE ${COREFOUNDATION_LIBRARY}) endif() install(TARGETS openstudio EXPORT openstudio DESTINATION bin COMPONENT "CLI") -install(FILES openstudio_cli.rb DESTINATION Ruby COMPONENT "RubyAPI") -install(FILES measure_manager.rb DESTINATION Ruby COMPONENT "RubyAPI") -install(FILES measure_manager_server.rb DESTINATION Ruby COMPONENT "RubyAPI") +###install(FILES openstudio_cli.rb DESTINATION Ruby COMPONENT "RubyAPI") +###install(FILES measure_manager.rb DESTINATION Ruby COMPONENT "RubyAPI") +###install(FILES measure_manager_server.rb DESTINATION Ruby COMPONENT "RubyAPI") if( BUILD_PAT ) if( APPLE ) @@ -330,39 +108,39 @@ if( BUILD_PAT ) endif() -############################################################################### -# T E S T I N G: C T E S T S # -############################################################################### - -file(GLOB RUBY_TEST_SRC - # find all CLI test - "test/test*.rb" - - # Also Run the ruby tests with the cli - "../../ruby/test/*.rb" -) - -# message("CLI: RUBY_TEST_SRC=${RUBY_TEST_SRC}") - -# add a test for each unit test -set(RUBY_TEST_REQUIRES "#include test files") -foreach(f ${RUBY_TEST_SRC}) - - file(READ "${f}" CONTENTS) - string(REGEX MATCHALL "def +test_([A-Za-z_0-9 ]+)" FOUND_TESTS ${CONTENTS}) - - foreach(HIT ${FOUND_TESTS}) - string(REGEX REPLACE "def +test_([A-Za-z_0-9]+)" "\\1" TEST_NAME ${HIT}) - string(REGEX MATCH "/?([A-Za-z_0-9 ]+)\\.rb" FILE_NAME ${f}) - string(REGEX REPLACE "/?([A-Za-z_0-9 ]+)\\.rb" "\\1" FILE_NAME ${FILE_NAME}) - if(BUILD_TESTING) - - add_test(NAME "CLITest-${FILE_NAME}-${TEST_NAME}" - COMMAND "${CMAKE_COMMAND}" -E chdir "${CMAKE_CURRENT_BINARY_DIR}" - "$" "${f}" "--name=test_${TEST_NAME}" - ) - - set_tests_properties("CLITest-${FILE_NAME}-${TEST_NAME}" PROPERTIES TIMEOUT 660 ) - endif() - endforeach() -endforeach() +################################################################################### +##### T E S T I N G: C T E S T S # +################################################################################### +#### +####file(GLOB RUBY_TEST_SRC +#### # find all CLI test +#### "test/test*.rb" +#### +#### # Also Run the ruby tests with the cli +#### "../../ruby/test/*.rb" +####) +#### +##### message("CLI: RUBY_TEST_SRC=${RUBY_TEST_SRC}") +#### +##### add a test for each unit test +####set(RUBY_TEST_REQUIRES "#include test files") +####foreach(f ${RUBY_TEST_SRC}) +#### +#### file(READ "${f}" CONTENTS) +#### string(REGEX MATCHALL "def +test_([A-Za-z_0-9 ]+)" FOUND_TESTS ${CONTENTS}) +#### +#### foreach(HIT ${FOUND_TESTS}) +#### string(REGEX REPLACE "def +test_([A-Za-z_0-9]+)" "\\1" TEST_NAME ${HIT}) +#### string(REGEX MATCH "/?([A-Za-z_0-9 ]+)\\.rb" FILE_NAME ${f}) +#### string(REGEX REPLACE "/?([A-Za-z_0-9 ]+)\\.rb" "\\1" FILE_NAME ${FILE_NAME}) +#### if(BUILD_TESTING) +#### +#### add_test(NAME "CLITest-${FILE_NAME}-${TEST_NAME}" +#### COMMAND "${CMAKE_COMMAND}" -E chdir "${CMAKE_CURRENT_BINARY_DIR}" +#### "$" "${f}" "--name=test_${TEST_NAME}" +#### ) +#### +#### set_tests_properties("CLITest-${FILE_NAME}-${TEST_NAME}" PROPERTIES TIMEOUT 660 ) +#### endif() +#### endforeach() +####endforeach() diff --git a/src/cli/main.cpp b/src/cli/main.cpp index c17243fbd2f..227fcc582f6 100644 --- a/src/cli/main.cpp +++ b/src/cli/main.cpp @@ -1,577 +1,15 @@ -/*********************************************************************************************************************** -* OpenStudio(R), Copyright (c) 2008-2022, Alliance for Sustainable Energy, LLC, and other contributors. All rights reserved. -* -* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -* following conditions are met: -* -* (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following -* disclaimer. -* -* (2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following -* disclaimer in the documentation and/or other materials provided with the distribution. -* -* (3) Neither the name of the copyright holder nor the names of any contributors may be used to endorse or promote products -* derived from this software without specific prior written permission from the respective party. -* -* (4) Other than as required in clauses (1) and (2), distributions in any form of modifications or other derivative works -* may not use the "OpenStudio" trademark, "OS", "os", or any other confusingly similar designation without specific prior -* written permission from Alliance for Sustainable Energy, LLC. -* -* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND ANY CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S), ANY CONTRIBUTORS, THE UNITED STATES GOVERNMENT, OR THE UNITED -* STATES DEPARTMENT OF ENERGY, NOR ANY OF THEIR EMPLOYEES, BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF -* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -***********************************************************************************************************************/ - -#include "RubyInterpreter.hpp" -#include "GC_Value.hpp" -#include "InitMacros.hxx" -#include "../../ruby/init_openstudio.hpp" -#include - +#include "../scriptengine/ScriptEngine.hpp" #include -#ifndef _MSC_VER -# include -# include -#else -# include -# pragma warning(disable : 4930) -# pragma warning(disable : 4101) -#endif - -extern "C" -{ - void Init_EmbeddedScripting(void); - INIT_DECLARATIONS; - - void Init_encdb(); - - //void Init_ascii(); // this is not included in libenc - void Init_big5(); - void Init_cesu_8(); - void Init_cp949(); - void Init_emacs_mule(); - void Init_euc_jp(); - void Init_euc_kr(); - void Init_euc_tw(); - void Init_gb18030(); - void Init_gb2312(); - void Init_gbk(); - void Init_iso_8859_1(); - void Init_iso_8859_10(); - void Init_iso_8859_11(); - void Init_iso_8859_13(); - void Init_iso_8859_14(); - void Init_iso_8859_15(); - void Init_iso_8859_16(); - void Init_iso_8859_2(); - void Init_iso_8859_3(); - void Init_iso_8859_4(); - void Init_iso_8859_5(); - void Init_iso_8859_6(); - void Init_iso_8859_7(); - void Init_iso_8859_8(); - void Init_iso_8859_9(); - void Init_koi8_r(); - void Init_koi8_u(); - void Init_shift_jis(); - //void Init_unicode(); // this is not included in libenc - //void Init_us_ascii(); // this is not included in libenc - void Init_utf_16be(); - void Init_utf_16le(); - void Init_utf_32be(); - void Init_utf_32le(); - //void Init_utf_8(); // this is not included in libenc - void Init_windows_1250(); - void Init_windows_1251(); - void Init_windows_1252(); - void Init_windows_1253(); - void Init_windows_1254(); - void Init_windows_1257(); - void Init_windows_31j(); - - void Init_transdb(); - - void Init_trans_big5(); - void Init_trans_cesu_8(); - void Init_trans_chinese(); - void Init_trans_ebcdic(); - void Init_trans_emoji(); - void Init_trans_emoji_iso2022_kddi(); - void Init_trans_emoji_sjis_docomo(); - void Init_trans_emoji_sjis_kddi(); - void Init_trans_emoji_sjis_softbank(); - void Init_trans_escape(); - void Init_trans_gb18030(); - void Init_trans_gbk(); - void Init_trans_iso2022(); - void Init_trans_japanese(); - void Init_trans_japanese_euc(); - void Init_trans_japanese_sjis(); - void Init_trans_korean(); - void Init_trans_single_byte(); - void Init_trans_utf8_mac(); - void Init_trans_utf_16_32(); - - void Init_bigdecimal(); - void Init_bigdecimal(void); - void Init_continuation(void); - void Init_coverage(void); - void Init_cparse(void); - void Init_date_core(void); - void Init_digest(void); - void Init_escape(void); - void Init_etc(void); - void Init_fcntl(void); - void Init_fiber(void); - void Init_fiddle(void); - void Init_generator(void); - void Init_md5(void); - void Init_monitor(void); - void Init_nkf(void); - void Init_nonblock(void); - void Init_objspace(void); - void Init_parser(void); - void Init_pathname(void); - void Init_psych(void); - void Init_ripper(void); - void Init_rmd160(void); - void Init_sdbm(void); - void Init_sha1(void); - void Init_sha2(void); - void Init_sizeof(void); - void Init_socket(void); - void Init_stringio(void); - void Init_strscan(void); - void Init_wait(void); - void Init_zlib(void); - - void Init_openssl(void); - void Init_ruby_description(void); - -#ifndef _WIN32 - void Init_console(void); - void Init_dbm(void); - void Init_gdbm(void); - void Init_pty(void); - void Init_readline(void); - void Init_syslog(void); -#endif - - VALUE init_rest_of_openstudio(...) { - init_openstudio_internal_extended(); - return Qtrue; - } -} - -std::vector paths; -static RubyInterpreter rubyInterpreter(paths); //(paths); +std::unique_ptr rubyEngine; +std::unique_ptr pythonEngine; int main(int argc, char* argv[]) { - ruby_sysinit(&argc, &argv); - { - RUBY_INIT_STACK; - ruby_init(); - - swig::GC_VALUE::hash_id = rb_intern("hash"); - swig::GC_VALUE::lt_id = rb_intern("<"); - swig::GC_VALUE::gt_id = rb_intern(">"); - swig::GC_VALUE::eq_id = rb_intern("=="); - swig::GC_VALUE::le_id = rb_intern("<="); - swig::GC_VALUE::ge_id = rb_intern(">="); - - swig::GC_VALUE::pos_id = rb_intern("+@"); - swig::GC_VALUE::neg_id = rb_intern("-@"); - swig::GC_VALUE::inv_id = rb_intern("~"); - - swig::GC_VALUE::add_id = rb_intern("+"); - swig::GC_VALUE::sub_id = rb_intern("-"); - swig::GC_VALUE::mul_id = rb_intern("*"); - swig::GC_VALUE::div_id = rb_intern("/"); - swig::GC_VALUE::mod_id = rb_intern("%"); - - swig::GC_VALUE::and_id = rb_intern("&"); - swig::GC_VALUE::or_id = rb_intern("|"); - swig::GC_VALUE::xor_id = rb_intern("^"); - - swig::GC_VALUE::lshift_id = rb_intern("<<"); - swig::GC_VALUE::rshift_id = rb_intern(">>"); - - INIT_CALLS; - - // in case any further init methods try to require files, init this first - Init_EmbeddedScripting(); - - // Need embedded_help for requiring files out of the embedded system - auto embedded_extensions_string = embedded_files::getFileAsString(":/embedded_help.rb"); - - try { - rubyInterpreter.evalString(embedded_extensions_string); - } catch (const std::exception& e) { - rubyInterpreter.evalString(R"(STDOUT.flush)"); - std::cout << "Exception in embedded_help: " << e.what() << std::endl; // endl will flush - return ruby_cleanup(1); - } catch (...) { - rubyInterpreter.evalString(R"(STDOUT.flush)"); - std::cout << "Unknown Exception in embedded_help" << std::endl; // endl will flush - return ruby_cleanup(1); - } - - //// encodings - /// Get the symbols from: `DUMPBIN /ARCHIVEMEMBERS C:\Users\julien\.conan\data\openstudio_ruby\2.7.2\nrel\testing\package\2fa339f0e9f8e459bd56b19a37be69734f2745f4\lib\enc\libenc.lib` - Init_encdb(); - rb_provide("enc/encdb.so"); - //Init_ascii(); - //rb_provide("enc/ascii.so"); - Init_big5(); - rb_provide("enc/big5.so"); - Init_cesu_8(); - rb_provide("enc/cesu_8.so"); - Init_cp949(); - rb_provide("enc/cp949.so"); - Init_emacs_mule(); - rb_provide("enc/emacs_mule.so"); - Init_euc_jp(); - rb_provide("enc/euc_jp.so"); - Init_euc_kr(); - rb_provide("enc/euc_kr.so"); - Init_euc_tw(); - rb_provide("enc/euc_tw.so"); - Init_gb18030(); - rb_provide("enc/gb18030.so"); - Init_gb2312(); - rb_provide("enc/gb2312.so"); - Init_gbk(); - rb_provide("enc/gbk.so"); - Init_iso_8859_1(); - rb_provide("enc/iso_8859_1.so"); - Init_iso_8859_10(); - rb_provide("enc/iso_8859_10.so"); - Init_iso_8859_11(); - rb_provide("enc/iso_8859_11.so"); - Init_iso_8859_13(); - rb_provide("enc/iso_8859_13.so"); - Init_iso_8859_14(); - rb_provide("enc/iso_8859_14.so"); - Init_iso_8859_15(); - rb_provide("enc/iso_8859_15.so"); - Init_iso_8859_16(); - rb_provide("enc/iso_8859_16.so"); - Init_iso_8859_2(); - rb_provide("enc/iso_8859_2.so"); - Init_iso_8859_3(); - rb_provide("enc/iso_8859_3.so"); - Init_iso_8859_4(); - rb_provide("enc/iso_8859_4.so"); - Init_iso_8859_5(); - rb_provide("enc/iso_8859_5.so"); - Init_iso_8859_6(); - rb_provide("enc/iso_8859_6.so"); - Init_iso_8859_7(); - rb_provide("enc/iso_8859_7.so"); - Init_iso_8859_8(); - rb_provide("enc/iso_8859_8.so"); - Init_iso_8859_9(); - rb_provide("enc/iso_8859_9.so"); - Init_koi8_r(); - rb_provide("enc/koi8_r.so"); - Init_koi8_u(); - rb_provide("enc/koi8_u.so"); - Init_shift_jis(); - rb_provide("enc/shift_jis.so"); - //Init_unicode(); - //rb_provide("enc/unicode.so"); - //Init_us_ascii(); - //rb_provide("enc/us_ascii.so"); - Init_utf_16be(); - rb_provide("enc/utf_16be.so"); - Init_utf_16le(); - rb_provide("enc/utf_16le.so"); - Init_utf_32be(); - rb_provide("enc/utf_32be.so"); - Init_utf_32le(); - rb_provide("enc/utf_32le.so"); - //Init_utf_8(); - //rb_provide("enc/utf_8.so"); - Init_windows_1250(); - rb_provide("enc/windows_1250.so"); - Init_windows_1251(); - rb_provide("enc/windows_1251.so"); - Init_windows_1252(); - rb_provide("enc/windows_1252.so"); - Init_windows_1253(); - rb_provide("enc/windows_1253.so"); - Init_windows_1254(); - rb_provide("enc/windows_1254.so"); - Init_windows_1257(); - rb_provide("enc/windows_1257.so"); - Init_windows_31j(); - rb_provide("enc/windows_31j.so"); - - /// Get the symbols from: `DUMPBIN /ARCHIVEMEMBERS C:\Users\julien\.conan\data\openstudio_ruby\2.7.2\nrel\testing\package\2fa339f0e9f8e459bd56b19a37be69734f2745f4\lib\enc\libtrans.lib` - Init_transdb(); - rb_provide("enc/trans/transdb.so"); - - //Init_trans_big5(); - //rb_provide("enc/trans/big5.so"); - Init_trans_big5(); - rb_provide("enc/trans/big5.so"); - - Init_trans_cesu_8(); - rb_provide("enc/trans/cesu_8.so"); - - Init_trans_chinese(); - rb_provide("enc/trans/chinese.so"); - - Init_trans_ebcdic(); - rb_provide("enc/trans/ebcdic.so"); - - Init_trans_emoji(); - rb_provide("enc/trans/emoji.so"); - - Init_trans_emoji_iso2022_kddi(); - rb_provide("enc/trans/emoji_iso2022_kddi.so"); - - Init_trans_emoji_sjis_docomo(); - rb_provide("enc/trans/emoji_sjis_docomo.so"); - - Init_trans_emoji_sjis_kddi(); - rb_provide("enc/trans/emoji_sjis_kddi.so"); - - Init_trans_emoji_sjis_softbank(); - rb_provide("enc/trans/emoji_sjis_softbank.so"); - - Init_trans_escape(); - rb_provide("enc/trans/escape.so"); - - Init_trans_gb18030(); - rb_provide("enc/trans/gb18030.so"); - - Init_trans_gbk(); - rb_provide("enc/trans/gbk.so"); - - Init_trans_iso2022(); - rb_provide("enc/trans/iso2022.so"); - - Init_trans_japanese(); - rb_provide("enc/trans/japanese.so"); - - Init_trans_japanese_euc(); - rb_provide("enc/trans/japanese_euc.so"); - - Init_trans_japanese_sjis(); - rb_provide("enc/trans/japanese_sjis.so"); - - Init_trans_korean(); - rb_provide("enc/trans/korean.so"); - - Init_trans_single_byte(); - rb_provide("enc/trans/single_byte.so"); - - Init_trans_utf8_mac(); - rb_provide("enc/trans/utf8_mac.so"); - - Init_trans_utf_16_32(); - rb_provide("enc/trans/utf_16_32.so"); - - Init_bigdecimal(); - rb_provide("bigdecimal"); - rb_provide("bigdecimal.so"); - - Init_continuation(); - rb_provide("continuation"); - rb_provide("continuation.so"); - - Init_coverage(); - rb_provide("coverage"); - rb_provide("coverage.so"); - - Init_cparse(); - rb_provide("cparse"); - rb_provide("racc/cparse"); - rb_provide("cparse.so"); - rb_provide("racc/cparse.so"); - - Init_date_core(); - rb_provide("date_core"); - rb_provide("date_core.so"); - - Init_digest(); - rb_provide("digest"); - rb_provide("digest.so"); - - Init_escape(); - rb_provide("escape"); - rb_provide("escape.so"); - - Init_etc(); - rb_provide("etc"); - rb_provide("etc.so"); - - Init_fcntl(); - rb_provide("fcntl"); - rb_provide("fcntl.so"); - - Init_fiber(); - rb_provide("fiber"); - rb_provide("fiber.so"); - - Init_fiddle(); - rb_provide("fiddle"); - rb_provide("fiddle.so"); - - Init_generator(); - rb_provide("json/ext/generator"); - rb_provide("json/ext/generator.so"); - - Init_md5(); - rb_provide("md5"); - rb_provide("digest/md5"); - rb_provide("md5.so"); - rb_provide("digest/md5.so"); - - Init_monitor(); - rb_provide("monitor"); - rb_provide("monitor.so"); - - Init_nkf(); - rb_provide("nkf"); - rb_provide("nkf.so"); - - Init_nonblock(); - rb_provide("nonblock"); - rb_provide("nonblock.so"); - rb_provide("io/nonblock"); - rb_provide("io/nonblock.so"); - - Init_ruby_description(); - - Init_objspace(); - rb_provide("objspace"); - rb_provide("objspace.so"); - - Init_parser(); - rb_provide("json/ext/parser"); - rb_provide("json/ext/parser.so"); - - Init_pathname(); - rb_provide("pathname"); - rb_provide("pathname.so"); - - Init_psych(); - rb_provide("psych"); - rb_provide("psych.so"); - - Init_ripper(); - rb_provide("ripper"); - rb_provide("ripper.so"); - - Init_rmd160(); - rb_provide("rmd160"); - rb_provide("digest/rmd160"); - rb_provide("rmd160.so"); - rb_provide("digest/rmd160.so"); - - Init_sdbm(); - rb_provide("sdbm"); - rb_provide("sdbm.so"); - - Init_sha1(); - rb_provide("sha1"); - rb_provide("digest/sha1"); - rb_provide("sha1.so"); - rb_provide("digest/sha1.so"); - - Init_sha2(); - rb_provide("sha2"); - rb_provide("digest/sha2"); - rb_provide("sha2.so"); - rb_provide("digest/sha2.so"); - - Init_sizeof(); - rb_provide("sizeof"); - rb_provide("sizeof.so"); - - Init_socket(); - rb_provide("socket"); - rb_provide("socket.so"); - - Init_stringio(); - rb_provide("stringio"); - rb_provide("stringio.so"); - - Init_strscan(); - rb_provide("strscan"); - rb_provide("strscan.so"); - - Init_wait(); - rb_provide("wait"); - rb_provide("io/wait"); - rb_provide("wait.so"); - rb_provide("io/wait.so"); - - Init_zlib(); - rb_provide("zlib"); - rb_provide("zlib.so"); - - Init_openssl(); - rb_provide("openssl"); - rb_provide("openssl.so"); - - Init_nonblock(); - rb_provide("io/nonblock"); - rb_provide("io/nonblock.so"); - -#ifndef _WIN32 - - // DLM: we have Init_console on Windows but crashes when try to init it, fails to load openssl - Init_console(); - rb_provide("console"); - rb_provide("console.so"); - - Init_dbm(); - rb_provide("dbm"); - rb_provide("dbm.so"); - - Init_gdbm(); - rb_provide("gdbm"); - rb_provide("gdbm.so"); - - Init_pty(); - rb_provide("pty"); - rb_provide("pty.so"); - - Init_readline(); - rb_provide("readline"); - rb_provide("readline.so"); - - Init_syslog(); - rb_provide("syslog"); - rb_provide("syslog.so"); -#endif - - // openstudio - init_openstudio_internal_basic(); - - auto module = rb_define_module("OpenStudio"); - rb_define_module_function(module, "init_rest_of_openstudio", init_rest_of_openstudio, 0); - } - - // DLM: this will interpret any strings passed on the command line as UTF-8 - // can we be smarter and detect the correct encoding? use wmain on windows to get utf-16? - // or we might want to follow ruby and allow '--external-encoding=UTF-8' as an input argument? - rb_enc_set_default_external(rb_enc_from_encoding(rb_utf8_encoding())); - // chop off the first argument which is the exe path/name - ruby_set_argv(argc - 1, argv + 1); + rubyEngine = openstudio::loadScriptEngine("rubyengine", argc - 1, argv + 1); try { - rubyInterpreter.evalString(R"( + rubyEngine->exec(R"( begin require 'openstudio_cli' rescue Exception => e @@ -582,30 +20,15 @@ int main(int argc, char* argv[]) { end )"); } catch (const std::exception& e) { - rubyInterpreter.evalString(R"(STDOUT.flush)"); + rubyEngine->exec(R"(STDOUT.flush)"); std::cout << "Exception: " << e.what() << std::endl; // endl will flush - return ruby_cleanup(1); + return 1; } catch (...) { - rubyInterpreter.evalString(R"(STDOUT.flush)"); + rubyEngine->exec(R"(STDOUT.flush)"); std::cout << "Unknown Exception" << std::endl; // endl will flush - return ruby_cleanup(1); - } - rubyInterpreter.evalString(R"(STDOUT.flush)"); - std::cout << std::flush; - return ruby_cleanup(0); -} - -extern "C" -{ - int rb_hasFile(const char* t_filename) { - // TODO Consider expanding this to use the path which we have artificially defined in embedded_help.rb - std::string expandedName = std::string(":/ruby/2.7.0/") + std::string(t_filename) + ".rb"; - return embedded_files::hasFile(expandedName); + return 1; } - int rb_require_embedded(const char* t_filename) { - std::string require_script = R"(require ')" + std::string(t_filename) + R"(')"; - rubyInterpreter.evalString(require_script); - return 0; - } + rubyEngine->exec(R"(STDOUT.flush)"); + std::cout << std::flush; } diff --git a/src/scriptengine/CMakeLists.txt b/src/scriptengine/CMakeLists.txt new file mode 100644 index 00000000000..c53d2b7ecdd --- /dev/null +++ b/src/scriptengine/CMakeLists.txt @@ -0,0 +1,5 @@ +# Script Engine is a base for scripting in OpenStudio +# See implementations in ruby/engine and python/engine + +add_library(scriptengine INTERFACE) +target_include_directories(scriptengine INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/scriptengine/ScriptEngine.hpp b/src/scriptengine/ScriptEngine.hpp new file mode 100644 index 00000000000..9e4574b1fb5 --- /dev/null +++ b/src/scriptengine/ScriptEngine.hpp @@ -0,0 +1,94 @@ +#ifndef SCRIPTENGINE_included +#define SCRIPTENGINE_included + +#include "../utilities/core/ApplicationPathHelpers.hpp" +#include "../utilities/core/DynamicLibrary.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openstudio { +class Measure; +class ScriptEngine; + +struct ScriptObject { + std::any object; +}; +} // namespace openstudio + +typedef openstudio::ScriptEngine *ScriptEngineFactoryType(int argc, char *argv[]); + +namespace openstudio { + +class ScriptEngine { +public: + ScriptEngine([[maybe_unused]] int argc, [[maybe_unused]] char *argv[]) { + registerType("openstudio::Measure *"); + } + + virtual ~ScriptEngine() = default; + ScriptEngine(const ScriptEngine &) = delete; + ScriptEngine(ScriptEngine &&) = delete; + ScriptEngine &operator=(const ScriptEngine &) = delete; + ScriptEngine &operator=(ScriptEngine &&) = delete; + + virtual ScriptObject eval(std::string_view sv) = 0; + + // execute string without expecting a return value + virtual void exec(std::string_view sv) = 0; + + template T getAs(ScriptObject &obj) { + void *result = getAs_impl(obj, typeid(T)); + if (result) { + return static_cast(result); + } else { + throw std::bad_cast(); + } + } + + template void registerType(std::string name) { types.emplace(std::cref(typeid(T)), std::move(name)); } + +protected: + // convert the underlying object to the correct type, then return it as a void * + // so the above template function can provide it back to the caller. + virtual void *getAs_impl(ScriptObject &obj, const std::type_info &) = 0; + + const std::string &getRegisteredTypeName(const std::type_info &type) { + const auto &found_name = types.find(type); + + if (found_name != types.end()) { + return found_name->second; + } + + throw std::runtime_error("unknown type requested"); + } + +private: + struct Compare { + bool operator()(const std::reference_wrapper &lhs, + const std::reference_wrapper &rhs) const { + return lhs.get().before(rhs.get()); + } + }; + + std::map, std::string, Compare> types; +}; + +inline std::unique_ptr loadScriptEngine(std::string_view libraryBaseName, int argc, char *argv[]) { + auto enginePath = openstudio::getOpenStudioModuleDirectory() / openstudio::getSharedLibraryName(libraryBaseName); + openstudio::DynamicLibrary engineLib(enginePath); + + const std::function factory = + engineLib.load_symbol("makeScriptEngine"); + + return std::unique_ptr(factory(argc, argv)); +} + +} // namespace openstudio + +#endif diff --git a/src/utilities/CMakeLists.txt b/src/utilities/CMakeLists.txt index 672b5d2c657..da71ea1e9b8 100644 --- a/src/utilities/CMakeLists.txt +++ b/src/utilities/CMakeLists.txt @@ -23,6 +23,9 @@ set(core_src core/Containers.hpp core/Containers.cpp core/Deprecated.hpp + core/DynamicLibrary.hpp + core/DynamicLibraryPOSIX.hpp + core/DynamicLibraryWindows.hpp core/Enum.hpp core/EnumHelpers.hpp core/Exception.hpp @@ -452,6 +455,13 @@ target_compile_definitions(${target_name} PRIVATE openstudio_utilities_EXPORTS S target_include_directories(${target_name} PUBLIC ${PROJECT_BINARY_DIR}) target_include_directories(${target_name} PUBLIC ${PROJECT_BINARY_DIR}/src) +include(CheckIncludeFile) +check_include_file("unistd.h" HAVE_UNISTD_H) +if(HAVE_UNISTD_H) + target_compile_definitions(${target_name} PUBLIC "-DHAVE_UNISTD_H") +endif() + + if(BUILD_TESTING) CREATE_TEST_TARGETS(${target_name} "${${target_name}_test_src}" openstudiolib) add_dependencies(${target_name}_tests openstudio_model_resources) diff --git a/src/utilities/core/ApplicationPathHelpers.cxx.in b/src/utilities/core/ApplicationPathHelpers.cxx.in index 6003a75ae3b..7db55d0b2aa 100644 --- a/src/utilities/core/ApplicationPathHelpers.cxx.in +++ b/src/utilities/core/ApplicationPathHelpers.cxx.in @@ -33,6 +33,7 @@ #include +#include #include #include #include @@ -467,4 +468,16 @@ namespace openstudio { return openstudio::path(); } + std::string_view getSharedLibrarySuffix() { + return "${CMAKE_SHARED_LIBRARY_SUFFIX}"; + } + + std::string_view getSharedLibraryPrefix() { + return "${CMAKE_SHARED_LIBRARY_PREFIX}"; + } + + std::string getSharedLibraryName(std::string_view basename) { + return fmt::format("{}{}{}", getSharedLibraryPrefix(), basename, getSharedLibrarySuffix()); + } + } // openstudio diff --git a/src/utilities/core/ApplicationPathHelpers.hpp b/src/utilities/core/ApplicationPathHelpers.hpp index aff99a35cd3..547c20aaaf7 100644 --- a/src/utilities/core/ApplicationPathHelpers.hpp +++ b/src/utilities/core/ApplicationPathHelpers.hpp @@ -75,6 +75,18 @@ UTILITIES_API openstudio::path getRadianceDirectory(); /// \returns The path to the Perl executable if it exists. UTILITIES_API openstudio::path getPerlExecutable(); +/// \returns The platform specific suffix for shared libraries. +/// "dll" on Windows, ".so" on Linux, etc. +UTILITIES_API std::string_view getSharedLibrarySuffix(); + +/// \returns The platform specific file prefix for shared libraries. +/// "lib" on Linux. +UTILITIES_API std::string_view getSharedLibraryPrefix(); + +/// \returns The platform specific shared library name, +/// concatenates the prefix and suffix to the given basename +UTILITIES_API std::string getSharedLibraryName(std::string_view basename); + } // namespace openstudio #endif //UTILITIES_CORE_PATHHELPERS_HPP diff --git a/src/utilities/core/DynamicLibrary.hpp b/src/utilities/core/DynamicLibrary.hpp new file mode 100644 index 00000000000..fc35a20d7f1 --- /dev/null +++ b/src/utilities/core/DynamicLibrary.hpp @@ -0,0 +1,5 @@ +#if defined(_MSC_VER) +#include "DynamicLibraryWindows.hpp" +#elif defined(HAVE_UNISTD_H) +#include "DynamicLibraryPOSIX.hpp" +#endif diff --git a/src/utilities/core/DynamicLibraryPOSIX.hpp b/src/utilities/core/DynamicLibraryPOSIX.hpp new file mode 100644 index 00000000000..9cf0bd90c92 --- /dev/null +++ b/src/utilities/core/DynamicLibraryPOSIX.hpp @@ -0,0 +1,45 @@ +#ifndef dynamiclibrary_posix_hpp_INCLUDED +#define dynamiclibrary_posix_hpp_INCLUDED + +#include "Path.hpp" +#include +#include +#include + +namespace openstudio { + +struct DynamicLibrary +{ + template [[nodiscard]] Signature *load_symbol(const std::string &name) + { + // reinterpret_cast is necessary here + const auto symbol = reinterpret_cast(dlsym(m_handle.get(), name.c_str())); + + if (symbol == nullptr) { + throw std::runtime_error(fmt::format("Unable to load symbol: '{}', reason: '{}'", name, dlerror())); + } + + return symbol; + } + + explicit DynamicLibrary(openstudio::path location) + : m_location{std::move(location)}, m_handle{dlopen(m_location.c_str(), RTLD_LAZY), m_handle_deleter} + { + if (!m_handle) { + throw std::runtime_error(fmt::format("Unable to load library '{}', reason: '{}'", m_location.string(), dlerror())); + } + } + + static void m_handle_deleter(void *h) { + if (h) { + dlclose(h); + } + } + + openstudio::path m_location{}; + std::unique_ptr m_handle{nullptr, m_handle_deleter}; +}; +} + +#endif // dynamiclibrary_posix_hpp_INCLUDED + diff --git a/src/utilities/core/DynamicLibraryWindows.hpp b/src/utilities/core/DynamicLibraryWindows.hpp new file mode 100644 index 00000000000..c76d60fcd1d --- /dev/null +++ b/src/utilities/core/DynamicLibraryWindows.hpp @@ -0,0 +1,115 @@ +#ifndef sdynamiclibrary_windows_hpp_INCLUDED +#define sdynamiclibrary_windows_hpp_INCLUDED + +#include "Path.hpp" +#include +#include +#include +#include + +namespace openstudio { + +struct DynamicLibrary +{ + + template static [[nodiscard]] std::wstring to_wstring(const T &t_str) + { + return std::wstring(t_str.begin(), t_str.end()); + } + + template static [[nodiscard]] std::string to_string(const T &t_str) + { + return std::string(t_str.begin(), t_str.end()); + } + +#if defined(_UNICODE) || defined(UNICODE) + template static std::wstring to_proper_string(const T &t_str) + { + return to_wstring(t_str); + } +#else + template static std::string [[nodiscard]] to_proper_string(const T &t_str) + { + return to_string(t_str); + } +#endif + + static std::string [[nodiscard]] get_error_message(DWORD t_err) + { + typedef LPTSTR StringType; + +#if defined(_UNICODE) || defined(UNICODE) + std::wstring retval = L"Unknown Error"; +#else + std::string retval = "Unknown Error"; +#endif + StringType lpMsgBuf = nullptr; + + // TODO this reinterpret_cast is suspicious (implementation borrowed from ChaiScript) + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + t_err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&lpMsgBuf), + 0, + nullptr) != 0 && + lpMsgBuf) { + retval = lpMsgBuf; + LocalFree(lpMsgBuf); + } + + return to_string(retval); + } + + template + [[nodiscard]] Signature *load_symbol(const std::string &name) + { + // reinterpret_cast is necessary here + const auto symbol = reinterpret_cast(GetProcAddress(m_handle, name.c_str())); + + if (symbol == nullptr) { + throw std::runtime_error( + fmt::format("Unable to load symbol: '{}', reason: '{}'", name, get_error_message(GetLastError()))); + } + + return symbol; + } + + explicit DynamicLibrary(openstudio::path location) + : m_location{std::move(location)}, m_handle{LoadLibrary(to_proper_string(m_location.string()).c_str())} + { + if (!m_handle) { + throw std::runtime_error(fmt::format( + "Unable to load library '{}', reason: '{}'", m_location.string(), get_error_message(GetLastError()))); + } + } + + DynamicLibrary() = delete; + DynamicLibrary(DynamicLibrary &&other) noexcept : m_handle{other.m_handle} + { + other.m_handle = HMODULE{}; + } + DynamicLibrary(const DynamicLibrary &) = delete; + DynamicLibrary &operator=(DynamicLibrary &&other) noexcept + { + if (m_handle) { + FreeLibrary(m_handle); + } + m_handle = std::exchange(other.m_handle, HMODULE{}); + return *this; + } + DynamicLibrary &operator=(const DynamicLibrary &) = delete; + + ~DynamicLibrary() noexcept + { + if (m_handle) { + FreeLibrary(m_handle); + } + } + + openstudio::path m_location{}; + HMODULE m_handle{}; +}; + +} +#endif