使用unitests构建我自己的库会抱怨未定义的引用

我正在建造一个简单的图书馆,里面有一些最简单的设施。 然而,make抱怨在构建unitest时存在未定义的引用。 我已经发布了所有代码和makefile。 这是什么原因?

文件层次:

/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

ex30.c:

 #include  #include "dbg.h" #include  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:

 #include  #include  #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  #include  #include  #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:

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

 #undef NDEBUG #ifndef _minunit_h #define _minunit_h #include  #include "dbg.h" #include  #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:

 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链接。

您应该在Makefile sg中添加一条规则,如下所示:

 %: %.c ${OTHER_OBJECTS_AND_ARCHIVES} ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS} -o $@ $^ ${LIBS} 

Makefile有几个问题。 而且,我不得不猜测一下正确的文件层次结构。 而且,你的list.c不完整


Makefile的一个大错误是你用来构建测试的这一行:

 tests: CFLAGS += $(TARGET) 

$(TARGET)是你的库.a文件。 通过这种方式指定命令,无论何时构建测试,您都会获得一个命令(例如):

 cc -o mytest1 -O2 -g lib.a mytest1.c 

问题是lib.a在编译mytest1.c 之前会被扫描依赖mytest1.c ,因此链接器知道mytest1.clib.a想要的符号,因此没有从库中提取任何内容。

正确的命令是:

 cc -o mytest1 -O2 -g mytest1.c lib.a 

要在Makefile正确指定,请参阅下面的更改。


我修复了你的Makefile并注释了上面的bug,以及其他几个:

 # NOTE/BUG: there are issues with _not_ using full path. they _can_ be solved # without doing this, but this makes things easier SRC := $(shell pwd) # NOTE: to just _build_ the tests but _not_ try to run them, specify: # RUNTESTS=tests # on the command line RUNTESTS ?= runtests # NOTE: cosmetic change to library name ###LIBNAME = YOUR_LIBRARY LIBNAME = lcthw CFLAGS += -g CFLAGS += -O2 CFLAGS += -Wall # NOTE: IMO, -Wextra is overkill and causes more problems than it's worth # what I do is something like this and then do: # make CMDLINE_CFLAGS=-Wextra # on those rare occasions where it might be useful ###CFLAGS += -Wextra CFLAGS += $(CMDLINE_CFLAGS) CFLAGS += -I$(SRC) -rdynamic -DNDEBUG $(OPTFLAGS) LIBS=-ldl $(OPTLIBS) PREFIX?=/usr/local # library sources and objects # NOTE/BUG: the wildcard was failing ###SOURCES=$(wildcard src/**/*.c src/*.c) SOURCES=$(wildcard lcthw/*.c) OBJECTS=$(patsubst %.c,%.o,$(SOURCES)) # test sources and objects TEST_SRC=$(wildcard tests/*_tests.c) TEST_OBJS=$(patsubst %.c,%.o,$(TEST_SRC)) TESTS=$(patsubst %.o,%,$(TEST_OBJS)) # library target TARGET=build/lib$(LIBNAME).a SO_TARGET=$(patsubst %.a,%.so,$(TARGET)) TARGETS += $(TARGET) # NOTE: comment the following out to _not_ build the shared library -- it's # _not_ used but will be built now (to use it, change TARGET to SO_TARGET when # building TESTS) TARGETS += $(SO_TARGET) # The Target Build all: $(TARGETS) $(RUNTESTS) ###dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS) dev: CFLAGS=-g -Wall -I$(SRC) -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 # NOTE/BUG: in order to link properly $(TARGET) must be the last part of the # command # # (ie) doing CFLAGS += $(TARGET) produced the equivalent of: # $(CC) -o $@ $(CFLAGS) $(TARGET) $@.c # # instead of: # $(CC) -o $@ $(CFLAGS) $@.c $(TARGET) # # the reason the first method failed was because the library was scanned # _before_ the .c was compiled, so it didn't know to pull any .o files from # it # # the second example _will_ work and is closer to what you originally specified, # but my personal preference is to always build the .o files: $(TESTS): $(CC) -c $(CFLAGS) -o $@.o $@.c $(CC) -o $@ $(CFLAGS) $@.o $(TARGET) # just build the tests .PHONY: tests tests: $(TESTS) # build and run the tests .PHONY: runtests runtests: $(TESTS) sh ./tests/runtests.sh valgrind: VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE) # The Cleaner clean: rm -f *.o rm -rf bin build $(OBJECTS) $(TESTS) rm -f $(TEST_OBJS) 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 

