Reputation: 5156
I'm following the "Learn C the Hard Way" ebook(?), and reached exercise 32.
It uses a previously developed project structure, where tests link to the built library. However, when I run make test
, I get "undefined reference to X", where X is every function defined in my library's header.
twoll_tests.c
is my test file, libds
is the library. See project tree at the bottom of this question.
The compilation line for the tests is this:
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG -std=c11 build/libds.a tests/twoll_tests.c -o tests/twoll_tests
Makefile contents:
CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG -std=c11 $(OPTFLAGS) LIBS=-ldl $(OPTLIBS) PREFIX?=/usr/local SOURCES=$(wildcard src/**/*.c src/*.c) OBJECTS=$(patsubst %.c,%.o,$(SOURCES)) TEST_SRC=$(wildcard tests/*_tests.c) TESTS=$(patsubst %.c,%,$(TEST_SRC)) TARGET=build/libds.a SO_TARGET=$(patsubst %.a,%.so,$(TARGET)) # The Target Build all: $(TARGET) $(SO_TARGET) dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS) dev: all $(TARGET): CFLAGS += -fPIC $(TARGET): build $(OBJECTS) ar rcs $@ $(OBJECTS) ranlib $@ $(SO_TARGET): $(TARGET) $(OBJECTS) $(CC) -shared -o $@ $(OBJECTS) build: @mkdir -p build @mkdir -p bin # The Unit Tests .PHONY: test test: CFLAGS += $(TARGET) test: $(TESTS) sh ./tests/runtests.sh valgrind: VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE) # The Cleaner clean: rm -rf build $(OBJECTS) $(TESTS) rm -f tests/tests.log find . -name "*.gc*" -exec rm {} \; rm -rf `find . -name "*.dSYM" -print` # The Install install: all install -d $(DESTDIR)/$(PREFIX)/lib/ install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/ # The Checker BADFUNCS='[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)' check: @echo Files with potentially dangerous functions. @egrep $(BADFUNCS) $(SOURCES) || true
And finally, the project tree:
. ├── bin ├── build │ ├── libds.a │ └── libds.so ├── LICENSE ├── Makefile ├── README.md ├── src │ └── libds │ ├── twoll.c │ ├── twoll.h │ └── twoll.o └── tests ├── runtests.sh ├── tester.h ├── tests.log └── twoll_tests.c
EDIT: Here's the full output of make test
:
kroltan@kroltan ~/Projects/learncthehardway/ex32 $ make all cc -g -O2 -Wall -Wextra -Isrc -rdynamic -std=c11 -fPIC -c -o src/libds/twoll.o src/libds/twoll.c ar rcs build/libds.a src/libds/twoll.o ranlib build/libds.a cc -shared -o build/libds.so src/libds/twoll.o kroltan@kroltan ~/Projects/learncthehardway/ex32 $ make test cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG -std=c11 build/libds.a tests/twoll_tests.c -o tests/twoll_tests /tmp/ccnOW9OX.o: In function `twoll_new_test': /home/kroltan/Projects/learncthehardway/ex32/tests/twoll_tests.c:8: undefined reference to `twoll_new' /tmp/ccnOW9OX.o: In function `twoll_del_test': /home/kroltan/Projects/learncthehardway/ex32/tests/twoll_tests.c:13: undefined reference to `twoll_new' /home/kroltan/Projects/learncthehardway/ex32/tests/twoll_tests.c:13: undefined reference to `twoll_del' /tmp/ccnOW9OX.o: In function `twoll_push_pop_test': /home/kroltan/Projects/learncthehardway/ex32/tests/twoll_tests.c:18: undefined reference to `twoll_new' /home/kroltan/Projects/learncthehardway/ex32/tests/twoll_tests.c:18: undefined reference to `twoll_push' /home/kroltan/Projects/learncthehardway/ex32/tests/twoll_tests.c:18: undefined reference to `twoll_push' /home/kroltan/Projects/learncthehardway/ex32/tests/twoll_tests.c:18: undefined reference to `twoll_push' /home/kroltan/Projects/learncthehardway/ex32/tests/twoll_tests.c:18: undefined reference to `twoll_pop' /home/kroltan/Projects/learncthehardway/ex32/tests/twoll_tests.c:18: undefined reference to `twoll_pop' /home/kroltan/Projects/learncthehardway/ex32/tests/twoll_tests.c:18: undefined reference to `twoll_pop' collect2: error: ld returned 1 exit status make: *** [tests/twoll_tests] Error 1
Upvotes: 3
Views: 1220
Reputation: 5156
With the help of @unwind and @Umamahesh P, it was found that the library must be added after the target when invoking the compiler. After some extra searching around the internet, I've found that the correct Make variable for libraries is actually LDLIBS
, not LIBS
as was given on the book. I've changed the variable usage, and added the following assignment to my test
target:
test: LDLIBS += $(TARGET)
With just that, linking is successful.
Upvotes: 0
Reputation: 399703
This part of the compiler invocation:
build/libds.a tests/twoll_tests.c
is bad; you always want the libraries after the source files. Think of it like this:
You can't resolve something before it's been created, so the libraries should go last.
Upvotes: 3