2010-01-10 18 views
6

Tengo un error de enlace bastante extraño en un proyecto que usa automake. Lo que hago parece bastante sencillo desde el manual, por lo que realmente preguntarse qué puedo hacer el mal ...Referencia no definida al compilar contra bibliotecas generadas por Automake

Mi proyecto tiene tres carpetas:

  • src/común, en el que compila una serie de C++ archivos en un libube-common.a lib estático
  • src/engine, en el que compilo una serie de archivos en un libube-engine.a lib estático
  • src/client, en el cual ... lo adivinó, libue-client.a, y también un archivo ube.cpp que es mi principal

Cada una de las bibliotecas se compila con un Makefile.am así:

noinst_LIBRARIES=libube-common.a 
libube_common_a_SOURCES=gettext.h lua_helper.hpp \ 
silent_ostream.hpp \ 
logging.hpp logging.cpp \ 
logger_interface.hpp \ 
    ... etc ... 
AM_CPPFLAGS=-DSRCDIR=\"${srcdir}\" \ 
-DLUADIR=\"${luadir}\" \ 
-Wall -Werror \ 
-I$(srcdir)/../../include \ 
$(LUA_INCLUDE) \ 
$(BOOST_CPPFLAGS) 

Esto da lugar a los diversos objetos que se construyen con una línea como:

g++ -DHAVE_CONFIG_H -I. -I../../../../../src/common -I../.. -DSRCDIR=\"../../../../../src/common\" -DLUADIR=\"\" -Wall -Werror -I../../../../../src/common/../../include -I/usr/include/lua5.1 -I/usr/include -g -O2 -MT logging.o -MD -MP -MF .deps/logging.Tpo -c -o logging.o ../../../../../src/common/logging.cpp 

Y todos ellos se ponen en la biblioteca con:

ar cru libube-common.a logging.o prefix_resource_resolver.o stat_file_checker.o 
ranlib libube-common.a 

Todo esto parece bueno y bien, que incluso puede vinculada algunos programas de pequeñas pruebas en contra de la biblioteca (dentro de la misma makefile)

Luego, en el Makefile.am de mi programa principal, pedí a enlazar con las bibliotecas locales:

ube_LDADD=../common/libube-common.a \ 
     ../engine/libube-engine.a \ 
     libube-client.a \ 
      ... other libs ... 

y ahí es donde se producen errores como este:

g++ -g -O2 -o ube ube.o ../common/libube-common.a ../engine/libube-engine.a libube- client.a -L/usr/include/lua5.1/lib -llua5.1 -lm -ldl -L/usr/lib -lSDL -lSDL_image -lpng -ltiff -ljpeg -lz -lSDL_ttf -lfreetype -lSDL_mixer -lSDL_mixer -lSDL_ttf -lSDL_image 

