I'm trying to create a C library, and a C++ test program for the library using Boost::Test. The code that I posted in this question is a simplification of my code, and it displays the exact same problem. Please help!
Here's the directory structure. childlib
is the library I'm trying to create, and test
is the test program.
> tree
.
`-- src
|-- main
| |-- c
| | `-- childlib.c
| |-- childlib.h
| `-- makefile
`-- test
|-- cpp
| `-- test.cpp
`-- makefile
5 directories, 5 files
I can make childlib
into a static library successfully:
> cd src/main
> make
gcc -c -fPIC -o c/childlib.o c/childlib.c
ar rcs libchildlib.a c/childlib.o
ranlib libchildlib.a
But I can't make my test program by linking with it:
> cd ../test/
> make
g++ -I. -I../main -Imy_boost_install_dir/include -c -std=c++11 -o cpp/test.o cpp/test.cpp
g++ -L../main -Lmy_boost_install_dir/lib -lchildlib -lboost_unit_test_framework -Wl,-rpath=my_boost_install_dir/lib -o test_childlib cpp/test.o
cpp/test.o: In function `test_func1::test_method()':
test.cpp:(.text+0x15e7e): undefined reference to `childlib_func1()'
collect2: error: ld returned 1 exit status
make: *** [test_childlib] Error 1
On the other hand, if I run the compilation manually by removing the reference to the static lib and adding childlib.c as a source file, I can make the test program successfully:
> g++ -Lmy_boost_install_dir/lib -lboost_unit_test_framework -Wl,-rpath=my_boost_install_dir/lib -o test_childlib cpp/test.o ../main/c/childlib.c
> ./test_childlib
Running 1 test case...
*** No errors detected
Here's the various source files. test.cpp:
#include <cstdlib>
#include <stdint.h>
#define BOOST_TEST_MODULE childlib test
#include <boost/test/unit_test.hpp>
#include <boost/test/included/unit_test.hpp>
#include "childlib.h"
BOOST_AUTO_TEST_CASE( test_func1 ) {
childlib_func1();
BOOST_CHECK(true);
}
childlib.h:
#ifndef CHILDLIB_H_
#define CHILDLIB_H_
void childlib_func1();
#endif /* CHILDLIB_H_ */
and finally childlib.c:
#include <stdint.h>
void childlib_func1() {
return;
}
The two makefiles are below.
childlib
makefile: Eventually I want to also create a dynamic library but right now it's commented out of make all
:
# childlib
CC = gcc
SRCS = $(wildcard c/*.c)
OBJS = $(SRCS:.c=.o)
HDRS = $(wildcard *.h)
MODULE_BASE_NAME = childlib
MODULE_LIB_A = lib$(MODULE_BASE_NAME).a
MODULE_LIB_SO = lib$(MODULE_BASE_NAME).so
INCLUDE_DIRS = .
INCLUDE = $(addprefix -I,$(INCLUDE_DIRS))
CFLAGS = -c -fPIC
all: $(MODULE_LIB_A) # $(MODULE_LIB_SO)
$(MODULE_LIB_A): $(OBJS)
ar rcs $@ $^
ranlib $@
$(MODULE_LIB_SO): $(OBJS)
$(CC) -shared -o $@ $^
.c.o:
$(CC) $(CFLAGS) -o $@ $^
clean:
-rm -f $(MODULE_LIB_A) $(MODULE_LIB_SO) $(OBJS)
-find . -name '*~' -delete
Here's the test harness makefile:
# test
CC = g++
SRCS = $(wildcard cpp/*.cpp)
OBJS = $(SRCS:.cpp=.o)
MODULE_EXE = test_childlib
MAIN_DIR = ../main
BOOST_DIR = my_boost_install_dir
BOOST_INC_DIR = $(BOOST_DIR)/include
BOOST_LIB_DIR = $(BOOST_DIR)/lib
BOOST_LIBS = boost_unit_test_framework
INCLUDE = $(addprefix -I,. $(MAIN_DIR) $(BOOST_INC_DIR))
LIB_DIRS = $(addprefix -L,$(MAIN_DIR) $(BOOST_LIB_DIR))
LIBS = $(addprefix -l,childlib $(BOOST_LIBS))
LINKER_OPTS = -Wl,-rpath=$(BOOST_LIB_DIR)
CFLAGS = -c -std=c++11
all: test
test: $(MODULE_EXE)
./$(MODULE_EXE)
$(MODULE_EXE): $(OBJS)
$(CC) $(LIB_DIRS) $(LIBS) $(LINKER_OPTS) -o $@ $^
.cpp.o:
$(CC) $(INCLUDE) $(CFLAGS) -o $@ $^
clean:
-rm -f $(MODULE_EXE) $(OBJS)
-find . -name '*~' -delete
I'm using gcc & g++ 4.8.1. I'm also using boost 1.54.0 compiled with gcc 4.7.2.
It seems like I need to just provide the right options to g++ in my test makefile, but I don't know what they are. Can someone please help me link the childlib
library with my test program?
I haven't seen all your code, but the problem is most likely caused by how static linking works (http://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking/).
Try changing the order of the libraries, and specifically, try changing this:
$(MODULE_EXE): $(OBJS)
$(CC) $(LIB_DIRS) $(LIBS) $(LINKER_OPTS) -o $@ $^
into this:
$(MODULE_EXE): $(OBJS)
$(CC) $(LIB_DIRS) $(LINKER_OPTS) -o $@ $^ $(LIBS)
Libraries and object are processed in the order encountered on the linking command line: object files (or source files after transformation into object files) are always included. Their undefined symbols are added to the list of symbols to be resolved. Libraries are inspected to look for any undefined symbols the point they are encountered. Any object file from a library being investigated is included if it defines, at least, one symbol which is undefined so far. Once processed, the libraries are otherwise forgotten.
tl;dr: put the object file first, the libraries last.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With