1概述:
能读懂, 编写makefile应该是每一个Linux程序猿都必须拥有的基本素质,因为makefile是大型的工程中绕不过去的,你能不能写makefile了,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。
因为,makefile关系到了整个工程的编译规则:
- 如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
- 如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程。
- 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
从上面的例子可以看出 , makefile是一个"自动化编译器",你只需要写好一个makefile, 然后执行一个make命令 , 整个工程就会自动按照规则编译了,极大提高了我们程序猿的效率。
2编写Makefile:
2.1 单文件Makefile:
//Makefile
test: test.o #(test)是目标文件 (:)是依赖的文件或者命令 (test.o) 是test依赖的文件 gcc -o test test.c .PHONY: clean #(.PHONY)是伪目标标记, 伪目标标记后的命令,不会和目录下文件重复,\ 也就是说clean如果没有伪目标标记的话,目录下有一个clean的文件\ make clean将不会正确执行! clean: rm -fr *.o test
引用一段百度百科结合我上面的例子,说明一下吧
1 冒号(:)左边的test是目标文件 , 目标文件依赖于冒号(:)右边,
2 冒号右边即可以是文件test.o,也可以是目标,后面多文件多目录编译会讲到实例
3 第二行的gcc很明显是一个命令, 命令必须要以tab开头隔开 , Makefile里的命令可以是任何的shell命令
//test.c
#include <stdio.h> int main(void) { printf("once_file_makefile ...\n"); return 0; }
运行结果:
2.2 多目录下Makefile编写
首先我建立一个Makefile的根目录如下图(1):
根目录下的Makefile如下:
CC := gcc SUBDIR := d1\ d2\ test\ obj#子目录文件夹 OBJS := d1.o d2.o test.o#生成目标文件所需的依赖 OBJS_DIR := obj#所有目标文件依赖的.o文件存放的目录 BIN := myapp#最终的目标文件 BIN_DIR := app#存放目标文件的目录 export CC OBJS OBJS_DIR BIN BIN_DIR#export关键字,导出CC等变量到子目录,让次级目录下的Makefile可以识别 all: CHECK_DIR $(SUBDIR) #分别执行CKECK_DIR和$(SUBDIR)两个依赖,这里的依赖是目标而不是文件 CHECK_DIR: @mkdir -p $(BIN_DIR) #创建存放目标文件的目录 $(SUBDIR): ECHO @make -C $@ #在所有的子目录下执行make ECHO: @echo start compile ... .PHONY:clean clean: @rm -fr $(OBJ)/*.o $(BIN_DIR)
d1/Makefile
../$(OBJS_DIR)/d1.o:d1.c #OBJS_DIR是从根目录通过export关键字传过来的变量\ 也就等同于../obj/d1.o依赖于当前目录下d1.c $(CC) -c $^ -o $@ #gcc -c 依赖(d1.c) -o 目标(d1.o) $^表示所有依赖, $@表示目标
d2/Makefile
../$(OBJS_DIR)/d2.o: d2.c $(CC) -c $^ -o $@
test/Makefile
../$(OBJS_DIR)/test.o: test.c $(CC) -c $^ -o $@
obj/Makefile
../$(BIN_DIR)/$(BIN):$(OBJS) $(CC) -o $@ $^
include/include.h
// include/include.h #ifndef __INCLUDE_H__ #define __INCLUDE_H__ #include <stdio.h> #define DEBUG(f, arg...) printf("file:[%s] " " line:[%d] :"f"\n",\ __FILE__, __LINE__, ##arg) #endif //__INCLUDE_H__
d1/d1.c d2/d2.c test/test.c
// d1/d1.c #include "../include/include.h" void d2(void) { DEBUG("Massage from d2"); }
// d2/d2.c #include "../include/include.h" void d1(void) { DEBUG("Massage from d1"); }
// test/test.c #include "../include/include.h" int main(void) { d1(); d2(); return 0; }
运行结果: