diff --git a/plugins/ruby/CMakeLists.txt b/plugins/ruby/CMakeLists.txt index 440f3a249..bcd10ac89 100644 --- a/plugins/ruby/CMakeLists.txt +++ b/plugins/ruby/CMakeLists.txt @@ -1,15 +1,9 @@ -find_package(Ruby) -if(RUBY_FOUND) - ADD_CUSTOM_COMMAND( - OUTPUT ruby-autogen.rb - COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb - DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl - ) - ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb) - include_directories("${dfhack_SOURCE_DIR}/depends/tthread" ${RUBY_INCLUDE_PATH}) - DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) - target_link_libraries(ruby ${RUBY_LIBRARY}) - install(FILES ruby.rb ruby-autogen.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION}) -else(RUBY_FOUND) - MESSAGE(STATUS "Required library (ruby) not found - ruby plugin can't be built.") -endif(RUBY_FOUND) +ADD_CUSTOM_COMMAND( + OUTPUT ruby-autogen.rb + COMMAND ${PERL_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/codegen.pl ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml ${CMAKE_CURRENT_SOURCE_DIR}/ruby-autogen.rb + DEPENDS ${dfhack_SOURCE_DIR}/library/include/df/codegen.out.xml codegen.pl +) +ADD_CUSTOM_TARGET(ruby-autogen-rb ALL DEPENDS ruby-autogen.rb) +include_directories("${dfhack_SOURCE_DIR}/depends/tthread") +DFHACK_PLUGIN(ruby ruby.cpp LINK_LIBRARIES dfhack-tinythread) +install(FILES ruby.rb ruby-autogen.rb DESTINATION ${DFHACK_LIBRARY_DESTINATION}) diff --git a/plugins/ruby/ruby.cpp b/plugins/ruby/ruby.cpp index c6053900e..189fc7a3b 100644 --- a/plugins/ruby/ruby.cpp +++ b/plugins/ruby/ruby.cpp @@ -11,8 +11,6 @@ #include "tinythread.h" -#include - using namespace DFHack; @@ -20,6 +18,8 @@ using namespace DFHack; // DFHack stuff +static int df_loadruby(void); +static void df_unloadruby(void); static void df_rubythread(void*); static command_result df_rubyload(color_ostream &out, std::vector & parameters); static command_result df_rubyeval(color_ostream &out, std::vector & parameters); @@ -45,6 +45,11 @@ DFHACK_PLUGIN("ruby") DFhackCExport command_result plugin_init ( color_ostream &out, std::vector &commands) { + if (!df_loadruby()) { + Core::printerr("failed to load libruby\n"); + return CR_FAILURE; + } + m_irun = new tthread::mutex(); m_mutex = new tthread::mutex(); r_type = RB_INIT; @@ -90,6 +95,8 @@ DFhackCExport command_result plugin_shutdown ( color_ostream &out ) m_mutex->unlock(); delete m_mutex; + df_unloadruby(); + return CR_OK; } @@ -191,6 +198,111 @@ static command_result df_rubyeval(color_ostream &out, std::vector // ruby stuff +// ruby-dev on windows is messy +// ruby.h on linux 64 is broken +// so we dynamically load libruby instead of linking it at compile time +// lib path can be set in dfhack.ini to use the system libruby, but by +// default we'll embed our own (downloaded by cmake) + +// these ruby definitions are invalid for windows 64bit +typedef unsigned long VALUE; +typedef unsigned long ID; + +#define Qfalse ((VALUE)0) +#define Qtrue ((VALUE)2) +#define Qnil ((VALUE)4) + +#define INT2FIX(i) ((VALUE)((((long)i) << 1) | 1)) +#define FIX2INT(i) (((long)i) >> 1) +#define RUBY_METHOD_FUNC(func) ((VALUE(*)(...))func) + +VALUE *rb_eRuntimeError; + +void (*ruby_sysinit)(int *, const char ***); +void (*ruby_init)(void); +void (*ruby_init_loadpath)(void); +void (*ruby_script)(const char*); +void (*ruby_finalize)(void); +ID (*rb_intern)(const char*); +VALUE (*rb_raise)(VALUE, const char*, ...); +VALUE (*rb_funcall)(VALUE, ID, int, ...); +VALUE (*rb_define_module)(const char*); +void (*rb_define_singleton_method)(VALUE, const char*, VALUE(*)(...), int); +void (*rb_define_const)(VALUE, const char*, VALUE); +void (*rb_load_protect)(VALUE, int, int*); +VALUE (*rb_gv_get)(const char*); +VALUE (*rb_str_new)(const char*, long); +VALUE (*rb_str_new2)(const char*); +char* (*rb_string_value_ptr)(VALUE*); +VALUE (*rb_eval_string_protect)(const char*, int*); +VALUE (*rb_ary_shift)(VALUE); +VALUE (*rb_float_new)(double); +double (*rb_num2dbl)(VALUE); +VALUE (*rb_int2inum)(long); +VALUE (*rb_uint2inum)(unsigned long); +unsigned long (*rb_num2ulong)(VALUE); +// end of rip(ruby.h) + +DFHack::DFLibrary *libruby_handle; + +// load the ruby library, initialize function pointers +static int df_loadruby(void) +{ + const char *libpath = +#ifdef WIN32 + "msvcrt-ruby191.dll"; +#else + "libruby1.9.so.1.9.0"; +#endif + + libruby_handle = OpenPlugin(libpath); + if (!libruby_handle) + return 0; + + if (!(rb_eRuntimeError = (VALUE*)LookupPlugin(libruby_handle, "rb_eRuntimeError"))) + return 0; + + // XXX does msvc support decltype ? might need a #define decltype typeof + // or just assign to *(void**)(&s) = ... + // ruby_sysinit is optional (ruby1.9 only) + ruby_sysinit = (decltype(ruby_sysinit))LookupPlugin(libruby_handle, "ruby_sysinit"); +#define rbloadsym(s) if (!(s = (decltype(s))LookupPlugin(libruby_handle, #s))) return 0 + rbloadsym(ruby_init); + rbloadsym(ruby_init_loadpath); + rbloadsym(ruby_script); + rbloadsym(ruby_finalize); + rbloadsym(rb_intern); + rbloadsym(rb_raise); + rbloadsym(rb_funcall); + rbloadsym(rb_define_module); + rbloadsym(rb_define_singleton_method); + rbloadsym(rb_define_const); + rbloadsym(rb_load_protect); + rbloadsym(rb_gv_get); + rbloadsym(rb_str_new); + rbloadsym(rb_str_new2); + rbloadsym(rb_string_value_ptr); + rbloadsym(rb_eval_string_protect); + rbloadsym(rb_ary_shift); + rbloadsym(rb_float_new); + rbloadsym(rb_num2dbl); + rbloadsym(rb_int2inum); + rbloadsym(rb_uint2inum); + rbloadsym(rb_num2ulong); +#undef rbloadsym + + return 1; +} + +static void df_unloadruby(void) +{ + if (libruby_handle) { + ClosePlugin(libruby_handle); + libruby_handle = 0; + } +} + + // ruby thread code static void dump_rb_error(void) { @@ -353,7 +465,7 @@ static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) */ static VALUE rb_dfregister(VALUE self, VALUE name, VALUE descr) { - rb_raise(rb_eRuntimeError, "not implemented"); + rb_raise(*rb_eRuntimeError, "not implemented"); } static VALUE rb_dfget_global_address(VALUE self, VALUE name) @@ -402,7 +514,7 @@ static VALUE rb_dfmalloc(VALUE self, VALUE len) { char *ptr = (char*)malloc(FIX2INT(len)); if (!ptr) - rb_raise(rb_eRuntimeError, "no memory"); + rb_raise(*rb_eRuntimeError, "no memory"); memset(ptr, 0, FIX2INT(len)); return rb_uint2inum((long)ptr); }