通用Makefile原理及应用

http://www.cppblog.com/qinqing1984/archive/2012/08/16/187398.html

原理
      在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
 3 inc_path := $(SRC_PATH) /usr/include /usr/local/include
 4 inc_path := $( addprefix -I,$(inc_path))
 5 override SHARE_PATH += /usr/lib /usr/local/lib
 6 override SHARE_PATH  := $( addprefix -L,$(SHARE_PATH))
 7 override SHARE_LIB  := $( if $(SHARE_LIB),$( addprefix -l,$(SHARE_LIB)))
 8 override STATIC_PATH := $( patsubst %/,%,$(STATIC_PATH)) 
 9 override STATIC_LIB := $( if  $(STATIC_PATH),$( if  $(STATIC_LIB),$( addprefix $(STATIC_PATH)/,$(STATIC_LIB))))
10 override SRC_PATH := $( patsubst %/,%,$(SRC_PATH))
11 override MACROS := $( addprefix -D,$(MACROS))
12
13 cxxflags := -Wall $(MACROS)
14
15 ifeq ($(MODE),debug)
16   cxxflags += -g 
17   tmp_path := $(SRC_PATH)/debug
18 else
19   cxxflags += -O2 -DNDEBUG 
20   tmp_path := $(SRC_PATH)/release
21 endif
22
23 lib_name := $( addprefix lib,$(OUT_NAME))
24
25 srcs := $( wildcard $(SRC_PATH)/*.c) $( wildcard $(SRC_PATH)/*.cpp)
26 deps := $( patsubst %.c,%.d,$( patsubst %.cpp,%.d,$(srcs)))
27 deps := $( foreach  dep,$(deps),$( notdir $(dep)))
28 deps := $( addprefix $(tmp_path)/,$(deps))
29
30 objs := $( patsubst %.c,%.o,$( patsubst %.cpp,%.o,$(srcs)))
31 objs := $( foreach  obj,$(objs),$( notdir $(obj)))
32 objs := $( addprefix $(tmp_path)/,$(objs))
33
34 share_name  := $(tmp_path)/$(lib_name).so
35 static_name := $(tmp_path)/$(lib_name).a
36 exe_name    := $(tmp_path)/$(OUT_NAME)
37     
38 override MACROS := $( if  $(MACROS),$( addprefix -D,$(MACROS)))
39
40 . PHONYexe lib static share c lean c onfig
41
42 arflags    := -rc
43
44 defin MKDIR
45 if [ ! -d $(tmp_path) ];  then \
46 mkdir  $(tmp_path);\
47 fi 
48 endef
49
50 config:
51     @$( MKDIR)
52
53 execonfig $(exe_name)
54
55 libconfig  static  share
56
57 static: $(static_name)
58     
59 share: $(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
88 clean:
89     $(RM) $(objs) $(deps) $(share_name) $(static_name) $(exe_name)

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

你可能感兴趣的:(server,exe,Path,makefile,wildcard,macros)