2012-02-12 15 views
5

Comencé un pequeño experimento hoy: escribí una clase de C++ que depende de algunas otras bibliotecas (ALGLIB, Eigen, herramientas internas) y quería crear un contenedor de Ruby para esa clase. Actualmente estoy usando Rice para hacer eso. Primero escribí una muy simple envoltorio de C++ para mi clase:Extensión de C++ Ruby con bibliotecas externas

// @file MLPWrapper.h 
#pragma once 

#include "mlp/MLP.h" 
#include <ruby.h> 

class MLPWrapper 
{ 
    MLP mlp; // <- my C++ class 
public: 
    ... 
    void fit() 
    { 
    ... 
    mlp.fit(stop); 
    } 
}; 

de la biblioteca CPP-archivo es la siguiente:

// @file cmlp.cpp 
#include "rice/Data_Type.hpp" 
#include "rice/Constructor.hpp" 
#include "MLPWrapper.h" 

using namespace Rice; 

extern "C" 
void Init_cmlp() 
{ 
    Data_Type<MLPWrapper> rb_cMLPWrapper = define_class<MLPWrapper>("MLP") 
     .define_constructor(Constructor<MLPWrapper>()) 
     ... 
     .define_method("fit", &MLPWrapper::fit); 
} 

y este es el extconf.rb:

require "mkmf-rice" 

$CFLAGS << "-O3" 

HEADER_DIRS = [ 
    "..", 
    "../../external/libraries/eigen-eigen-3.0.1", 
    "../../external/libraries/alglib/cpp/src", 
    "../../external/libraries/CMA-ESpp" 
] 
LIB_DIRS = [ 
    "../../build/external/libraries/alglib/cpp/src", 
    "../../build/external/libraries/CMA-ESpp/cma-es", 
    "../../build/src/tools" 
] 

dir_config("libs", HEADER_DIRS, LIB_DIRS) 

have_library("libtools") 
have_library("libalglib") 

create_makefile("cmlp") 

Todo funciona bien, excepto el enlace. Obviamente, se incluyen los archivos de encabezado de las bibliotecas, de lo contrario no se compilaría. Pero cuando ejecuto un pequeño programa de prueba ("require" cmlp "; mlp = MLP.new") en Ruby no encuentra el símbolo _ZN6LoggerC1ENS_6TargetESs, que es parte de libtools (una enumeración). Esto es lo que sucede cuando construyo la extensión C++ y ejecutar el programa de prueba:

$ ruby extconf.rb 
checking for main() in -lrice... yes 
checking for main() in -llibtools... no 
checking for main() in -llibalglib... no 
checking for main() in -llibcmaes... no 
creating Makefile 
$ make 
g++ -I. -I. -I/usr/lib/ruby/1.8/x86_64-linux -I. -I.. -I../../external/libraries/eigen-eigen-3.0.1 -I../../external/libraries/alglib/cpp/src -I../../external/libraries/CMA-ESpp  -I/var/lib/gems/1.8/gems/rice-1.4.3/ruby/lib/include -fPIC -fno-strict-aliasing -g -g -O2 -fPIC -O3 -Wall -g -c cmlp.cpp 
g++ -shared -o cmlp.so cmlp.o -L. -L/usr/lib -L../../build/external/libraries/alglib/cpp/src -L../../build/external/libraries/CMA-ESpp/cma-es -L../../build/src/tools -L. -Wl,-Bsymbolic-functions -rdynamic -Wl,-export-dynamic -L/var/lib/gems/1.8/gems/rice-1.4.3/ruby/lib/lib -lrice -lruby1.8 -lpthread -lrt -ldl -lcrypt -lm -lc 
$ ruby test.rb 
ruby: symbol lookup error: ./cmlp.so: undefined symbol: _ZN6LoggerC1ENS_6TargetESs 

Las bibliotecas están compilados con CMake (add_library (...)) y se encuentran en

../../build/src/tools/libtools.so 
../../build/external/libraries/alglib/cpp/src/libalglib.so 
../../build/external/libraries/CMA-ESpp/cma-es/libcmaes.so 

I no sé cómo resolver este problema por mi cuenta y no pude encontrar ninguna documentación útil para mi problema. ¿Cómo arreglo este extconf.rb? Agradezco cada sugerencia.

edición: OK, he cambiado el extconf.rb:

require "rubygems" 
require "mkmf-rice" 

BASE_DIR = "/bla/" 

$CFLAGS << " -O3" 

dir_config("tools", [BASE_DIR + "src", BASE_DIR + "external/libraries/eigen-eigen-3.0.1"], BASE_DIR + "build/src/tools") 
unless have_library("tools") 
    abort "tools are missing. please compile tools" 
end 