提醒:由于代码块在SO上的发布方式,makefile中的选项卡将转换为空格。 因此,当您拉动makefile时,您需要将一行上的前导空格转换为单个选项卡。 否则,您将看到(例如)可怕的Makefile:59: *** missing separator. Stop. Makefile:59: *** missing separator. Stop.


这是我最终得到的文件层次结构:

 elixir/ex30.c elixir/lcthw/list.c elixir/lcthw/list.h elixir/libex30.c elixir/Makefile elixir/tests/dbg.h elixir/tests/libex30_tests.c elixir/tests/list_tests.c elixir/tests/minunit.h elixir/tests/runtests.sh 

makefile可以正常工作。 请注意, elixir可以是(例如)/ /home/elixir/projects/.../mylib因为makefile现在可以执行pwd

但是,如果[无意中]把东西放在错误的地方,你当然可以移动它们,但你可能需要稍微调整Makefile


此外,即使发布了list.c ,它也是不完整的,只定义了list.h大约一半的函数。 这个bug被主要的makefile bug掩盖了。 所以,我添加了虚拟版本以获得干净的构建:

 #include  //#include  List *List_create() { return calloc(1, sizeof(List)); } void List_destroy(List *list) { LIST_FOREACH(list, first, next, cur) { if(cur->prev) { free(cur->prev); } } free(list->last); free(list); } void List_clear(List *list) { LIST_FOREACH(list, first, next, cur) { free(cur->value); } } void List_clear_destroy(List *list) { List_clear(list); List_destroy(list); } void List_push(List *list, void *value) { } void * List_pop(List *list) { return NULL; } void List_unshift(List *list, void *value) { } void * List_shift(List *list) { return NULL; } void * List_remove(List *list, ListNode *node) { return NULL; } 

修复后我得到的输出看起来像这样:

 cc -g -O2 -Wall -I/home/myhome/elixir -rdynamic -DNDEBUG -fPIC -c -o lcthw/list.o lcthw/list.c ar rcs build/liblcthw.a lcthw/list.o ranlib build/liblcthw.a cc -shared -o build/liblcthw.so lcthw/list.o cc -c -g -O2 -Wall -I/home/myhome/elixir -rdynamic -DNDEBUG -o tests/list_tests.o tests/list_tests.c cc -o tests/list_tests -g -O2 -Wall -I/home/myhome/elixir -rdynamic -DNDEBUG tests/list_tests.o build/liblcthw.a cc -c -g -O2 -Wall -I/home/myhome/elixir -rdynamic -DNDEBUG -o tests/libex30_tests.o tests/libex30_tests.c cc -o tests/libex30_tests -g -O2 -Wall -I/home/myhome/elixir -rdynamic -DNDEBUG tests/libex30_tests.o build/liblcthw.a sh ./tests/runtests.sh Running unit tests: ---- RUNNING: ./tests/libex30_tests ALL TESTS PASSED Tests run: 4 tests/libex30_tests PASS ---- RUNNING: ./tests/list_tests FAILED: Wrong last value. Tests run: 2 ERROR in test tests/list_tests: here's tests/tests.log ------ DEBUG tests/libex30_tests.c:32:all_tests: ----- test_failures DEBUG tests/libex30_tests.c:33:all_tests: ----- test_dlclose DEBUG tests/list_tests.c:113:main: ----- RUNNING: ./tests/list_tests DEBUG tests/list_tests.c:103:all_tests: ----- test_create DEBUG tests/list_tests.c:104:all_tests: ----- test_push_pop [ERROR] (tests/list_tests.c:32: errno: None) Wrong last value. Makefile:96: recipe for target 'runtests' failed make: *** [runtests] Error 1 

