首页 > 代码库 > makefile

makefile

一个项目中有太多文件需要编译,如果只用gcc进行手动编译,几乎是不可能的。

因此,出现了makefile来完成这个任务,makefile文件定义了一些规则,当执行makefile中定义的动作时,make命令会根据定义的规则对多个文件进行编译,从而达到编译,链接和生成可执行文件的目的。

 

1. makefile的基本规则:

  target:dependent objects

  [TAB] command

target:生成的目标对象文件名

dependent objects:依赖的对象文件名

command:要生成此规则中的目标对象,所需要执行的命令,必须用tab进行缩进

  action:

  [TAB] command

action:可以执行的一个动作,不需要依赖目标对象

command:要完成此规则中的动作,所需要执行的命令,必须用tab进行缩进

例如:一个makefile文件定义了两个动作:

1. 生成test可执行文件:依赖三个目标文件,为了成功生成test文件,必须先保证三个目标文件存在。make命令会先尝试从左往右依次生成目标文件,每个目标文件的生成又定义了自己的规则,make命令会按照每个目标文件的生成规则来生成各个目标文件,每个目标文件的生成规则下都一个gcc命令,表明每个目标文件的生成都需要执行各自的gcc命令来生成各自的目标文件,当三个目标文件的生成后,就可以执行test生成规则中的gcc命令来成功test可执行文件;

2. clean动作:该规则不需要生成任何目标文件,该规则只是定义了一个shell命令,用于删除生成过程中产生的文件而已;

 1 test:add_int.o add_float.o main.o 2     gcc -o test main.o add_int.o add_float.o 3 add_int.o:math/add_int.cpp math/add.h 4     gcc -c -o add_int.o math/add_int.cpp 5 add_float.o:math/add_float.cpp math/add.h 6     gcc -c -o add_float.o math/add_float.cpp 7 main.o:main.cpp 8     gcc -c -o main.o main.cpp 9     10 clean:11     rm -rf test main.o add_int.o add_float.o

 

2. 模式匹配:

main.o:%o:%c 

  gcc -c $< -o $@

%O:%C:表示用target中的main.o名字替换成main.c,如果是cpp就使用%cpp

$<:表示用依赖项代替,即main.c

$@:表示target的名字代替,即main.o

 

3. 使用变量

为了减少手动输入的造成负担和错误,以及为了更方便的进行维护,makefile允许使用变量定义文本内容

例如:定义一个变量用于保存所有的依赖的目标对象

1 OBJS = add_int.o add_float.o main.o2 3 test:$(OBJS)4     gcc -o test $(OBJS)

 

例如:定义一个变量用于保存一个常用的命令

1 OBJS = add_int.o add_float.o main.o2 RMF = rm -f3 TARGET = test4 5 clean:6     $(RMF) $(TARGET) $(OBJS)

通过使用变量,最后可以得到一个非常简洁的版本,将所有的目标文件的生成通过变量定义就可以合并到一个规则中进行生成了

注:需要正确定义每个目标文件的生成路径

 1 OBJS = math/add_int.o math/add_float.o main.o 2 RMF = rm -f 3 TARGET = test 4 CC = gcc 5  6 $(TARGET):$(OBJS) 7     $(CC) -o $(TARGET) $(OBJS) 8 $(OBJS):%o:%cpp 9     $(CC) -c $< -o $@10     11 clean:12     $(RMF) $(TARGET) $(OBJS)

除了自定义变量,makefile已经有一些预定义好的变量

AR: ar,生成静态库文件

AS:as,汇编编译器的名称

CC:cc,C语言编译器的名称

CXX:g++,C++编译器的名称

RM:rm -f

ARFLAGS:生成静态库文件的选项

ASFLAGS:汇编编译器的编译选项

CFLAGS:C编译器的编译选项

CXXFLAGS:C++编译器的编译选项

除了自定义变量,预编译变量,还有一种自动变量

$*:目标文件的名称,不包含扩展名

$+:所有依赖文件的名称,可能重复

$<:依赖文件中的第一个文件的名称

$?:依赖文件中,所有比目标文件时间戳晚的文件(即更新的依赖文件)

$@:目标文件的名称

$^:依赖文件中,不会重复的依赖文件名

 

4. 使用搜索路径

在makefile文件中手动添加路径非常麻烦而且很容易出错,因此makefile提供了一种更简便的方式,实现了自动搜索目录下的文件