libube-client.a(game_loop.o): In function `Logging::debug_ostream(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)': 
(...) logging.hpp:54: undefined reference to `Logging::get_ostream(LogLevel::Level, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)' 
(...)logging.hpp:54: undefined reference to `Logging::get_ostream(LogLevel::Level, std::basic_string<char, std::char_traits<char>, std::allocator<char> >)' 

Al principio Pensé que era debido a algunos símbolos estáticos, pero también tengo el problema con los no estáticos.

comprobado que las librerías generadas, y que parecen contener el símbolo correctamente:

~/prj/ube/builds/linux/current/src/common$ nm -C libube-common.a | grep logging.o -C 20 

logging.o: 
00000010 t global constructors keyed to _ZN7Logging15disable_loggingEv 
00000000 V guard variable for Logging::get_instance()::s_local_instance 
000000b0 T Logging::get_ostream(LogLevel::Level, std::string) 
00000000 T Logging::disable_logging() 
00000040 T Logging::is_category_enabled(LogLevel::Level, std::string&) 
    U std::ios_base::Init::Init() 
    U std::ios_base::Init::~Init() 
00000000 b std::__ioinit 
    U __cxa_atexit 
    U __dso_handle 
    U __gxx_personality_v0 

La única solución es vincular explícitamente en contra de mis archivos .o (añadiéndolos a la línea ube_LDADD ... pero que desafía un poco la idea de utilizar una biblioteca !!)

me parece que han estado siguiendo el manual: http://www.gnu.org/software/hello/manual/automake/Linking.html#Linking

Pero ovbiously metí la pata en algún lugar, por lo que cualquier idea es bienvenida !!

Gracias

PH


EDIT: La biblioteca en sí mismo parece funcionar, parece ser un problema de vinculación. Puedo vincular mis programas de casos de prueba contra ellos. Esto es lo que hago:

En la carpeta src/common/tests, hay una principal llamada common-tests.cpp que ejecuta pruebas unitarias; el bin de pruebas comunes está vinculado a la biblioteca libube-common.un (sólo necesita objetos que se encuentran dentro de la lib, ya que esos son pruebas unitarias)

# There is one program that aggreatates all tests cases 
check_PROGRAMS = common-tests 
common_tests_SOURCES= tests/common_tests.cpp \ 
tests/prefix_resource_resolver_test.cpp \ 
tests/mock_file_checker.hpp \ 
tests/stat_file_checker_test.cpp 

# The program needs to be compiled against the local lib 
common_tests_LDADD=libube-common.a -L$(top_srcdir)/lib -lgtest -lgmock -llua -ldl 

# This means common-tests is run when using 'make check'. 
TESTS = common-tests 

Cuando se ejecuta make check, el programa de prueba se compila esta manera:

g++ -g -O2 -o common-tests common_tests.o prefix_resource_resolver_test.o stat_file_checker_test.o libube-common.a -L../../../../../lib -lgtest -lgmock -llua -ldl -lSDL_mixer -lSDL_ttf -lSDL_image 

Y las cosas funcionan perfectamente. La única diferencia que puedo ver es que en este caso la biblioteca está justo al lado del ejecutable para vincular ... ¿podría esto realmente marcar la diferencia?

Además, he intentado usar opciones como -Wl, - todo el archivo, pero no sirvió de nada (además de que no sé cómo agregarlos a la línea generada por Automake ...)

+0

¿Esas mismas bibliotecas funcionan en su plataforma? ¿Puedes vincularlos a una pequeña caja de prueba? – Potatoswatter

+0

Normalmente, es, ver mis ediciones ... – phtrivier

Respuesta

4

Este Es muy probable que se trate de un problema de orden de la biblioteca: cuanto más "común" sea una biblioteca, más tarde debería ocurrir en la línea de enlace final. Específicamente, GNU ld lee las bibliotecas para símbolos exactamente una vez, y luego descarta todos los demás símbolos de la biblioteca, antes de pasar a la próxima instrucción de la biblioteca. Hay varias soluciones (vea la página de manual para 'ld') pero la más fácil es reordenar las líneas en su Makefile.am para poner libube-common.a después de las libs del cliente y del motor.

Tenga en cuenta que Darwin ld hace no tiene este comportamiento, conserva todos los símbolos de la biblioteca de forma predeterminada (que potencialmente utiliza mucha más memoria durante la vinculación).

+0

Has leído mi mente (o de hecho, he leído la tuya, o ambos simplemente agregamos la misma epifanía al mismo tiempo): era un problema de pedido. Ver, como mi lib de cliente depende de las librerías de motor que dependen de las librerías comunes, las escribí ingenuamente en el orden common - engine - client. Acabo de cambiar el orden a cliente, motor, común, y adivina qué, todo funciona. Estaba a punto de editar mi pregunta, ¡pero obtienes puntos por la acción preventiva! Gracias a ti y a http://www.network-theory.co.uk/docs/gccintro/gccintro_18.html. – phtrivier

+0

Esto acaba de resolver un problema que me afectó durante horas :-) – krico

+0

Gracias, yo también estaba atrapado ... –

Cuestiones relacionadas