更新:

我为这个错误道歉。 我意识到我混合了这两个项目……第三次更新后…我已经纠正了错误并且我已经给出了文件层次结构。 你能看看更新后的post吗? 它有较少的文件,并没有真正使用链表代码。

注意输出。 它确实成功运行了非列表测试。 如果你在list.c填写虚函数,列表测试也会起作用。

实际上,使用ex30代替ex30项目的src ex30

但是,我怀疑你真正想要的东西[最终]是这样的:

 elixir/common/dbg.h elixir/common/rules.mk elixir/ex30/ex30.c elixir/ex30/libex30.c elixir/ex30/Makefile elixir/lcthw/list.c elixir/lcthw/list.h elixir/lcthw/Makefile elixir/tests/libex30_tests.c elixir/tests/list_tests.c elixir/tests/Makefile elixir/tests/minunit.h elixir/tests/runtests.sh 

请注意, Makefile的大部分将被移动到rules.mk ,每个Makefile只会是几行。 并且,您可以添加具有类似子层次结构的更多项目。

IMO,一个可能更清晰的变种是:

 elixir/common/dbg.h elixir/common/rules.mk elixir/ex30/ex30.c elixir/ex30/libex30.c elixir/ex30/libex30_tests.c elixir/ex30/Makefile elixir/lcthw/list.c elixir/lcthw/list.h elixir/lcthw/list_tests.c elixir/lcthw/Makefile elixir/tests/Makefile elixir/tests/minunit.h elixir/tests/runtests.sh 

可以为每个项目添加src子目录。

随着build 。 但是,我建议build是一个顶级目录,它有每个项目的子目录:

 elixir/build/ex30/libex30_tests elixir/build/ex30/libex30_tests.o elixir/build/lcthw/lcthw.a elixir/build/lcthw/list.o elixir/build/lcthw/list_tests.o elixir/build/lcthw/list_tests 

这样做的优点是源树层次结构不会混杂.o.a和可执行文件(即)重建的东西。 这使得git设置更容易,因为您不需要每个项目的.gitignore文件。

YMMV,但是,我已经在每个可以想象的组织中为我自己的东西做了这个,这是我经过多次试验和错误后我已经确定的那个。

如果可以,我可以相应调整并重新发布。


为了给你一个更强大的rules.mk文件的更具体的例子,这里有一个我很多用于SO问题的文件。

