在windows系统中,我们编写了一个应用程序,直接点击编译链接,IDE(如VC、VS)就会为我们生成我们需要的目标文件(hex、exe等);而在linux中,我们需要在命令行输入命令来完成这个过程,并且对于不同的应用程序,输入的命令不同,同一个应用程序,可能需要输入多条命令,例如:
a.c
#include <stdio.h>
#include "a.h"
int main(int argc, char *argv[])
{
printf("a.h %d\r\n",NUM);
printstr("abc\r\n");
return 0;
}
b.c
#include <stdio.h>
void printstr(const char *str)
{
printf("%s",str);
}
a.h
#ifndef __A_H
#define __A_H
#define NUM 123
#endif
将a.c、b.c编译链接为build,输入命令
gcc -o build a.c b.c
每次执行命令,都会重新编译链接,如果文件数目多,效率就变得非常的低
当文件数目比较多的时候,为了提高效率,我们需要选择性编译文件,有的文件没有改动,就不需要编译了,为此引入了Makefile,Makefile可以通过编写一定的规则,达到这样的目的
Makefile的主要语法:
目标文件:依赖文件
【TAB键】命令
obj : srctree
command
Makefile编译条件:
1、上次编译时依赖文件不存在,这次编译时已经被创建
2、两次编译之间,依赖文件被更新
一个简单的Makefile:
build : a.c b.c
gcc -o build a.c b.c
把依赖文件和Makefile放在同一目录下,送到服务器上,进入该目录输入make,即可完成编译链接
第二次输入make,出现如下提示,原因是依赖文件没有被更新,在a.c或b.c中新增一行,再次make,同样可以编译链接
make: `build' is up to date.
这个Makefile只能达到跟直接输入命令差不多的效果,当我们修改一个文件,它会给我们重新编译两个文件再链接
改进:分别编译,统一链接
build : a.o b.o
gcc -o build a.o b.o
a.o : a.c
gcc -c -o a.o a.c
b.o : b.c
gcc -c -o b.o b.c
使用改进后的Makefile,如果我们每次修改一个,再次make,它只会编译被修改的那个文件,再将它加入链接,做到了选择性编译
但这样的Makefile还是非常局限,有一百个输入文件,就要编写一百条编译规则
改进:使用Makefile语法统一目标文件、依赖文件
build : a.o b.o
gcc -o $@ $^
%.o : %.c
gcc -c -o $@ @<
这样,无论有多少个源文件,只需要一条编译命令
当Makefile写得比较长,添加源文件可能变得不方便
改进:使用变量,将变量放在文件的头部(或者是接近头部的地方),有新的源文件就往变量上添加
srctree := a.o
srctree += b.o
build : $srctree
gcc -o $@ $^
%.o : %.c
gcc -c -o $@ @<
同样目标文件也是一样
srctree := a.o
srctree += b.o
obj := build1
obj += buildd2
$(obj) : $(srctree)
gcc -o $@ $^
%.o : %.c
gcc -c -o $@ @<
这时,如果我们修改a.h再make,会发现make不成功,改进思路将头文件添加到编译规则中,但是头文件有的在编译器的库中,有的在当前目录,直接包含是不方便的
改进:生成一个依赖文件表,并使用它
srctree := a.o
srctree += b.o
obj := build1
obj += buildd2
$(obj) : $(srctree)
gcc -o $@ $^
dep_files := $(foreach f,$(srctree),.$(f).d)
dep_files := $(wildcard $(dep_files))
ifneq ($(dep_files), )
include $(dep_files)
endif
%.o : %.c
gcc -Wp,-MD,[email protected] -c -o $@ $<
clean :
rm -f *.o build
对于一个工程,这种Makefile无法满足需要
假设工程结构如下
思路:在每个目录下都建立一个Makefile,各级子目录的Makefile保存有源文件信息,由顶层目录的Makefile.build负责编译链接,顶层目录的Makefile除具备子目录Makefile的功能,还需要完成调用Makefile.build、为Makefile.build提供编译命令(指定编译工具、链接工具、各种选项等)、最后一次链接等工作
这个例子只有两层,多层结构的做法类似。从最底层开始,编译所有源文件,将目标文件链接打包为一个built-in.o,上层目录再编译所有源文件,将目标文件同它下层的built-in.o一同打包成一个built-in.o,重复上述操作若干次,最后顶层Makefile将main.c编译为maio.o,将maio.o同下层的多个built-in.o打包成顶层的built-in.o,再使用built-in.o链接为最终目标(可能需要根据需求转换格式)
顶层Makefile.build
PHONY := __build
__build:
obj :=
subdir :=
include Makefile
__subdir := $(patsubst %/,%,$(filter %/,$(obj)))
subdir += $(__subdir)
subdir_objs := $(foreach f,$(subdir),$(f)/built-in.o)
cur_objs := $(filter-out %/,$(obj))
dep_files := $(foreach f,$(cur_objs),.$(f).d)
dep_files := $(wildcard $(dep_files))
ifneq ($(dep_files), )
include $(dep_files)
endif
PHONY += $(subdir)
__build : $(subdir) built-in.o
$(subdir) :
make -C $@ -f $(TOPDIR)Makefile.build
built-in.o : $(subdir_objs) $(cur_objs)
echo $(subdir)
$(LD) -r -o $@ $^
dep_files = [email protected]
%.o : %.c
$(CC) $(CFLAGS) -Wp,-MD,$(dep_files) -c -o $@ $<
.PHONY : $(PHONY)
顶层Makefile(可以修改CROSS_COMPITE来指定交叉编译工具链)
CROSS_COMPILE =
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
export CC LD
TOPDIR := $(shell pwd)/
export TOPDIR
CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include
export CFLAGS
LDFLAGS := -lm
export LDFLAGS
TARGET := built
obj += main.o
obj += dir1/
obj += dir2/
all :
make -C ./ -f $(TOPDIR)Makefile.build
$(CC) $(LDFLAGS) -o $(TARGET) built-in.o
clean :
rm -f $(shell find -name "*.o")
rm -f $(TARGET)
distclean :
rm -f $(shell find -name "*.o")
rm -f $(TARGET)
rm -f $(shell find -name "*.d")
子目录Makefile
obj += a.o
obj += b.o
obj += c.o
obj += d.o