随笔-156  评论-223  文章-30  trackbacks-0
原理
   在linux平台下编译由多个源码文件或目录组成的项目工程时,需要编写make脚本即Makefile文件来编译,当项目工程宠大时,这种方式比单纯地使用gcc命令行方便快捷,且易于维护。由于具体工程的源码文件数量的多少及名称的不同,因此编写一个较为通用的Makefile文件,来实现编译各种不同的工程,具有重要的参考意义和价值。本文展示了通用Makefile.in文件及其应用示例。Makefile.in文件,顾名思义,内部实现用的,应由外部具体的Makefile文件提供具体的命令行参数来调用,它包括exe,static,share三个规则目标,因此支持可执行文件、动态库和静态库三种工程的编译,而每种工程又支持debug和release两种版本,默认为release版本,在编译时会自动创建debug或release目录来存放所有中间文件*.o和*.d。在其脚本源码中,详见下面实现,小写变量为内部所有,大写变量为make命令行提供的参数,目前支持以下几种命令行参数:
     1)输出名称:OUT_NAME,对于库工程,内部自动添加lib前缀
     2)输出路径:OUT_PATH,
末尾反斜杠/可有可无
     3)源码路径:SRC_PATH, 末尾反斜杠/可有可无
     4)依赖动态库路径:SHARE_PATH,不带库名称的路径, 末尾反斜杠/可有可无 
     5)依赖动态库名称:SHARE_LIB,不带库路径的名称,内部自动添加-l前缀
     6)依赖静态库路径:STATIC_PATH,不带库名称的路径,
尾反斜杠/可有可无
     7)依赖静态库路径:STATIC_LIB,不带库路径的名称
     8)预定义宏:MACROS,内部自动添加-D前缀
     9)编译模式:MODE,表示编译成debug或release版本
     关于头文件包含的支持,这里没有提供命令行参数,在内部它固定为SRC_PATH、/usr/include和/usr/local/include三个路径,对于大多数的工程,应该够用了。

实现
 1#Makefile.in
 2
 3inc_path := $(SRC_PATH) /usr/include /usr/local/include
 4inc_path := $(addprefix -I,$(inc_path))
 5override SHARE_PATH += /usr/lib /usr/local/lib
 6override SHARE_PATH  := $(addprefix -L,$(SHARE_PATH))
 7override SHARE_LIB  := $(if $(SHARE_LIB),$(addprefix -l,$(SHARE_LIB)))
 8override STATIC_PATH := $(patsubst %/,%,$(STATIC_PATH)) 
 9override STATIC_LIB := $(if $(STATIC_PATH),$(if $(STATIC_LIB),$(addprefix $(STATIC_PATH)/,$(STATIC_LIB))))
10override SRC_PATH := $(patsubst %/,%,$(SRC_PATH))
11override MACROS := $(addprefix -D,$(MACROS))
12
13cxxflags := -Wall $(MACROS)
14
15ifeq ($(MODE),debug)
16  cxxflags += -
17  tmp_path := $(SRC_PATH)/debug
18else
19  cxxflags += -O2 -DNDEBUG 
20  tmp_path := $(SRC_PATH)/release
21endif
22
23lib_name := $(addprefix lib,$(OUT_NAME))
24
25srcs := $(wildcard $(SRC_PATH)/*.c) $(wildcard $(SRC_PATH)/*.cpp)
26deps := $(patsubst %.c,%.d,$(patsubst %.cpp,%.d,$(srcs)))
27deps := $(foreach dep,$(deps),$(notdir $(dep)))
28deps := $(addprefix $(tmp_path)/,$(deps))
29
30objs := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(srcs)))
31objs := $(foreach obj,$(objs),$(notdir $(obj)))
32objs := $(addprefix $(tmp_path)/,$(objs))
33
34share_name  := $(tmp_path)/$(lib_name).so
35static_name := $(tmp_path)/$(lib_name).a
36exe_name    := $(tmp_path)/$(OUT_NAME)
37    
38override MACROS := $(if $(MACROS),$(addprefix -D,$(MACROS)))
39
40.PHONY: exe lib static share clean config
41
42arflags    := -rc
43
44definMKDIR
45if [ ! -d $(tmp_path) ]; then \
46mkdir $(tmp_path);\
47fi 
48endef
49
50config:
51    @$(MKDIR)
52
53exe: config $(exe_name)
54
55lib: config static share
56
57static: $(static_name)
58    
59share: $(share_name)
60
61$(exe_name): $(objs) 
62    @echo "Linking to execute ($@ : $(objs))."
63    $(CXX) -o $@ $(objs) $(SHARE_PATH) $(SHARE_LIB) $(STATIC_LIB)
64    @cp $(exe_name) $(OUT_PATH) 
65        
66$(static_name): $(objs)
67    @echo "Archive to static library ($@ [$(objs)])."
68    $(AR) $(arflags) $@ $(objs)
69    @cp $(static_name) $(OUT_PATH) 
70    
71$(share_name): $(objs)
72    @echo "Linking to shared library ($@ [$(objs)])."
73    $(CXX) $(cxxflags) -o $@ $(objs) -fPIC -shared
74    @cp $(share_name) $(OUT_PATH) 
75    
76$(tmp_path)/%.o: $(SRC_PATH)/%.cpp $(tmp_path)/%.d
77    @echo "Compile $@ ($<)."
78    $(CXX) $(cxxflags) $(inc_path) -c $< -o $@
79
80$(tmp_path)/%.d: $(SRC_PATH)/%.cpp 
81    @echo "Compile $@ ($<)."
82    $(CXX) $(cxxflags) -MM $< -o $@.$$$$; \
83    sed 's,\($*\)\.o[ :]*,\1.o $@:, g' < $@.$$$$ > $@; \
84    rm -f $@.$$$$
85
86-include $(deps)
87
88clean:
89    $(RM) $(objs) $(deps) $(share_name) $(static_name) $(exe_name)

应用
   这里假设有两个源码子目录netcomm和server,前者为动态库netcomm工程,后者为主程序server工程,它依赖netcomm库,每个目录下都有其自己的Makefile,这个用于编译单个模块或主程序,它们的父目录为src,在这个目录下有两个Makefile文件,一个是Makefile.in,这个就是上面讲到的通用内部Makefile;另一个是Makefile,这个用来联编所有的模块和主程序。
   先来看下netcomm的Makefile文件内容,如下所示
 1path := SRC_PATH=. OUT_PATH=../../output
 2
 3.PHONY: all debug release clean
 4
 5all: debug release 
 6    
 7debug:
 8    $(MAKE) -../Makefile.in lib MODE=debug OUT_NAME=netcommd $(path)
 9
10release:
11    $(MAKE) -../Makefile.in lib MODE=release OUT_NAME=netcomm $(path)
12
13clean:
14    $(MAKE) -../Makefile.in clean MODE=debug OUT_NAME=netcommd $(path)
15    $(MAKE) -../Makefile.in clean MODE=release OUT_NAME=netcomm $(path)
     
   再看下server的Makefile文件内容,如下所示   
 1macros := MACROS="_USE_MEM_POOL=1"
 2
 3path := SRC_PATH=. OUT_PATH=../../output SHARE_PATH=../../output
 4
 5.PHONY: all debug release clean 
 6
 7all: debug release 
 8
 9debug:
10    $(MAKE) -../Makefile.in exe MODE=debug OUT_NAME=serverd SHARE_LIB="netcommd" $(macros) $(path)
11
12release:
13    $(MAKE) -../Makefile.in exe MODE=release OUT_NAME=server SHARE_LIB="netcomm" $(macros) $(path)
14
15clean:
16    $(MAKE) -../Makefile.in clean MODE=debug OUT_NAME=serverd $(path)
17    $(MAKE) -../Makefile.in clean MODE=release OUT_NAME=server $(path)
    
   最后看下src的Makefile文件内容,如下所示
 1.PHONY: all release debug clean
 2
 3all: debug release
 4
 5debug:
 6    $(MAKE) debug -C netcomm 
 7    $(MAKE) debug -C server
 8
 9release:
10    $(MAKE) release -C netcomm
11    $(MAKE) release -C server
12
13clean:
14    $(MAKE) clean -C netcomm
15    $(MAKE) clean -C server
   以上所有脚本代码,在make 3.81下测试通过。
posted on 2012-08-16 19:29 春秋十二月 阅读(3480) 评论(3)  编辑 收藏 引用 所属分类: System

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理