在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
.
PHONY:
exe lib static share c
lean c
onfig
41
42
arflags := -rc
43
44
defin
e MKDIR
45
if [ ! -d $(tmp_path) ];
then \
46
mkdir
$(tmp_path);\
47
fi
48
endef
49
50
config:
51
@$(
MKDIR)
52
53
exe:
config $(exe_name)
54
55
lib:
config
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)

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

应用
这里假设有两个源码子目录netcomm和server,前者为动态库netcomm工程,后者为主程序server工程,它依赖netcomm库,每个目录下都有其自己的Makefile,这个用于编译单个模块或主程序,它们的父目录为src,在这个目录下有两个Makefile文件,一个是Makefile.in,这个就是上面讲到的通用内部Makefile;另一个是Makefile,这个用来联编所有的模块和主程序。
先来看下netcomm的Makefile文件内容,如下所示
1
path := SRC_PATH=. OUT_PATH=../../output
2
3
.
PHONY:
all 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)

2

3

4

5

6

7

8

9

10

11

12

13

14

15

再看下server的Makefile文件内容,如下所示
1
macros := MACROS="_USE_MEM_POOL=1"
2
3
path := SRC_PATH=. OUT_PATH=../../output SHARE_PATH=../../output
4
5
.
PH
ONY:
all 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)

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

最后看下src的Makefile文件内容,如下所示
1
.
PHONY:
all 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

2

3

4

5

6

7

8

9

10

11

12

13

14

15
