(二)同级目录下makefile应用进阶实例分析
前言
假设一个工程包含 main.cpp / dog.cpp / cat.cpp / snake.cpp 以及 dog.h / cat.h / snake.h等文件在同一级目录下面,文件结构如下所示
- window目录下查看

- linux平台下查看

其中程序如下所示:
/* dog.cpp */ #include <stdio.h> void dogPrintf(void) { printf("I am a dog !\n"); } /* dog.h */ #ifndef _DOG_H #define _DOG_H void dogPrintf(void); #endif
/* cat.cpp */ #include <stdio.h> void catPrintf(void) { printf("I am a cat!\n"); } /* cat.h */ #ifndef _CAT_H #define _CAT_H void catPrintf(void); #endif
/* snake.cpp */ #include <stdio.h> void snakePrintf(void) { printf("I am a snake!\n"); } /* snake.h */ #ifndef _SNAKE_H #define _SNAKE_H void snakePrintf(void); #endif
/* main.c */
#include <iostream>
#include "dog.h"
#include "cat.h"
#include "snake.h"
int main(void)
{
dogPrintf();
catPrintf();
snakePrintf();
return 0;
}
(1)不使用makefile,直接编译
在linux环境下使用g++编译流程如下所示:
shenqian5@Cpl-MT-General-74-39:~/work/test$ ls cat.cpp cat.h dog.cpp dog.h main.cpp snake.cpp snake.h shenqian5@Cpl-MT-General-74-39:~/work/test$ g++ -c dog.cpp shenqian5@Cpl-MT-General-74-39:~/work/test$ g++ -c cat.cpp shenqian5@Cpl-MT-General-74-39:~/work/test$ g++ -c snake.cpp shenqian5@Cpl-MT-General-74-39:~/work/test$ g++ -c main.cpp shenqian5@Cpl-MT-General-74-39:~/work/test$ ls cat.cpp cat.h cat.o dog.cpp dog.h dog.o main.cpp main.o snake.cpp snake.h snake.o shenqian5@Cpl-MT-General-74-39:~/work/test$ g++ -o main main.o dog.o cat.o snake.o shenqian5@Cpl-MT-General-74-39:~/work/test$ ls cat.cpp cat.h cat.o dog.cpp dog.h dog.o main main.cpp main.o snake.cpp snake.h snake.o shenqian5@Cpl-MT-General-74-39:~/work/test$ ./main I am a dog ! I am a cat! I am a snake! shenqian5@Cpl-MT-General-74-39:~/work/test$
总结:不适用makefile直接编译,每次编译都要经过繁杂的步骤,对于大型项目基本不可能得到推广,因此必须引入makefile
(2)初级makefile,进行编译
此时需要在同级目录下增加一个makefile文件(makefile文件没有后缀名),如图所示:

然后在makefile文件中编写编译规则,初级makefile内容如下所示:
main : main.o dog.o cat.o snake.o g++ -o main main.o dog.o cat.o snake.o main.o : main.cpp dog.h cat.h snake.h g++ -c main.cpp dog.o : dog.cpp dog.h g++ -c dog.cpp cat.o : cat.cpp cat.h g++ -c cat.cpp snake.o : snake.cpp snake.h g++ -c snake.cpp .PHONY : clean clean: rm -rf *.o main
在linux平台下执行如下:
shenqian5@Cpl-MT-General-74-39:~/work/test$ make g++ -c main.cpp g++ -c dog.cpp g++ -c cat.cpp g++ -c snake.cpp g++ -o main main.o dog.o cat.o snake.o shenqian5@Cpl-MT-General-74-39:~/work/test$ ./main I am a dog ! I am a cat! I am a snake! shenqian5@Cpl-MT-General-74-39:~/work/test$
(3)进阶makefile,进行编译
使用定义变量进行替代,以及$@ $^ $< 三个自动化变量进一步的进行简化初级makefile文件,如下所示:
$@ —— 表示规则中的目标文件集
$^ —— 表示所有的依赖目标的集合
$< —— 表示依赖目标中的第一个目标名字
TARGET = test CC = g++ CFLAGS = -c -Wall -g $(TARGET) : main.o dog.o cat.o snake.o g++ -o $@ $^ main.o : main.cpp dog.h cat.h snake.h g++ -c $< dog.o : dog.cpp dog.h g++ -c $< cat.o : cat.cpp cat.h g++ -c $< snake.o : snake.cpp snake.h g++ -c $< .PHONY : clean clean: rm -rf *.o $(TARGET)
(4)高级makefile,进行编译
TARGET = test CC = g++ CFLAGS = -c -Wall -g $(TARGET) : main.o dog.o cat.o snake.o g++ -o $@ $^ .c .o : g++ -c $< .PHONY : clean clean: rm -rf *.o $(TARGET)
这里需要注意的一点是:
.c .o :
g++ -c $<
在这里表示的是,所有的.o文件都是依赖于相应的.c文件,这是Makefile的“后缀规则”,是一种比较古老的规则,可以使用“模式规则”来实现:
1 TARGET = test 2 CC = g++ 3 CFLAGS = -c -Wall -g 4 5 $(TARGET) : main.o dog.o cat.o snake.o 6 g++ -o $@ $^ 7 %.c : %.o 8 g++ -c $< 9 10 .PHONY : clean 11 clean: 12 rm -rf *.o $(TARGET)
(5)终极makefile,进行编译
1 TARGET := test 2 CC = g++ 3 CFLAGS = -c -Wall -g 4 5 %.o : %.c 6 $(CC) -c $< -o $@ 7 SOURCE := $(wildcard *.cpp) 8 OBJS := $(patsubst %.cpp, %.o, $(SOURCE)) 9 10 $(TARGET) : $(OBJS) 11 $(CC) -g -o ./$(TARGET) $(OBJS) 12 13 .PHONY : clean 14 clean: 15 $(RM) -rf *.o $(TARGET)
第1行 := 表示前面的变量不能使用后面的变量,比如 TARGET := $(OBJS) 将会出错。
第5行 %.o : %.c 这个是模式规则
第6行 自动化变量 $< 在模式规则中,表示依赖集合, $@ 表示目标集合
第7行 wildcard是扩展通配符,是Makefile的一个函数,这里是Makefile的函数调用。在这里是将当前目录下所有 .cpp 结尾的文件展开
第8行 patsubst 是替换通配符,将SOURCE中以 *cpp 结尾的名字替换为 *.o 其实这里OBJ的值就是 main.o dog.o cat.o snake.o 而SOURCE的值为:main.cpp dog.cpp cat.cpp snake.cpp
第11行 正常的Makefile书写,目标:依赖,规则,只是这里都是用变量代替了。
第13行 使用了伪目标clean,用来执行清除工作。
第15行 清除编译产生的中间文件。RM 的默认值是 rm -f 这里执行的是:rm -f *.o test

浙公网安备 33010602011771号