Skip to content

Commit

Permalink
Refactor all things ruby into a shared library
Browse files Browse the repository at this point in the history
The ruby engine is now loaded dynamically on demand like a plugin

ref #4641
  • Loading branch information
kbenne authored and jmarrec committed Sep 13, 2022
1 parent d5e1865 commit 50f6c03
Show file tree
Hide file tree
Showing 48 changed files with 1,746 additions and 1,259 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -1005,6 +1005,7 @@ set(project_directories
sdd
lib
install_utility
scriptengine
)

# resources must be added after find EnergyPlus
Expand Down
199 changes: 8 additions & 191 deletions 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 $<TARGET_FILE_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}" "$<TARGET_FILE_DIR:openstudio_rb>/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" "$<TARGET_FILE_DIR:openstudio_rb>" "${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)
19 changes: 19 additions & 0 deletions 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.
15 changes: 15 additions & 0 deletions 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()
58 changes: 13 additions & 45 deletions ruby/init_openstudio.cpp → ruby/bindings/InitRubyBindings.cpp
Expand Up @@ -27,7 +27,8 @@
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
***********************************************************************************************************************/

#include "init_openstudio.hpp"
#include "InitRubyBindings.hpp"
#include "RubyEval.hpp"
#include <ruby.h>
#include <stdexcept>
#include <iostream>
Expand Down Expand Up @@ -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");

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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
14 changes: 14 additions & 0 deletions 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

0 comments on commit 50f6c03

Please sign in to comment.