dir_config("alglib", BASE_DIR + "external/libraries/alglib/cpp/src", BASE_DIR + "build/external/libraries/alglib/cpp/src") 
unless have_library("alglib") 
    abort "alglib is missing. please compile alglib" 
end 

dir_config("cmaes", BASE_DIR + "external/libraries/CMA-ESpp", BASE_DIR + "build/external/libraries/CMA-ESpp/cma-es") 
unless have_library("cmaes") 
    abort "cmaes is missing. please compile cmaes" 
end 

create_makefile("cmlp") 

El Makefile generado es:

SHELL = /bin/sh 

#### Start of system configuration section. #### 

srcdir = . 
topdir = /usr/lib/ruby/1.8/x86_64-linux 
hdrdir = $(topdir) 
VPATH = $(srcdir):$(topdir):$(hdrdir) 
exec_prefix = $(prefix) 
prefix = $(DESTDIR)/usr 
sharedstatedir = $(prefix)/com 
mandir = $(prefix)/share/man 
psdir = $(docdir) 
oldincludedir = $(DESTDIR)/usr/include 
localedir = $(datarootdir)/locale 
bindir = $(exec_prefix)/bin 
libexecdir = $(prefix)/lib/ruby1.8 
sitedir = $(DESTDIR)/usr/local/lib/site_ruby 
htmldir = $(docdir) 
vendorarchdir = $(vendorlibdir)/$(sitearch) 
includedir = $(prefix)/include 
infodir = $(prefix)/share/info 
vendorlibdir = $(vendordir)/$(ruby_version) 
sysconfdir = $(DESTDIR)/etc 
libdir = $(exec_prefix)/lib 
sbindir = $(exec_prefix)/sbin 
rubylibdir = $(libdir)/ruby/$(ruby_version) 
docdir = $(datarootdir)/doc/$(PACKAGE) 
dvidir = $(docdir) 
vendordir = $(libdir)/ruby/vendor_ruby 
datarootdir = $(prefix)/share 
pdfdir = $(docdir) 
archdir = $(rubylibdir)/$(arch) 
sitearchdir = $(sitelibdir)/$(sitearch) 
datadir = $(datarootdir) 
localstatedir = $(DESTDIR)/var 
sitelibdir = $(sitedir)/$(ruby_version) 

CC = gcc 
LIBRUBY = $(LIBRUBY_SO) 
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a 
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME) 
LIBRUBYARG_STATIC = -lruby1.8-static 

RUBY_EXTCONF_H = 
CFLAGS = -fPIC -fno-strict-aliasing -g -g -O2 -fPIC $(cflags) -O3 
INCFLAGS = -I. -I. -I/usr/lib/ruby/1.8/x86_64-linux -I. 
DEFS  = 
CPPFLAGS = -I/bla/external/libraries/CMA-ESpp -I/bla//external/libraries/alglib/cpp/src -I//bla/src -I/bla/external/libraries/eigen-eigen-3.0.1  -I/var/lib/gems/1.8/gems/rice-1.4.3/ruby/lib/include 
CXXFLAGS = $(CFLAGS) -Wall -g 
ldflags = -L. -Wl,-Bsymbolic-functions -rdynamic -Wl,-export-dynamic -L/var/lib/gems/1.8/gems/rice-1.4.3/ruby/lib/lib 
dldflags = 
archflag = 
DLDFLAGS = $(ldflags) $(dldflags) $(archflag) 
LDSHARED = g++ -shared 
AR = ar 
EXEEXT = 

RUBY_INSTALL_NAME = ruby1.8 
RUBY_SO_NAME = ruby1.8 
arch = x86_64-linux 
sitearch = x86_64-linux 
ruby_version = 1.8 
ruby = /usr/bin/ruby1.8 
RUBY = $(ruby) 
RM = rm -f 
MAKEDIRS = mkdir -p 
INSTALL = /usr/bin/install -c 
INSTALL_PROG = $(INSTALL) -m 0755 
INSTALL_DATA = $(INSTALL) -m 644 
COPY = cp 

#### End of system configuration section. #### 

preload = 

CXX = g++ 
libpath = . $(libdir) /bla/external/libraries/CMA-ESpp/cma-es /bla/build/external/libraries/alglib/cpp/src /bla/build/src/tools 
LIBPATH = -L. -L$(libdir) -L/bla/build/external/libraries/CMA-ESpp/cma-es -L/bla/build/external/libraries/alglib/cpp/src -L/bla/build/src/tools 
DEFFILE = 

CLEANFILES = mkmf.log 
DISTCLEANFILES = 

