From: Aurelien Jacobs Date: Mon, 11 Jan 2016 13:31:47 +0000 (+0100) Subject: Implement Ruby bindings on top of SWIG/C++ bindings. X-Git-Tag: libsigrok-0.4.0~27 X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=27d44cf6e0eaaa75979510596d6148193a8434c0;p=libsigrok.git Implement Ruby bindings on top of SWIG/C++ bindings. --- diff --git a/Makefile.am b/Makefile.am index f536a6c1..808a2369 100644 --- a/Makefile.am +++ b/Makefile.am @@ -654,6 +654,51 @@ CLEAN_EXTRA += python-clean endif +if BINDINGS_RUBY + +RDIR = bindings/ruby +RDOC = $(RDIR)/doc.i +RWRAP = $(RDIR)/classes_wrap.cpp +ROBJ = $(RWRAP:.cpp=.o) +REXT = $(RDIR)/sigrok.so + +$(RDOC): $(srcdir)/bindings/swig/doc.py $(CPPXMLDOC) + $(AM_V_GEN)$(PYTHON) $(srcdir)/bindings/swig/doc.py ruby $(CPPXMLDOC) > $@ + +$(RWRAP): $(RDIR)/classes.i $(RDOC) \ + bindings/swig/classes.i bindings/swig/templates.i \ + bindings/swig/enums.i + $(AM_V_GEN)$(SWIG) -ruby -c++ -Ibindings -Ibindings/cxx/include $(swig_defs) -o $@ $< + +$(ROBJ): $(RWRAP) \ + $(library_include_HEADERS) \ + $(nodist_library_include_HEADERS) \ + $(bindings_cxx_libsigrokcxx_la_include_HEADERS) \ + $(nodist_bindings_cxx_libsigrokcxx_la_include_HEADERS) + $(AM_V_CXX)$(CXX) $(RBSIGROK_CFLAGS) -I. -Iinclude -Ibindings/cxx/include -fPIC -o $@ -c $< + +$(REXT): $(ROBJ) @ORDER@ bindings/cxx/libsigrokcxx.la + $(AM_V_CXXLD)$(CXX) -shared -std=c++11 -o $@ $< -lsigrokcxx -Lbindings/cxx/.libs $(RBSIGROK_LIBS) + +ruby-build: $(REXT) + +ruby-install: $(REXT) + $(INSTALL) -d $(prefix)/$(RBSIGROK_EXTDIR) + $(INSTALL) $< $(prefix)/$(RBSIGROK_EXTDIR) + +ruby-clean: + -$(AM_V_at)rm -fr $(RDIR)/doc + -$(AM_V_at)rm $(REXT) $(ROBJ) $(RWRAP) $(RDOC) + +ruby-doc: $(RWRAP) + $(AM_V_at)yard doc -o $(RDIR)/doc $< + +BUILD_EXTRA += ruby-build +INSTALL_EXTRA += ruby-install +CLEAN_EXTRA += ruby-clean + +endif + if BINDINGS_JAVA JDIR = bindings/java diff --git a/README b/README index 75ca2ee3..7cb2717b 100644 --- a/README +++ b/README @@ -71,6 +71,13 @@ Requirements for the Python bindings: - graphviz (optional, only needed for the Python API docs) - doxypy (optional, only needed for the Python API docs) +Requirements for the Ruby bindings: + + - libsigrokcxx >= 0.3.0 (the libsigrok C++ bindings, see above) + - Ruby >= 1.9.3 (including development files!) + - SWIG >= 3.0.8 + - YARD (optional, only needed for the Ruby API docs) + Requirements for the Java bindings: - libsigrokcxx >= 0.3.0 (the libsigrok C++ bindings, see above) diff --git a/bindings/ruby/classes.i b/bindings/ruby/classes.i new file mode 100644 index 00000000..96bd3770 --- /dev/null +++ b/bindings/ruby/classes.i @@ -0,0 +1,428 @@ +/* + * This file is part of the libsigrok project. + * + * Copyright (C) 2016 Aurelien Jacobs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +%define DOCSTRING +" += Introduction + +The sigrok Ruby API provides an object-oriented Ruby interface to the +functionality in libsigrok. It is built on top of the libsigrokcxx C++ API. + += Getting started + +Usage of the sigrok Ruby API needs to begin with a call to Context.create(). +This will create the global libsigrok context and returns a Context object. +Methods on this object provide access to the hardware drivers, input and output +formats supported by the library, as well as means of creating other objects +such as sessions and triggers. + += Error handling + +When any libsigrok C API call returns an error, an Error exception is raised, +which provides access to the error code and description." +%enddef + +%module(docstring=DOCSTRING) sigrok + +%{ +#include +#include + +#include "config.h" +%} + +%include "../swig/templates.i" + +%{ +static const char *string_from_ruby(VALUE obj) +{ + switch (TYPE(obj)) { + case T_STRING: + return StringValueCStr(obj); + case T_SYMBOL: + return rb_id2name(SYM2ID(obj)); + default: + throw sigrok::Error(SR_ERR_ARG); + } +} + +/* Convert from Glib::Variant to native Ruby types. */ +static VALUE variant_to_ruby(Glib::VariantBase variant) +{ + if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL)) { + return Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get() ? Qtrue : Qfalse; + } else if (variant.is_of_type(Glib::VARIANT_TYPE_BYTE)) { + return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get()); + } else if (variant.is_of_type(Glib::VARIANT_TYPE_INT16)) { + return INT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get()); + } else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT16)) { + return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get()); + } else if (variant.is_of_type(Glib::VARIANT_TYPE_INT32)) { + return INT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get()); + } else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT32)) { + return UINT2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get()); + } else if (variant.is_of_type(Glib::VARIANT_TYPE_INT64)) { + return LL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get()); + } else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64)) { + return ULL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get()); + } else if (variant.is_of_type(Glib::VARIANT_TYPE_DOUBLE)) { + return DBL2NUM(Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get()); + } else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING)) { + auto str = Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get(); + return rb_str_new(str.c_str(), str.length()); + } else if (variant.is_of_type(Glib::VARIANT_TYPE_VARIANT)) { + auto var = Glib::VariantBase::cast_dynamic< Glib::Variant >(variant).get(); + return variant_to_ruby(var); + } else if (variant.is_container()) { + Glib::VariantContainerBase container = Glib::VariantBase::cast_dynamic< Glib::VariantContainerBase > (variant); + gsize count = container.get_n_children(); + if (container.is_of_type(Glib::VARIANT_TYPE_DICTIONARY)) { + VALUE hash = rb_hash_new(); + for (gsize i = 0; i < count; i++) { + Glib::VariantContainerBase entry = Glib::VariantBase::cast_dynamic< Glib::VariantContainerBase > (container.get_child(i)); + VALUE key = variant_to_ruby(entry.get_child(0)); + VALUE val = variant_to_ruby(entry.get_child(1)); + rb_hash_aset(hash, key, val); + } + return hash; + } else if (container.is_of_type(Glib::VARIANT_TYPE_ARRAY) || + container.is_of_type(Glib::VARIANT_TYPE_TUPLE)) { + VALUE array = rb_ary_new2(count); + for (gsize i = 0; i < count; i++) { + VALUE val = variant_to_ruby(container.get_child(i)); + rb_ary_store(array, i, val); + } + return array; + } + } else { + SWIG_exception(SWIG_TypeError, ("TODO: GVariant(" + variant.get_type().get_string() + ") -> Ruby").c_str()); + } +} +%} + +/* Map from Glib::Variant to native Ruby types. */ +%typemap(out) Glib::VariantBase { + $result = variant_to_ruby($1); +} + +/* Map from Glib::VariantContainer to native Ruby types. */ +%typemap(out) Glib::VariantContainerBase { + $result = variant_to_ruby($1); +} + +/* Map from callable Ruby Proc to LogCallbackFunction */ +%typemap(in) sigrok::LogCallbackFunction { + if (!rb_obj_is_proc($input)) + SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object"); + + std::shared_ptr proc(new VALUE($input), rb_gc_unregister_address); + rb_gc_register_address(proc.get()); + + $1 = [=] (const sigrok::LogLevel *loglevel, std::string message) { + VALUE log_obj = SWIG_NewPointerObj( + SWIG_as_voidptr(loglevel), SWIGTYPE_p_sigrok__LogLevel, 0); + + VALUE string_obj = rb_external_str_new_with_enc(message.c_str(), message.length(), rb_utf8_encoding()); + + VALUE args = rb_ary_new_from_args(2, log_obj, string_obj); + rb_proc_call(*proc.get(), args); + }; +} + +/* Map from callable Ruby Proc to SessionStoppedCallback */ +%typemap(in) sigrok::SessionStoppedCallback { + if (!rb_obj_is_proc($input)) + SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object"); + + std::shared_ptr proc(new VALUE($input), rb_gc_unregister_address); + rb_gc_register_address(proc.get()); + + $1 = [=] () { + rb_proc_call(*proc.get(), rb_ary_new()); + }; +} + +/* Map from callable Ruby Proc to DatafeedCallbackFunction */ +%typemap(in) sigrok::DatafeedCallbackFunction { + if (!rb_obj_is_proc($input)) + SWIG_exception(SWIG_TypeError, "Expected a callable Ruby object"); + + std::shared_ptr proc(new VALUE($input), rb_gc_unregister_address); + rb_gc_register_address(proc.get()); + + $1 = [=] (std::shared_ptr device, + std::shared_ptr packet) { + VALUE device_obj = SWIG_NewPointerObj( + SWIG_as_voidptr(new std::shared_ptr(device)), + SWIGTYPE_p_std__shared_ptrT_sigrok__Device_t, SWIG_POINTER_OWN); + + VALUE packet_obj = SWIG_NewPointerObj( + SWIG_as_voidptr(new std::shared_ptr(packet)), + SWIGTYPE_p_std__shared_ptrT_sigrok__Packet_t, SWIG_POINTER_OWN); + + VALUE args = rb_ary_new_from_args(2, device_obj, packet_obj); + rb_proc_call(*proc.get(), args); + }; +} + +/* Cast PacketPayload pointers to correct subclass type. */ +%ignore sigrok::Packet::payload; +%rename sigrok::Packet::_payload payload; +%extend sigrok::Packet +{ + VALUE _payload() + { + if ($self->type() == sigrok::PacketType::HEADER) { + return SWIG_NewPointerObj( + SWIG_as_voidptr(new std::shared_ptr(dynamic_pointer_cast($self->payload()))), + SWIGTYPE_p_std__shared_ptrT_sigrok__Header_t, SWIG_POINTER_OWN); + } else if ($self->type() == sigrok::PacketType::META) { + return SWIG_NewPointerObj( + SWIG_as_voidptr(new std::shared_ptr(dynamic_pointer_cast($self->payload()))), + SWIGTYPE_p_std__shared_ptrT_sigrok__Meta_t, SWIG_POINTER_OWN); + } else if ($self->type() == sigrok::PacketType::ANALOG) { + return SWIG_NewPointerObj( + SWIG_as_voidptr(new std::shared_ptr(dynamic_pointer_cast($self->payload()))), + SWIGTYPE_p_std__shared_ptrT_sigrok__Analog_t, SWIG_POINTER_OWN); + } else if ($self->type() == sigrok::PacketType::LOGIC) { + return SWIG_NewPointerObj( + SWIG_as_voidptr(new std::shared_ptr(dynamic_pointer_cast($self->payload()))), + SWIGTYPE_p_std__shared_ptrT_sigrok__Logic_t, SWIG_POINTER_OWN); + } else { + return Qnil; + } + } +} + +%{ + +#include "libsigrokcxx/libsigrokcxx.hpp" + +/* Convert from a Ruby type to Glib::Variant, according to config key data type. */ +Glib::VariantBase ruby_to_variant_by_key(VALUE input, const sigrok::ConfigKey *key) +{ + enum sr_datatype type = (enum sr_datatype) key->data_type()->id(); + + if (type == SR_T_UINT64 && RB_TYPE_P(input, T_FIXNUM)) + return Glib::Variant::create(NUM2ULL(input)); + if (type == SR_T_UINT64 && RB_TYPE_P(input, T_BIGNUM)) + return Glib::Variant::create(NUM2ULL(input)); + else if (type == SR_T_STRING && RB_TYPE_P(input, T_STRING)) + return Glib::Variant::create(string_from_ruby(input)); + else if (type == SR_T_STRING && RB_TYPE_P(input, T_SYMBOL)) + return Glib::Variant::create(string_from_ruby(input)); + else if (type == SR_T_BOOL && RB_TYPE_P(input, T_TRUE)) + return Glib::Variant::create(true); + else if (type == SR_T_BOOL && RB_TYPE_P(input, T_FALSE)) + return Glib::Variant::create(false); + else if (type == SR_T_FLOAT && RB_TYPE_P(input, T_FLOAT)) + return Glib::Variant::create(RFLOAT_VALUE(input)); + else if (type == SR_T_INT32 && RB_TYPE_P(input, T_FIXNUM)) + return Glib::Variant::create(NUM2INT(input)); + else + throw sigrok::Error(SR_ERR_ARG); +} + +/* Convert from a Ruby type to Glib::Variant, according to Option data type. */ +Glib::VariantBase ruby_to_variant_by_option(VALUE input, std::shared_ptr option) +{ + Glib::VariantBase variant = option->default_value(); + + if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_FIXNUM)) + return Glib::Variant::create(NUM2ULL(input)); + else if (variant.is_of_type(Glib::VARIANT_TYPE_UINT64) && RB_TYPE_P(input, T_BIGNUM)) + return Glib::Variant::create(NUM2ULL(input)); + else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_STRING)) + return Glib::Variant::create(string_from_ruby(input)); + else if (variant.is_of_type(Glib::VARIANT_TYPE_STRING) && RB_TYPE_P(input, T_SYMBOL)) + return Glib::Variant::create(string_from_ruby(input)); + else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_TRUE)) + return Glib::Variant::create(true); + else if (variant.is_of_type(Glib::VARIANT_TYPE_BOOL) && RB_TYPE_P(input, T_FALSE)) + return Glib::Variant::create(false); + else if (variant.is_of_type(Glib::VARIANT_TYPE_DOUBLE) && RB_TYPE_P(input, T_FLOAT)) + return Glib::Variant::create(RFLOAT_VALUE(input)); + else if (variant.is_of_type(Glib::VARIANT_TYPE_INT32) && RB_TYPE_P(input, T_FIXNUM)) + return Glib::Variant::create(NUM2INT(input)); + else + throw sigrok::Error(SR_ERR_ARG); +} + +struct hash_to_map_options_params { + std::map > options; + std::map output; +}; + +int convert_option(VALUE key, VALUE val, VALUE in) { + struct hash_to_map_options_params *params; + params = (struct hash_to_map_options_params *)in; + + auto k = string_from_ruby(key); + auto v = ruby_to_variant_by_option(val, params->options[k]); + params->output[k] = v; + + return ST_CONTINUE; +} + +/* Convert from a Ruby hash to a std::map */ +std::map hash_to_map_options(VALUE hash, + std::map > options) +{ + if (!RB_TYPE_P(hash, T_HASH)) + throw sigrok::Error(SR_ERR_ARG); + + struct hash_to_map_options_params params = { options }; + rb_hash_foreach(hash, (int (*)(ANYARGS))convert_option, (VALUE)¶ms); + + return params.output; +} + +int convert_option_by_key(VALUE key, VALUE val, VALUE in) { + std::map *options; + options = (std::map *)in; + + auto k = sigrok::ConfigKey::get_by_identifier(string_from_ruby(key)); + auto v = ruby_to_variant_by_key(val, k); + (*options)[k] = v; + + return ST_CONTINUE; +} + +%} + +/* Ignore these methods, we will override them below. */ +%ignore sigrok::Analog::data; +%ignore sigrok::Driver::scan; +%ignore sigrok::Input::send; +%ignore sigrok::InputFormat::create_input; +%ignore sigrok::OutputFormat::create_output; + +%include "doc.i" + +%define %attributevector(Class, Type, Name, Get) +%alias sigrok::Class::_ ## Get #Name; +%enddef + +%define %attributemap(Class, Type, Name, Get) +%alias sigrok::Class::_ ## Get #Name; +%enddef + +%define %enumextras(Class) +%extend sigrok::Class +{ + VALUE to_s() + { + std::string str = $self->name(); + return rb_external_str_new_with_enc(str.c_str(), str.length(), rb_utf8_encoding()); + } + + bool operator==(void *other) + { + return (long) $self == (long) other; + } +} +%enddef + +%include "../swig/classes.i" + +/* Replace the original Driver.scan with a keyword arguments version. */ +%rename sigrok::Driver::_scan scan; +%extend sigrok::Driver +{ + std::vector > _scan(VALUE kwargs = rb_hash_new()) + { + if (!RB_TYPE_P(kwargs, T_HASH)) + throw sigrok::Error(SR_ERR_ARG); + + std::map options; + rb_hash_foreach(kwargs, (int (*)(ANYARGS))convert_option_by_key, (VALUE)&options); + + return $self->scan(options); + } +} + +/* Support Input.send() with string argument. */ +%rename sigrok::Input::_send send; +%extend sigrok::Input +{ + void _send(VALUE data) + { + data = StringValue(data); + return $self->send(RSTRING_PTR(data), RSTRING_LEN(data)); + } +} + +/* Support InputFormat.create_input() with keyword arguments. */ +%rename sigrok::InputFormat::_create_input create_input; +%extend sigrok::InputFormat +{ + std::shared_ptr _create_input(VALUE hash = rb_hash_new()) + { + return $self->create_input(hash_to_map_options(hash, $self->options())); + } +} + +/* Support OutputFormat.create_output() with keyword arguments. */ +%rename sigrok::OutputFormat::_create_output create_output; +%extend sigrok::OutputFormat +{ + std::shared_ptr _create_output( + std::shared_ptr device, VALUE hash = rb_hash_new()) + { + return $self->create_output(device, + hash_to_map_options(hash, $self->options())); + } + + std::shared_ptr _create_output(string filename, + std::shared_ptr device, VALUE hash = rb_hash_new()) + { + return $self->create_output(filename, device, + hash_to_map_options(hash, $self->options())); + } +} + +/* Support config_set() with Ruby input types. */ +%extend sigrok::Configurable +{ + void config_set(const ConfigKey *key, VALUE input) + { + $self->config_set(key, ruby_to_variant_by_key(input, key)); + } +} + +/* Return Ruby array from Analog::data(). */ +%rename sigrok::Analog::_data data; +%extend sigrok::Analog +{ + VALUE _data() + { + int num_channels = $self->channels().size(); + int num_samples = $self->num_samples(); + float *data = (float *) $self->data_pointer(); + VALUE channels = rb_ary_new2(num_channels); + for(int i = 0; i < num_channels; i++) { + VALUE samples = rb_ary_new2(num_samples); + for (int j = 0; j < num_samples; j++) { + rb_ary_store(samples, j, DBL2NUM(data[i*num_samples+j])); + } + rb_ary_store(channels, i, samples); + } + return channels; + } +} diff --git a/bindings/swig/doc.py b/bindings/swig/doc.py index f71493d4..e8767af8 100644 --- a/bindings/swig/doc.py +++ b/bindings/swig/doc.py @@ -45,6 +45,8 @@ for compound in index.findall('compound'): if brief: if language == 'python': print('%%feature("docstring") %s "%s";' % (class_name, brief)) + elif language == 'ruby': + print('%%feature("docstring") %s "/* Document-class: %s\\n%s */\\n";' % (class_name, class_name.replace("sigrok", "Sigrok", 1), brief)) elif language == 'java': print('%%typemap(javaclassmodifiers) %s "/** %s */\npublic class"' % ( class_name, brief)) @@ -73,6 +75,12 @@ for compound in index.findall('compound'): class_name, member_name, brief)] + [ '@param %s %s' % (name, desc) for name, desc in parameters.items()]) + '";') + if language == 'ruby' and kind == 'public-func': + print(str.join('\n', [ + '%%feature("docstring") %s::%s "/* %s' % ( + class_name, member_name, brief)] + [ + '@param %s %s' % (name, desc) + for name, desc in parameters.items()]) + ' */\\n";') elif language == 'java' and kind == 'public-func': print(str.join('\n', [ '%%javamethodmodifiers %s::%s "/** %s' % ( @@ -101,3 +109,6 @@ for compound in index.findall('compound'): print('%s.%s.__doc__ = """%s"""' % ( trimmed_name, member_name, brief)) print('%}') + elif language == 'ruby' and constants: + for member_name, brief in constants: + print('%%feature("docstring") %s::%s "/* %s */\\n";' % (class_name, member_name, brief)) diff --git a/configure.ac b/configure.ac index 37172f28..5e8ba933 100644 --- a/configure.ac +++ b/configure.ac @@ -91,6 +91,7 @@ SR_VAR_OPT_PKG([SR_PKGLIBS], [sr_deps_avail]) SR_PKGLIBS_TESTS= SR_PKGLIBS_CXX= SR_PKGLIBS_PYTHON= +SR_PKGLIBS_RUBY= SR_EXTRA_LIBS= SR_ARG_OPT_PKG([libserialport], [LIBSERIALPORT], [NEED_SERIAL], @@ -282,6 +283,10 @@ AC_ARG_ENABLE([python], [AS_HELP_STRING([--enable-python], [build Python bindings [default=yes]])], [], [enable_python=$enable_bindings]) +AC_ARG_ENABLE([ruby], + [AS_HELP_STRING([--enable-ruby], [build Ruby bindings [default=yes]])], + [], [enable_ruby=$enable_bindings]) + AC_ARG_ENABLE([java], [AS_HELP_STRING([--enable-java], [build Java bindings [default=yes]])], [], [enable_java=$enable_bindings]) @@ -386,6 +391,41 @@ AS_IF([test -z "$sr_python_missing"], [BINDINGS_PYTHON=$enable_python], [BINDINGS_PYTHON=no]) AM_CONDITIONAL([BINDINGS_PYTHON], [test "x$BINDINGS_PYTHON" = xyes]) +##################### +## Ruby bindings ## +##################### + +AS_IF([test "x$BINDINGS_CXX" = xyes], + [sr_ruby_missing=], + [sr_ruby_missing='C++ bindings']) + +AX_RUBY_EXT + +AS_IF([test "x$RUBY" = x], + [SR_APPEND([sr_ruby_missing], [', '], ['Ruby'])]) + +# Extract major and minor version number of the Ruby interpreter. +sr_rbmajor=${RUBY_VERSION%%.*} +sr_rbminor=${RUBY_VERSION#*.} +sr_rbminor=${sr_rbminor%%.*} + +# The Ruby bindings need Ruby development files. +SR_PKG_CHECK([ruby_dev], [SR_PKGLIBS_RUBY], + [ruby], + [ruby-$sr_rbmajor.$sr_rbminor]) + +AS_IF([test "x$sr_have_ruby_dev" != xyes], + [SR_APPEND([sr_ruby_missing], [', '], [Headers])]) + +# The Ruby bindings use SWIG to generate code. +AS_IF([test "x$SWIG" = x], + [SR_APPEND([sr_ruby_missing], [', '], [SWIG])]) + +AS_IF([test -z "$sr_ruby_missing"], + [BINDINGS_RUBY=$enable_ruby], [BINDINGS_RUBY=no]) +AM_CONDITIONAL([BINDINGS_RUBY], [test "x$BINDINGS_RUBY" = xyes]) +# AC_SUBST(RUBY_EXT_DLEXT, $RUBY_EXT_DLEXT) + #################### ## Java bindings ## #################### @@ -454,6 +494,7 @@ PKG_CHECK_MODULES([TESTS], [$SR_PKGLIBS_TESTS glib-2.0 $SR_PKGLIBS]) AM_COND_IF([BINDINGS_CXX], [ PKG_CHECK_MODULES([LIBSIGROKCXX], [$SR_PKGLIBS_CXX]) PKG_CHECK_MODULES([PYSIGROK], [$SR_PKGLIBS_PYTHON $SR_PKGLIBS_CXX]) + PKG_CHECK_MODULES([RBSIGROK], [$SR_PKGLIBS_RUBY $SR_PKGLIBS_CXX], [AC_SUBST(RBSIGROK_EXTDIR, "lib/$($PKG_CONFIG --variable=sitearch $SR_PKGLIBS_RUBY)/$($PKG_CONFIG --variable=RUBY_BASE_NAME $SR_PKGLIBS_RUBY)/vendor_ruby/$($PKG_CONFIG --variable=ruby_version $SR_PKGLIBS_RUBY)")]) ]) # Check for specific libusb features, now that we know the CFLAGS. @@ -490,7 +531,7 @@ m4_define([SR_PREPARE_BINDING_REPORT], [ test -z "$sr_$1_missing" || sr_report_$1=" (missing: $sr_$1_missing)" test "x$enable_$1" = xyes || sr_report_$1=' (disabled)' ]) -m4_map_args([SR_PREPARE_BINDING_REPORT], [cxx], [python], [java]) +m4_map_args([SR_PREPARE_BINDING_REPORT], [cxx], [python], [ruby], [java]) cat >&AS_MESSAGE_FD <<_EOF @@ -531,6 +572,7 @@ Enabled SCPI backends: Enabled language bindings: - C++............................. $BINDINGS_CXX$sr_report_cxx - Python.......................... $BINDINGS_PYTHON$sr_report_python + - Ruby............................ $BINDINGS_RUBY$sr_report_ruby - Java............................ $BINDINGS_JAVA$sr_report_java _EOF