Makefile研究(三) —— 实际应用
转自:http://blog.csdn.net/jundic/article/details/17886637
前面讲了Makefile 的简单语法和简单的应用模板,但在实际项目应用中比这个肯定复杂很多,但是我想说他的Makefile应用模式都是大同小异,只是代码量和工程复杂度大写而已。
我觉得一个完整的工程目录应该这样:
1、工程文件结构目录应该要非常清晰,并且模块化,各个模块下的Makefile 独立
2、可以分别单独编译各个单独模块成.a 或.so
3、在顶层目录下可以一次性生成可执行文件。
现在我搭建了如下工程目录
build为编译产生的结果文件,out 为可执行bin文件,libs为生成的库
project 为工程文件夹,下面有module1 、module2 、module3三个模块,main.c 为主函数它将生成可执行文件,虚线框为编译时生成的文件夹或文件
module2 module3和module1的文件结构相同图中没有画出来。
现进行如下分工,module1 module2 将产生静态库到 build/libs/static 中,module3将产生动态库到 build/libs/dynamic 中,最后main.c 将调用这些库编译生成可执行文件到
build/out 中。
为了提高模块的复用性,我们会在顶层目录下,写一个rules.mk文件,事实上很多项目是这么做的比如uboot 。
rules.mk 代码
1 # rules .mk 2 3 # Generic Makefile for C/C++ Program 4 # Author: 5 # Description: # ------------ 6 # This is an easily customizable makefile template. The purpose is to 7 # provide an instant building environment for C/C++ programs. 8 # 9 # It searches all the C/C++ source files in the specified directories, 10 # makes dependencies, compiles and links to form an executable. 11 # 12 # Besides its default ability to build C/C++ programs which use only 13 # standard C/C++ libraries, you can customize the Makefile to build 14 # those using other libraries. Once done, without any changes you can 15 # then build programs using the same or less libraries, even if source 16 # files are renamed, added or removed. Therefore, it is particularly 17 # convenient to use it to build codes for experimental or study use. 18 # 19 # GNU make is expected to use the Makefile. Other versions of makes 20 # 21 22 .PHONY : all clean 23 24 # Top directory Makefile 25 26 # The C program compiler 27 CC = gcc 28 MACRO = DEBUGALL 29 CFLAGS += -g -werror -D$(MACRO) 30 AR = ar 31 ARFLAGES = crv 32 33 # default execute output directory 34 ifeq ($(DIR_EXES),) 35 DIR_EXES = $(TOPDIR)/build/out 36 endif 37 38 # defaulet libaray creat directory 39 ifeq ($(DIR_LIBS),) 40 DIR_LIBS = $(TOPDIR)/build/libs 41 endif 42 43 # directory 44 DIRS = $(DIR_OBJS) $(DIR_DEPS) $(DIR_EXES) $(DIR_LIBS) 45 46 # include directory 47 ifneq ($(DIR_INCS),"") 48 DIR_INCS := $(strip $(DIR_INCS)) 49 DIR_INCS := $(addprefix -I,$(DIR_INCS)) 50 endif 51 52 # when build execute file 53 ifneq ($(EXES),) 54 EXES := $(addprefix $(DIR_EXES)/,$(EXES)) 55 RMS += $(EXES) 56 DIR_LIBS := $(strip $(DIR_LIBS)) 57 DIR_LIBS := $(addprefix -L,$(DIR_LIBS)) 58 endif 59 60 # when build static libaray file 61 ifneq ($(LIBS),"") 62 LIBS := $(addprefix $(DIR_LIBS)/,$(LIBS)) 63 RMS += $(LIBS) 64 endif 65 66 # default source code file directory 67 ifeq ($(DIR_SRCS),) 68 DIR_SRCS = . 69 endif 70 SRCS = $(wildcard $(DIR_SRCS)/*.c) 71 OBJS = $(patsubst %.c, %.o,$(notdir $(SRCS))) 72 OBJS := $(addprefix $(DIR_OBJS)/,$(OBJS)) 73 RMS += $(OBJS) $(DIR_OBJS) 74 75 DEPS = $(patsubst %.c, %.dep,$(notdir $(SRCS))) 76 DEPS := $(addprefix $(DIR_DEPS)/,$(DEPS)) 77 RMS += $(DEPS) $(DIR_DEPS) 78 79 ifneq ($(EXES),"") 80 all : $(EXES) 81 endif 82 83 ifneq ($(LIBS),"") 84 all : $(LIBS) 85 endif 86 87 ifneq ($(LINK_LIBS),"") 88 LINK_LIBS := $(strip $(LINK_LIBS)) 89 LINK_LIBS := $(addprefix -l,$(LINK_LIBS)) 90 endif 91 92 # include dependent files 93 ifneq ($(MAKECMDGOALS), clean) 94 -include $(DEPS) 95 endif 96 97 $(DIRS): 98 mkdir -p $@ 99 100 # creat execute file 101 $(EXES) : $(DIR_OBJS) $(OBJS) $(DIR_EXES) 102 $(CC) $(DIR_INCS) $(CFLAGES) -o $@ $(OBJS) $(DIR_LIBS) $(LINK_LIBS) 103 104 # creat libaray file 105 $(LIBS) : $(DIR_LIBS) $(DIR_OBJS) $(OBJS) 106 # library type is static 107 ifeq ($(LIB_TYPE),static) 108 $(AR) $(ARFLAGS) $@ $(OBJS) 109 endif 110 111 # library type is dynamic 112 ifeq ($(LIB_TYPE),dynamic) 113 $(CC) -shared -o $@ $(OBJS) 114 endif 115 116 # creat object file 117 $(DIR_OBJS)/%.o : $(DIR_SRCS)/%.c 118 @echo "source files:" $< 119 @echo "object files:" $@ 120 ifeq ($(LIB_TYPE),static) 121 $(CC) $(DIR_INCS) $(CFLAGES) -o $@ -c $< 122 else 123 $(CC) $(DIR_INCS) $(CFLAGES) -fPIC -o $@ -c $< 124 endif 125 126 # creat depandant file 127 $(DIR_DEPS)/%.dep : $(DIR_SRCS)/%.c $(DIR_DEPS) 128 @echo "creating depend file ..." $@ 129 @set -e;\ 130 $(CC) $(DIR_INCS) -MM $< > $@.tmp;\ 131 sed 's,$∗\.o[ :]*,$(DIR_OBJS)/\1.o $@ : ,g' < $@.tmp > $@ 132 # rm $@.tmp 133 134 clean: 135 rm -rf $(RMS)
这个makefile 没有什么好分析的和第二篇文章单目录工程makefile一样的。
好了进入module1 下我们想把 module1编译成libmodule1.a 对应的Makefile 就很简单啦。
1 # module1 Makefile 2 DIR_CUR = $(shell pwd) 3 DIR_SRCS = $(DIR_CUR)/src 4 DIR_INCS += $(DIR_CUR)/inc 5 DIR_OBJS = objs 6 DIR_DEPS = deps 7 DIR_LIBS = ../../build/libs/static 8 # library type an name 9 LIB_TYPE = static 10 LIBS = libmodule1.a 11 # LIB_TYPE = dynamic 12 # LIBS = libmodule1.so 13 14 include ../../rules.mk
这个makefile就是要配置输出文件路径和名字然后将顶层rules.mk包含就ok啦。
同样的module2 目录下Makefile 也是一样的只要将LIBS 改为 libmodule2.a 即可。
module3 的我们想把他编译成动态库 则对应为如下
1 # module1 Makefile 2 DIR_CUR = $(shell pwd) 3 DIR_SRCS = $(DIR_CUR)/src 4 DIR_INCS += $(DIR_CUR)/inc 5 DIR_OBJS = objs 6 DIR_DEPS = deps 7 DIR_LIBS = ../../build/libs/static 8 # library type an name 9 #LIB_TYPE = static 10 #LIBS = libmodule3.a 11 LIB_TYPE = dynamic 12 LIBS = libmodule3.so 13 14 include ../../rules.mk
在project 中要编译main.c 又要链接前面生成的三个库 那对应的Makefile 如下:
1 # Current directory Makefile 2 CURDIR = $(shell pwd) 3 4 # executable file name 5 EXES = justest 6 7 # head file directories 8 DIR_INCS = module1/inc\ 9 module2/inc\ 10 module3/inc 11 12 # objs and dependant file directories 13 DIR_OBJS = objs 14 DIR_DEPS = deps 15 16 # static libraries 17 LINK_LIBS = module1 module2 module3 18 19 # library file directories 20 DIR_LIBS = $(TOPDIR)/build/libs/static\ 21 $(TOPDIR)/build/libs/dynamic 22 23 # include common rule makefile 24 include ../rules.mk
在这个makefile 只要配置生成的可执行文件的路径名,还有头文件目录,链接库的路径和名字就可以生成可执行文件了,这里也顶层的rules.mk。
当然在实际应用中可以到各个模块中去执行make 但当想一次性生成可执行文件,则还需要一个顶层Makefile 。
1 .PHONY: all clean 2 3 # sofeware version information 4 VERSION = 1 5 PROJECT = mk_demo 6 SUBLEVEL = 4 7 YEAL = 2013 8 RELEASE_VERSION = $(PROJECT).$(YEAL).$(VERSION).$(SUBLEVEL) 9 10 BUILD_DIRS = $(TOPDIR)/source/module1\ 11 $(TOPDIR)/source/module2\ 12 $(TOPDIR)/source/module3\ 13 $(TOPDIR)/source 14 15 all : 16 # @shell(export TOPDIR=`pwd`) 17 @echo "version information:" $(RELEASE_VERSION) 18 @echo "building ..." 19 @set -e;\ 20 for dir in $(BUILD_DIRS);\ 21 do\ 22 cd $$dir && $(MAKE);\ 23 done 24 @echo "" 25 @echo "\33[35m ~@^_^@~ \33[m" 26 @echo "" 27 @echo "Build Completed" 28 29 clean: 30 @echo "cleaning ...." 31 for dir in $(BUILD_DIRS);\ 32 do\ 33 cd $$dir && $(MAKE) clean;\ 34 done 35 rm -rf $(RMS) 36 @echo "" 37 @echo "Clean Completed" 38 39 version: 40 @echo $(TOPDIR) 41 @echo $(RELEASE_VERSION) 42 @echo $(BUILD_DIRS) 43 @echo $(RMS)
这里最关键的几句代码就是
for dir in $(BUILD_DIRS)
do \
cd $$dir && $(MAKE);\
done
在这里将进入各个模块目录下 执行make ,到这里一个稍微可以用工程makefile 工程实例,实现完了,其实只要模块划分好,还是很好理解的。我想说的时这种方法很常用,但不是唯一的,我有见过有些工程中也许并不需要每个模块都编译成库,或者说他要编译成库本身就是个很大的工程,一个模块的文件就特别多,目录结果就比较复杂这中该怎么写,先讲个思路,对应模块的Makefile 就搜索该目录下所以的Makefile 并包含进来。而子目录下的Makefile 将只将目录下需要编译的文件包含和路径包含经来就ok了,有时间也要总结下。
至此非常感谢我参考的《驾驭Makefile》,《Makefile编程》,《GCC 中文手册》