注意:这只是一个例子。 它不能单独使用,因为它依赖于几个包装脚本来操作。 不要试图使用或改编它。 最好的方法是从头开始构建自己的rules.mk ,使用原始的Makefile作为起点。

 # rules/rules.mk -- ovrstk rules control # # options: # GDB -- enable debug symbols # 0 -- normal # 1 -- use -O0 and define _USE_GDB_=1 # # CLANG -- use clang instead of gcc # 0 -- use gcc # 1 -- use clang # # CVERBOSE -- build verbosity # 0 -- normal # 1 -- add -v to cc and -Wl,--verbose to ld # # MKVERBOSE -- makefile/rules verbosity # 0 -- normal # 1 -- verbose # # M32 -- cross-build to 32 bit mode # 0 -- native build # 1 -- build for i386 # # BNC -- enable benchmarks # 0 -- normal mode # 1 -- enable benchmarks for function enter/exit pairs # 2 -- add min/max # # XCFLAGS -- extra command line CFLAGS # XDFLAGS -- extra command line DFLAGS # # DOT_I -- generate preprocessor output # DOT_S -- generate assembler output # # GLIB -- added options for glib # # symbols: # DFLAGS -- -D options # CFLAGS -- compiler options # # PREP -- targets to execute before ALL # ALL -- things to build (automatically uses LIBNAME/PGMTGT) # # LIBNAME -- library name to build (eg foo.a) -- can be multiple # OLIB -- list of .o files to build LIBNAME # OLIB- -- list of .o files to build  # # PGMTGT -- program to build (eg fludger) -- can be multiple # OLIST -- list of .o files to build PGMTGT # OLIST- -- list of .o files to build  # # CLEAN -- things to clean (automatically uses a bunch of stuff) # # NOPROTO -- do not generate prototypes # FINLINE -- allow gcc to inline functions automatically # WNOERROR -- inhibit -Werror # WEXTRA -- add -Wextra ifdef OVRPUB ifndef SDIR SDIR := $(shell pwd) STAIL := $(notdir $(SDIR)) endif ifndef GENTOP GENTOP := $(dir $(SDIR)) endif ifndef GENDIR GENDIR := $(GENTOP)/$(STAIL) endif ifndef ODIR ODIR := $(GENDIR) endif NOPROTO := 1 endif # disable prototype generation ifdef NOPROTO PROTOLST := true PROTOGEN := @true else PROTOLST := qproto PROTOGEN := @qproto PROTOALL := proto endif ifndef SDIR $(error rules: SDIR not defined) endif ifndef ODIR $(error rules: ODIR not defined) endif ifndef GENDIR $(error rules: GENDIR not defined) endif ifndef GENTOP $(error rules: GENTOP not defined) endif ifndef _rules_mk_ _rules_mk_ = 1 CLEAN += $(LIBNAME) $(PGMTGT) ifndef NOPROTO CLEAN += *.proto endif CLEAN += *.a CLEAN += *.o CLEAN += *.i CLEAN += *.dis CLEAN += *.lst CLEAN += *.TMP QPROTO := $(shell $(PROTOLST) -i -l -O$(GENTOP) $(SDIR)/*.c $(CPROTO)) HDEP += $(QPROTO) ###VPATH += $(GENDIR) ###VPATH += $(SDIR) ifdef INCLUDE_MK -include $(INCLUDE_MK) endif ifdef M32 CFLAGS += -m32 endif ifdef CVERBOSE CFLAGS += -v endif ifdef MKVERBOSE MSG := @echo else MSG := @true endif ifdef GSYM CFLAGS += -gdwarf-2 endif ifdef GDB CFLAGS += -gdwarf-2 DFLAGS += -D_USE_GDB_ else CFLAGS += -O2 endif ifdef DEBUG DFLAGS += -DDEBUG=$(DEBUG) endif ifndef ZPRT DFLAGS += -D_USE_ZPRT_=0 endif ifdef BNC DFLAGS += -D_USE_BNC_=$(BNC) endif ifdef GLIB _GLIB = glib-2.0 DFLAGS += $(shell pkg-config --cflags $(_GLIB)) STDLIB += $(shell pkg-config --libs $(_GLIB)) endif ifdef CLANG CC := clang CXX := clang++ else ifdef CPLUS ifeq ($(STDLIB),) STDLIB += -lstdc++ endif endif endif # alternate -std # NOTE: clang++ does not understand c++17 ifdef CSTD _CSTD := $(CSTD) ifdef CLANG ifdef CPLUS ifeq ($(CSTD),c++17) _CSTD := c++1y endif endif endif CFLAGS += -std=$(_CSTD) endif ifdef MPI export PATH := /usr/lib64/openmpi/bin:$(PATH) CC := mpicc CXX := mpicxx endif DFLAGS += -I$(GENTOP) DFLAGS += -I$(OVRTOP) DFLAGS += -I$(OVRBNC) CFLAGS += -Wall ifndef WNOERROR CFLAGS += -Werror endif ifdef WEXTRA CFLAGS += -Wextra endif CFLAGS += -Wno-unknown-pragmas CFLAGS += -Wempty-body CFLAGS += -fno-diagnostics-color ifdef DOT_I NOLDC := @true COPTS += -E -P O := i endif ifdef DOT_S NOLDC := @true COPTS += -S SOPTS += -E -P O := s endif ifndef COPTS COPTS += -c O := o endif ifeq ($(O),o) ALL += $(LIBNAME) $(PGMTGT) else ALL += $(addsuffix .$(O),$(basename $(PGMTGT) $(OLIST))) endif ifndef FINLINE # NOTE: we now need this to prevent inlining (enabled at -O2) ifndef CLANG CFLAGS += -fno-inline-small-functions endif # NOTE: we now need this to prevent inlining (enabled at -O3) CFLAGS += -fno-inline-functions endif ifdef FIXREG ifndef CLANG CFLAGS += $(FIXREG) endif endif ifndef F95 F95 := f95 endif ifndef LDC ifdef FTN LDC = $(F95) endif endif ifndef LDC ifdef CPLUS LDC = $(CXX) else LDC = $(CC) endif endif # FIXME/CAE -- gold wiki page says use -Wl but it seems to have no effect ifndef CLANG ###LDC += -Wl,-fuse-ld=ld.blah endif ifdef CVERBOSE LDC += -Wl,-verbose=2 endif ifdef LIBLIST LIBLIST := $(addprefix $(GENTOP)/,$(LIBLIST)) endif CFLAGS += $(XCFLAGS) DFLAGS += $(XDFLAGS) CFLAGS += $(DFLAGS) endif all: $(PREP) $(PROTOALL) $(ALL) # C %.o: %.c $(HDEP) $(MSG) %.o %.c $(CC) $(CFLAGS) $(COPTS) -o $*.$(O) $< %.i: %.c $(MSG) %.i %.c cpp $(DFLAGS) -P $< > $*.i %.s: %.c $(MSG) %.s %.c $(CC) $(CFLAGS) -S -o $*.s $< # C++ %.o: %.cpp $(HDEP) $(MSG) %.o %.cpp $(CXX) $(CFLAGS) $(COPTS) -o $*.$(O) $< %.i: %.cpp $(MSG) %.i %.cpp cpp $(DFLAGS) -P $< > $*.i %.s: %.cpp $(MSG) %.s %.cpp $(CXX) $(CFLAGS) -S -o $*.s $< # asm %.o: %.s $(HDEP) $(MSG) %.o %.s $(AS) $(AFLAGS) -o $*.$(O) $< %.o: %.S $(HDEP) $(MSG) %.o %.S $(CC) $(CFLAGS) $(COPTS) $(SOPTS) -o $*.$(O) $< # fortran %.o: %.f95 $(HDEP) $(MSG) %.o %.f95 $(F95) -c -o $*.$(O) $< .SECONDEXPANSION: ###.SUFFIXES: # build a library (type (2) build) $(LIBNAME):: $(OLIB) $$(OLIB-$$@) $(NOLDC) ar rv $@ $^ # build programs $(PGMTGT):: $$@.o $(OLIST) $$(OLIST-$$@) $(LIBLIST) $(NOLDC) $(LDC) $(CFLAGS) -o $@ $^ $(STDLIB) .PHONY: proto proto:: $(PROTOGEN) -i -v -O$(GENTOP) $(SDIR)/*.c $(CPROTO) .PHONY: clean clean:: rm -f $(CLEAN) .PHONY: help help:: egrep '^#' $(SDIR)/Makefile 

这是一个使用它的每个项目的Makefile

 # fastread/Makefile -- make file for fastread # # SO: read line by line in the most efficient way platform specific # SO: questions/33616284 ifndef _fastread_mk_ _fastread_mk_ = 1 LIBNAME = fastread OLIB += rdgets.o OLIB += rdmmap.o OLIB += lib.o OLIB += node1.o OLIB += node2.o PGMTGT = xrdfile OLIST += rdgets.o OLIST += rdmmap.o OLIST += lib.o OLIST += node1.o OLIST += node2.o ###LIBLIST = fastread/fastread.a HDEP += fastread.h DFLAGS += -I$(SDIR) endif include $(OVRTOP)/rules/rules.mk