用unitests构建我自己的库抱怨未定义的引用 [英] Building my own library with unitests complains about undefined reference
问题描述
我正在建立一个具有一些统一设施的简单图书馆.但是,make抱怨在建立unitest时存在未定义的引用.我已经发布了所有代码和makefile.这是什么原因呢?
I am building a simple library with some unitest facilities. However, the make complains that there is undefined reference while building the unitest. I have posted all the code and makefile. What is the reason for this?
文件层次结构:
/bin
/build
Makefile
/src
dbg.h
ex30.c
libex30.c
/tests
dbg.h
libex30_tests.c
minuint.h
runtests.sh
Makefile:
CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(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/libYOUR_LIBRARY.a
#SO_TARGET=$(patsubst %.a,%.so,$(TARGET))
# The Target Build
#all: $(TARGET) $(SO_TARGET) tests
all: $(TARGET) tests
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: tests
tests: CFLAGS += $(TARGET)
tests: $(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
在/src目录中,我有两个文件ex30.c,libex30.c和dbg.h
In /src dirctory, I have two files ex30.c, libex30.c, and dbg.h
ex30.c:
#include <stdio.h>
#include "dbg.h"
#include <dlfcn.h>
typedef int (*lib_function)(const char *data);
int main(int argc, char *argv[])
{
int rc = 0;
check(argc == 4, "USAGE: ex30 libex30.so function data");
char *lib_file = argv[1];
char *func_to_run = argv[2];
char *data = argv[3];
void *lib = dlopen(lib_file, RTLD_NOW);
check(lib != NULL, "Failed to open the library %s: %s", lib_file, dlerror());
lib_function func = dlsym(lib, func_to_run);
check(func != NULL, "Did not find %s function in the library %s: %s", func_to_run, lib_file, dlerror());
rc = func(data);
check(rc == 0, "Function %s return %d for data: %s", func_to_run, rc, data);
rc = dlclose(lib);
check(rc == 0, "Failed to close %s", lib_file);
return 0;
error:
return 1;
}
libex30.c:
libex30.c:
#include <stdio.h>
#include <ctype.h>
#include "dbg.h"
int print_a_message(const char *msg)
{
printf("A STRING: %s\n", msg);
return 0;
}
int uppercase(const char *msg)
{
int i = 0;
// BUG: \0 termination problems
for(i = 0; msg[i] != '\0'; i++) {
printf("%c", toupper(msg[i]));
}
printf("\n");
return 0;
}
int lowercase(const char *msg)
{
int i = 0;
// BUG: \0 termination problems
for(i = 0; msg[i] != '\0'; i++) {
printf("%c", tolower(msg[i]));
}
printf("\n");
return 0;
}
int fail_on_purpose(const char *msg)
{
return 1;
}
dbg.h:
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define debug(M, ...)
#else
#define debug(M, ...) fprintf(stderr, "DEBUG %s:%d:%s: " M "\n", __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#endif
#define clean_errno() (errno == 0 ? "None" : strerror(errno))
#define log_err(M, ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_warn(M, ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, clean_errno(), ##__VA_ARGS__)
#define log_info(M, ...) fprintf(stderr, "[INFO] (%s:%d) " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#define check(A, M, ...) if(!(A)) {log_err(M, ##__VA_ARGS__); errno=0; goto error;}
#define sentinel(M, ...) {log_err(M, ##__VA_ARGS__); errno=0; goto error;}
#define check_mem(A) check((A), "Out of memory.")
#define check_debug(A, M, ...) if(!(A)){debug(M, ##__VA_ARGS__); errno=0; goto error;}
#endif
在/tests中,我有libex30_tests.c,minunit.h和dbg.h:
In /tests, I have libex30_tests.c, minunit.h, and dbg.h:
libex30_tests.c:
libex30_tests.c:
#include "minunit.h"
char *test_dlopen()
{
return NULL;
}
char *test_functions()
{
return NULL;
}
char *test_failures()
{
return NULL;
}
char *test_dlclose()
{
return NULL;
}
char *all_tests() {
mu_suite_start();
mu_run_test(test_dlopen);
mu_run_test(test_functions);
mu_run_test(test_failures);
mu_run_test(test_dlclose);
return NULL;
}
RUN_TESTS(all_tests);
minunit.h
minunit.h
#undef NDEBUG
#ifndef _minunit_h
#define _minunit_h
#include <stdio.h>
#include "dbg.h"
#include <stdlib.h>
#define mu_suite_start() char *message = NULL
#define mu_assert(test, message) if (!(test)) { log_err(message); return message; }
#define mu_run_test(test) debug("\n-----%s", " " #test); \
message = test(); tests_run++; if (message) return message;
#define RUN_TESTS(name) int main(int argc, char *argv[]) {\
argc = 1; \
debug("----- RUNNING: %s", argv[0]);\
printf("----\nRUNNING: %s\n", argv[0]);\
char *result = name();\
if (result != 0) {\
printf("FAILED: %s\n", result);\
}\
else {\
printf("ALL TESTS PASSED\n");\
}\
printf("Tests run: %d\n", tests_run);\
exit(result != 0);\
}
int tests_run;
#endif
runtests.sh:
runtests.sh:
echo "Running unit tests:"
for i in tests/*_tests
do
if test -f $i
then
if $VALGRIND ./$i 2>> tests/tests.log
then
echo $i PASS
else
echo "ERROR in test $i: here's tests/tests.log"
echo "------"
tail tests/tests.log
exit 1
fi
fi
done
echo ""
这是我化妆后得到的:
cc -g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG build/libYOUR_LIBRARY.a tests/libex30_tests.c -o tests/libex30_tests
In file included from tests/libex30_tests.c:1:0:
tests/libex30_tests.c: In function ‘main’:
tests/minunit.h:15:38: warning: parameter ‘argc’ set but not used [-Wunused-but-set-parameter]
#define RUN_TESTS(name) int main(int argc, char *argv[]) {\
^
tests/libex30_tests.c:38:1: note: in expansion of macro ‘RUN_TESTS’
RUN_TESTS(all_tests);
^
/tmp/ccqde9jD.o: In function `main':
/home/rex/rex/projects/programming/c/learn_hard_way/ex30/tests/libex30_tests.c:38: multiple definition of `main'
build/libYOUR_LIBRARY.a(ex30.o):/home/rex/rex/projects/programming/c/learn_hard_way/ex30/src/ex30.c:9: first defined here
build/libYOUR_LIBRARY.a(ex30.o): In function `main':
ex30.c:(.text.startup+0x85): undefined reference to `dlopen'
ex30.c:(.text.startup+0x9c): undefined reference to `dlsym'
ex30.c:(.text.startup+0x120): undefined reference to `dlclose'
ex30.c:(.text.startup+0x188): undefined reference to `dlerror'
ex30.c:(.text.startup+0x1f0): undefined reference to `dlerror'
collect2: error: ld returned 1 exit status
make: *** [tests/libex30_tests] Error 1
推荐答案
这些符号由libdl.so
导出.与-ldl
链接.
Those symbols are exported by libdl.so
. Link with -ldl
.
这篇关于用unitests构建我自己的库抱怨未定义的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!