编写一个通用的Makefile

在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
发布了71 篇原创文章 · 获赞 4 · 访问量 7236

猜你喜欢

转载自blog.csdn.net/floatinglong/article/details/86614394