VPATH = math:. 添加了math目录和当前目录,用:分割

当使用VPATH添加了自动搜索目录后,就不需要再在每个文件名前手动添加目录

 1 VPATH = math:. 2  3 CFLAGS = -Imath -O2 4 OBJS = add_int.o add_float.o main.o 5 RMF = rm -f 6 TARGET = test 7 CC = gcc 8  9 $(TARGET):$(OBJS)10     $(CC) -o $(TARGET) $(OBJS) $(CFLAGS)11 $(OBJS):%o:%cpp12     $(CC) -c $(CFLAGS) $^ -o $@13     14 clean:15     $(RMF) $(TARGET) $(OBJS)

但是这样会将所有生成的目标文件都放在了当前文件夹下面,文件多了会影响我们对源文件的管理,因此我们想要将所有生成的文件放在单独的文件夹下面

 1 VPATH = math:. 2  3 CFLAGS = -Imath -O2 4 OBJS = add_int.o add_float.o main.o 5 OBJSDIR = ./objs 6 RMF = rm -f 7 RMRF = rm -rf 8 TARGET = test 9 CC = gcc10 11 link:12     $(CC) -o $(TARGET) $(OBJSDIR)/*.o $(CFLAGS)13     14 build:$(OBJSDIR) $(OBJS)15 $(OBJS):%o:%cpp16     $(CC) -c $(CFLAGS) $^ -o $(OBJSDIR)/$@17 $(OBJSDIR):18     mkdir -p $(OBJSDIR)19     20 clean:21     $(RMF) $(TARGET) $(OBJS)22     $(RMRF) $(OBJSDIR)

我们现在就可以将生成的目标文件放在objs目录下了,而且link,buid分成了两个命令来单独执行

 

5. 子目录中的makefile

在一个巨大的工程中,会涉及多个相对独立的模块,那么每个子目录可以有自己的makefile文件,然后由一个总的makefile文件控制这些子目录下的makefile完成编译和链接工作

对于总makefile中的变量,可以通过export命令向子目录进行传递

子目录下的makefile

1 OBJS = add_int.o add_float.o2 3 gen:$(OBJS)4 $(OBJS):%.o:%.cpp5     $(CC) -c $< -o $(OBJSDIR)/$@ $(CFLAGS)

总目录下的makefile

 1 VPATH = math:. 2  3 CFLAGS = -Imath -O2 4 OBJS = main.o 5 export OBJSDIR = $(shell pwd)/objs 6 RMF = rm -f 7 RMRF = rm -rf 8 TARGET = test 9 CC = gcc10 11 link:$(OBJSDIR) $(OBJS)12     $(MAKE) -C math13     $(CC) -o $(TARGET) $(OBJSDIR)/*.o $(CFLAGS)14 $(OBJS):%o:%cpp15     $(CC) -c $< -o $(OBJSDIR)/$@ $(CFLAGS)16 $(OBJSDIR):17     mkdir -p $(OBJSDIR)18     19 clean:20     $(RMF) $(TARGET) $(OBJS)21     $(RMRF) $(OBJSDIR)

 

6. makefile中的常用函数

$(wildcard *.*) 返回某个目录下的所有匹配的文件名列表

$(patsubst pattern, replacement, text_list) 将text_list中的文件名中匹配patter的部份用replacement代替,并返回替换后的text_list

$(foreach input_text, input_test_list, output_text_list) 循环遍历input_text_list,每次取出一个值放入input_text中,然后执行output_text_list中的表达式,结果返回到一个text_list中

例如:

 1 VPATH = math:. 2  3 CFLAGS = -Imath -O2 4 ALLDIRS = math . 5 ALLCPPS = $(foreach dir, $(ALLDIRS), $(wildcard $(dir)/*.cpp)) 6 ALLOBJS = $(patsubst %.cpp, %.o, $(ALLCPPS)) 7 RMF = rm -f 8 RMRF = rm -rf 9 TARGET = test10 CC = gcc11 12 link:$(ALLOBJS)13     $(CC) -o $(TARGET) $(ALLOBJS) $(CFLAGS)14 $(ALLOBJS):%o:%cpp15     $(CC) -c $< -o $@ $(CFLAGS)16     17 clean:18     $(RMF) $(TARGET) $(ALLOBJS)19     $(RMRF) $(OBJSDIR)20     21 show:22     echo $(ALLOBJS)

 

makefile