测试基于Ubuntu16.04 Eclipse for C/C++
参考链接:链接
适用于:在一个Project中,有多个*.cpp/*.c文件,多个文件中同时含有main函数。处于方便考虑,在Makefile文件中,目标可执行文件的依赖项,包含了所有源文件编译生成的*.o文件。这样的话,在编译的时候,就会产生main函数重复定义的错误。
例如,在一个Project中,有main.cpp和test.cpp两个源代码文件,位于src目录下,每一个文件中都有一个main函数,分别用于生成两个不同的目标可执行文件Main和Test。为了方便,在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 |
其中CXXFLAGS是GCC/G++在编译时的一系列参数,TARGET可以在执行make命令的时候定义。例如,要编译生成目标"Test",可以执行命令"make TARGET=Test Test"命令,之后通过Makefile文件中的"ifdef"语句就可以正确的生成CXXFLAGS。
测试Project的目录结构如下:
共有5个文件夹,inc和src中分别是头文件和源代码文件,doc中是一些说明文档,obj中是生成的*.o文件,bin中是生成的目标可执行文件。其中,main.cpp、test.cpp、WaterCurtain.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.cpp和main.cpp两个源代码文件均已经进行了修改,这样新的宏定义才会生效。
哪里有看不懂的地方,可以发邮件询问:[email protected]。