extout = 
extout_prefix = 
target_prefix = 
LOCAL_LIBS = 
LIBS = -lcmaes -lalglib -ltools -lrice -lruby1.8 -lpthread -lrt -ldl -lcrypt -lm -lc 
SRCS = cmlp.cpp 
OBJS = cmlp.o 
TARGET = cmlp 
DLLIB = $(TARGET).so 
EXTSTATIC = 
STATIC_LIB = 

BINDIR  = $(bindir) 
RUBYCOMMONDIR = $(sitedir)$(target_prefix) 
RUBYLIBDIR = $(sitelibdir)$(target_prefix) 
RUBYARCHDIR = $(sitearchdir)$(target_prefix) 

TARGET_SO  = $(DLLIB) 
CLEANLIBS  = $(TARGET).so $(TARGET).il? $(TARGET).tds $(TARGET).map 
CLEANOBJS  = *.o *.a *.s[ol] *.pdb *.exp *.bak 

all:  $(DLLIB) 
static:  $(STATIC_LIB) 

clean: 
     @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) 

distclean: clean 
     @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log 
     @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES) 

realclean: distclean 
install: install-so install-rb 

install-so: $(RUBYARCHDIR) 
install-so: $(RUBYARCHDIR)/$(DLLIB) 
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB) 
    $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR) 
install-rb: pre-install-rb install-rb-default 
install-rb-default: pre-install-rb-default 
pre-install-rb: Makefile 
pre-install-rb-default: Makefile 
$(RUBYARCHDIR): 
    $(MAKEDIRS) [email protected] 

site-install: site-install-so site-install-rb 
site-install-so: install-so 
site-install-rb: install-rb 

.SUFFIXES: .c .m .cc .cxx .cpp .C .o 

.cc.o: 
    $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< 

.cxx.o: 
    $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< 

.cpp.o: 
    $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< 

.C.o: 
    $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $< 

.c.o: 
    $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $< 

$(DLLIB): $(OBJS) Makefile 
    @-$(RM) [email protected] 
    $(LDSHARED) -o [email protected] $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS) 



$(OBJS): ruby.h defines.h 

Respuesta

4
../../build/src/tools/libtools.so 
../../build/external/libraries/alglib/cpp/src/libalglib.so  
../../build/external/libraries/CMA-ESpp/cma-es/libcmaes.so 

podría ser el problema. Yo probaría caminos absolutos aquí, podría imaginar que el pwd es diferente de lo esperado mientras ejecuta ruby extconf.rb. Además, supongo que necesita una entrada para cada biblioteca dir_config desea vincular. Así

debe sustituirse por

dir_config('tools', '<Path to include dir>', '<Path to lib dir>') 
dir_config('alglib', '<Path to include dir>', '<Path to lib dir>') 

Nota

dir_config('libs', HEADER_DIRS, LIB_DIRS) 

que el líder 'lib' debe omitirse , tal como lo haría en la opción -l del vinculador. A continuación, si usted quiere estar absolutamente seguro de que una biblioteca se encuentra, reemplace

have_library('libtools') 

por

have_library('tools') or raise 

Una vez más, 'lib' se omite.

Si aún no puede resolver el problema, publique el Makefile generado por `ruby extconf.rb '.

+0

OK, sus sugerencias me ayudaron un poco. Ruby encuentra las bibliotecas ahora, pero no se cargan cuando ejecuto el script de prueba. Este es el resultado: _./cmlp.so: libcmaes.so: no se puede abrir el archivo de objeto compartido: Ningún archivo o directorio - ./cmlp.so (LoadError) de /usr/lib/ruby/vendor_ruby/1.8/ rubygems/custom_require.rb: 36: en 'require ' de test.rb: 2_ Adjunté el Makefile. – alfa

+0

Cuando configuro LD_LIBRARY_PATH funciona, cuando trato de enlazar con bibliotecas estáticas, aparece el siguiente error: _/usr/bin/ld:/bla/build/externo/libraries/CMA-ESpp/cma-es/libcmaes. a (timings.cpp.o): la reubicación R_X86_64_PC32 contra el símbolo indefinido 'clock @@ GLIBC_2.2.5 'no se puede usar al crear un objeto compartido; recompile con -fPIC_ ¿Hay alguna forma mejor de resolver este problema que establecer la variable de entorno? – alfa

+2

Sí, si configura la opción 'rpath' de su enlazador correctamente ([vea también] (http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html)), no es necesario establecer LD_LIBRARY_PATH. Sin arroz, puedo confirmar que está configurado correctamente en tu Makefile cuando llamas a 'ruby extconf.rb --with-tools-lib-dir = --with-alglib-dir = '. Tal vez le das una oportunidad? Compruebe en el Makefile generado si la ruta de búsqueda en tiempo de ejecución está configurada correctamente. Otra opción es, por supuesto, simplemente instalar sus bibliotecas en una de las rutas de búsqueda estándar. – emboss

Cuestiones relacionadas