Linux下C/C++一个工程中多个main函数的Makefile文件的写法

时间:2024-03-20 10:00:00

测试基于Ubuntu16.04 Eclipse for C/C++

参考链接:链接

 

适用于:在一个Project中,有多个*.cpp/*.c文件,多个文件中同时含有main函数。处于方便考虑,在Makefile文件中,目标可执行文件的依赖项,包含了所有源文件编译生成的*.o文件。这样的话,在编译的时候,就会产生main函数重复定义的错误。

 

例如,在一个Project中,有main.cpptest.cpp两个源代码文件,位于src目录下,每一个文件中都有一个main函数,分别用于生成两个不同的目标可执行文件MainTest。为了方便,在Makefile文件中是这样写的:

1

2

3

4

5

6

7

OBJ=./src/main.o ./src/test.o

 

Main:${OBJ}

    ${CXX} ${OBJ} $(LIBS) -o [email protected]

 

Test:${OBJ}

    ${CXX} ${OBJ} $(LIBS) -o [email protected]

这样的话,在生成目标可执行文件Main或者Test时,均会对两个*.o文件的内容进行分析、链接,导致产生main函数重复定义的错误。

 

为了解决上述问题,使用了Makefile的条件编译功能。

在C/C++源代码中,main函数用#ifdef#endif包含起来,例如:

1

2

3

4

5

6

7

8

9

#ifdef TEST_

 

int main() {

    system("/home/xingyu/Desktop/test.sh abc");

 

    return 0;

}

 

#endif

上述例子中的宏"TEST_",不在C/C++源代码中定义,而是在调用GCC/G++编译器时,通过参数"-D TEST_"定义。对于不同的目标文件,用不同的宏将对应的main函数包含起来,再在调用时分别加入不同的"-D xxxxx"参数即可。

 

对于Makefile文件,可以用以下方式来处理:

1

2

3

4

5

6

7

8

9

CXXFLAGS = -c -O2 -g -Wall -fmessage-length=0 -I ${INC_DIR}

 

ifeq ($(TARGET),Test)

    CXXFLAGS += -D TEST_

endif

 

ifeq ($(TARGET),Main)

    CXXFLAGS += -D MAIN_

endif

其中CXXFLAGSGCC/G++在编译时的一系列参数,TARGET可以在执行make命令的时候定义。例如,要编译生成目标"Test",可以执行命令"make TARGET=Test Test"命令,之后通过Makefile文件中的"ifdef"语句就可以正确的生成CXXFLAGS

 

测试Project的目录结构如下:

Linux下C/C++一个工程中多个main函数的Makefile文件的写法Linux下C/C++一个工程中多个main函数的Makefile文件的写法

共有5个文件夹,incsrc中分别是头文件和源代码文件,doc中是一些说明文档,obj中是生成的*.o文件,bin中是生成的目标可执行文件。其中,main.cpptest.cppWaterCurtain.cpp三个文件中均含有main函数。

 

Project的完整Makefile文件内容如下:

1

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

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

CXXFLAGS = -c -O2 -g -Wall -fmessage-length=0 -I ${INC_DIR}

 

 

 

ifeq ($(TARGET),WaterCurtain)

 

    CXXFLAGS += -D WATERCURTAIN_

 

endif

 

 

 

ifeq ($(TARGET),Main)

 

    CXXFLAGS += -D MAIN_

 

endif

 

 

 

ifeq ($(TARGET),Test)

 

    CXXFLAGS += -D TEST_

 

endif

 

 

 

LIBS =

 

 

 

INC_DIR=./inc

 

BIN_DIR=./bin

 

SRC_DIR=./src

 

OBJ_DIR=./obj

 

 

 

SRC=${wildcard ${SRC_DIR}/*.cpp}

 

OBJ=${patsubst %.cpp,$(OBJ_DIR)/%.o,${notdir ${SRC}}}

 

 

 

 

 

TARGET=WaterCurtain Test Main

 

 

 

BIN_TARGET=${BIN_DIR}/${TARGET}

 

 

 

 

 

${BIN_TARGET}:${OBJ}

 

    @echo "building " [email protected]

 

    @${CXX} ${OBJ} $(LIBS) -o ${BIN_TARGET}

 

    @echo "finished " [email protected]

 

 

 

WaterCurtain:${BIN_DIR}/WaterCurtain

 

 

 

Test:${BIN_DIR}/Test

 

 

 

Main:${BIN_DIR}/Main

 

      

 

 

 

${OBJ_DIR}/%.o:${SRC_DIR}/%.cpp

 

    @echo "building " [email protected] 

 

    @${CXX} ${CXXFLAGS} $< -o [email protected]

 

    @echo "finished " [email protected]

 

 

 

clean_All:

 

    @echo "cleaning......"

 

    @rm -f $(OBJ) $(BIN_TARGET)

 

    @echo "finished cleaning"

 

 

 

clean_obj:

    @echo "cleaning obj files......"

    @rm -f $(OBJ)

    @echo "finished cleaning obj files"

 

 

 

 

clean_WaterCurtain:

 

    @echo "cleaning WaterCurtain......"

 

    @rm -f ${BIN_DIR}/WaterCurtain

 

    @echo "finished cleaning WaterCurtain"

 

      

 

clean_Test:

 

    @echo "cleaning Test......"

 

    @rm -f ${BIN_DIR}/Test

 

    @echo "finished cleaning Test"

 

 

 

clean_Main:

 

    @echo "cleaning Main......"

 

    @rm -f ${BIN_DIR}/Main

 

    @echo "finished cleaning Main"

 

 

 

 

 

 

 

#[email protected]:目标的名字

 

#

 

#$^:构造所需文件列表所有所有文件的名字

 

#

 

#$<:构造所需文件列表的第一个文件的名字

 

#

 

#$?:构造所需文件列表中更新过的文件

 

 

 

#$(subst 要被替换的字符串,用来替换的字符串,被处理的字符串):

 

#

 

#$(wildcard 寻找的文件):

 

#

 

#$(basename 文件名):

 

 

 

#用于查看变量的值

 

#test:

 

#   echo $(SRC)

 

#   echo $(OBJ)


注意:在编译与上一个目标文件不同的目标文件时,需要先删除所有*.o文件。

 

例如,上一次编译的是Test,现在想要编译Main,则需要先清楚所有的*.o文件,否则会导致错误。因为在编译Test的时候,由于宏定义的作用,会将test.cpp中的main函数编译生成test.o文件,而main.cpp中的main函数会被忽略,生成main.o文件。在编译新目标文件Main时,如果test.cpp文件没有发生更改,那么test.o文件就不会被重新编译,这样的话test.o文件中事实上是包含了main函数的,由于没有对test.cpp文件进行重新编译,因此在编译Main时,声明的宏定义没有起作用。如此一来,就会再次出现main函数重复定义的错误。如果不想删除所有的*.o文件,则需要确保在编译Test之后Main之前,test.cppmain.cpp两个源代码文件均已经进行了修改,这样新的宏定义才会生效。

 

 

哪里有看不懂的地方,可以发邮件询问:[email protected]