The syntax of Makefiles(makefile的语法):
makefile主要包括两个方面:1.一组依赖关系 2.规则
一个简单的makefile文件:Makefile1
myapp: main.o 2.o 3.o gcc -o myapp main.o 2.o 3.o main.o: main.c a.h gcc -c main.c 2.o: 2.c a.h b.h gcc -c 2.c 3.o: 3.c b.h c.h gcc -o 3.c
1.一组依赖关系:
依赖关系定义了最终应用程序里的每个文件 与源文件之间的关系。比如上面最终应用程序(可执行程序myapp)依赖于文件main.o,2.o 和3.o;同理main.o依赖于main.c和a.h。
依赖关系的写法:先写目标的名称,然后紧跟一个冒号,接着是空格或制表符tab,最后是用空格或制表符tab隔开的文件列表(这些文件用于创建目标文件)。如上面的:myapp: main.o 2.o 3.o
2.规则
规则定义了目标的创建方式。如上面的: gcc -o myapp main.o 2.o 3.o
这里有一个非常奇怪而又令人遗憾的语法现象:空格与制表符tab是有区别的,规则所在的行必须以制表符tab开头,用空格是不行的。如果少了制表符tab,make命令就不会正常工作,会提醒你少了间隔(制表符tab)。
3.make makefile
如果makefile文件不是默认文件名makefile、Makefile或GNUmakefile时,在调用make命令时加上-f选项。
现在来具体尝试创建makefile文件:
首先创建三个头文件(为简单全部为空文件):
touch a.h
touch b.h
touch c.h
然后创建c文件:
main.c如下:
/* main.c */ #include <stdlib.h> #include "a.h" extern void function_two(); extern void function_three(); int main() { function_two(); fucntion_three(); exit(EXIT_SUCCESS); }
2.c如下:
/* 2.c */ #include <stdio.h> #include "a.h" #include "b.h" void function_two() { printf("Hello "); }
3.c如下:
/* 3.c */ #include <stdio.h> #include "b.h" #include "c.h" void function_three() { printf("World\n"); }
现在来执行make命令:
make -f Makefile1
会执行如下命令:
gcc -c main.c
gcc -c 2.c
gcc -c 3.c
gcc -o myapp main.o 2.o 3.o
此时生成了一个叫myapp的可执行文件,运行myapp
./myapp
运行结果:Hello World
假如更新了b.h,再次make会发生什么不同,
touch b.h
make -f Makefile1
会执行如下命令:
gcc -c 2.c gcc -c 3.c gcc -o myapp main.o 2.o 3.o
可见依赖b.h的目标全进行了重新编译,而不依赖它的main.o却不需要重新编译,就也是make的优势所在。
makefile文件中的注释
makefile文件中的注释以#号开关,一直延续一这一行的结束。
makefile文件中的宏
makefile文件中的宏相当于C中的#define,好处不言而喻。
在makefile中定义宏:MACRONAME = value
引用宏:$(MACRONAME)或${MACRONAME},如果想把一个宏的值设置为空,可令等号(=)后面留空。
A Makefile with Macors:Makefile2
all: myapp # Which compiler CC = gcc # Where are include file kept INCLUDE = . # Options for development CFLAGS = - g -Wall -ansi # Options for release # CFLAGS = -O -Wall -ansi myapp: main.o 2.o 3.o $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c
现在删除旧的安装文件,并通过这个新的makefile文件创建新安装文件:
rm *.o myapp
make -f Makefile2
会执行如下命令:
gcc -I. -g -Wall -ansi -c main.c gcc -I. -g -Wall -ansi -c 2.c gcc -I. -g -Wall -ansi -c 3.c gcc -o myapp main.o 2.o 3.o
运行可执行文件myapp
./myapp
运行结果:Hello World
make命令将$(CC)、$(CFLAGS)和$(INCLUDE)替换为相应的宏定义。
其中all处可以指定多个目标(即可产生多个可执行文件),比如:all: myapp1 myapp2。
伪目标:
伪目标并不是一个文件,即并不会产生如myapp的可执行文件,它只是一个标签,所以我们不能像可执行文件myapp那样执行它(./myapp)。那如何执行它且它有什么作用呢?请看下面Makefile3:
all: myapp # Which compiler CC = gcc # Where are include file kept INCLUDE = . # Options for development CFLAGS = - g -Wall -ansi # Options for release # CFLAGS = -O -Wall -ansi myapp: main.o 2.o 3.o $(CC) -o myapp main.o 2.o 3.o main.o: main.c a.h $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c 2.o: 2.c a.h b.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c 3.o: 3.c b.h c.h $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c clean: -rm main.o 2.o 3.o myapp
Makefile3在Makefile2的基础上加上了
clean: -rm main.o 2.o 3.o myapp
这里的clean就是一个伪目标,为了表示是伪目标,一般如下写:
.PHNOY: clean clean: -rm main.o 2.o 3.o myapp
删除旧安装,利用Makefile3重新make。
rm *.o myapp
make -f Makefile3
发现与上面执行Makefil2的命令是一样的。
其实如下命令与 rm *.o myapp是一样的。
make -f Makefile3 clean
可见,伪目标需要显示调用。
内置规则:
如果makefile文件叫Makefile或makefile或GNUmakefile,而不是如上面那样叫Makefile1、Makefile2之流,则没有必要做精确说明,直接进行如下命令即可:
make
伪目标clean执行命令如下:
make claen
后缀和模式规则:
上面文件都是.c文件,假设是.cpp文件呢。
想要增加一条新的后缀规则,首先需要在makefile文件中增加一行语句,告诉make命令这个新的后缀名。然后即可用主穿上新的后缀名来定义规则。make使用特殊语法
.<old_suffix>.<new_suffix>
来定义一条通用规则,利用该规则可以从带有旧后缀名的文件创建带有新的后缀的文件,并保留原文件的前半部分。
下面是makefile文件一个片段,它用一个新的通用规则来将.cpp文件编译为.o文件。
.SUFFIXES: .cpp
.cpp.o:
$(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $<
特殊依赖关系.cpp.o:告诉make,紧随其后的规则是用于将后缀名为.cpp的文件转换为后缀名为.o的文件。
-xc++标志的作用是告诉gcc编译器这是一个c++源文件。
$< 为自动化变量,表示当前依赖文件的名字。还有几个重要的自动化变量:
$@ 表示当前目标的名字。
$? 表示当前目标所依赖的文件列表中比当前目标文件还要新的文件。
$* 表示不包括后缀名的当前依赖文件名字。
通过可用能配符%来实现同样的原理:
%.cpp: %.o
$(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $<
用make管理函数库
函数库实际上就是文件,通常是以.a(archive)为后缀名,该文件中包含了一组目标文件。
下面将文件2.o和3.o放入函数库mylib.a中:
all: myapp # Which compiler CC = gcc # Where are include file kept INCLUDE = . # Options for development CFLAGS = - g -Wall -ansi # Options for release # CFLAGS = -O -Wall -ansi # Local Libraries MYLIB = mylib.a myapp: main.o $(MYLIB) $(CC) -o myapp $(MYLIB) $(MYLIB): $(MYLIB)(2.o) $(MYLIB)(3.o) main.o: main.c a.h 2.o: 2.c a.h b.h 3.o: 3.c b.h c.h clean: -rm main.o 2.o 3.o myapp $(MYLIB)
其中
main.o: main.c a.h 2.o: 2.c a.h b.h 3.o: 3.c b.h c.h
利用默认规则来实现。
下面来测试这个新版的makefile文件Makefile4:
rm -f myapp *.o mylib.a
make -f Makefile4
执行如下:
gcc -g -Wall -ansi -c -o main.o main.c gcc -g -Wall -ansi -c -o 2.o 2.c
ar rv mylib.a 2.o a - 2.o gcc -g -Wall -ansi -c -o 3.o 3.c
ar rv mylib.a 3.o a - 3.o gcc -o myapp main.o mylib.a
用于管理函数库的语法是lib(file.o),它的含义是目标文件file.o是存储在函数库lib.a中的。make命令用一个内置规则来管理函数库,该规则的常见形式如下所示:
.c.a: $(CC) -c $(CFLAGS) $< $(AR) $(ARFLGAS) $@ $*.o
宏$(AR)和$(ARFLAGS)的默认取值通常分别是命令ar和选项rv,你可利用man ar来查看相关选项的含义。
上面语法告诉make,要想从.c文件得到.a文件,它必须应用两条规则:
1. $(CC) -c $(CFLAGS) $< 必须编译源文件以生成目标文件
2. $(AR) $(ARFLGAS) $@ $*.o 用ar命令将新的目标文件添加到函数库中
最后我贴出实习做Fcitx输入法项目时用的一个makefile例子:
CC = gcc CXX = g++ CFLAGS = -Ifctix -Iui -I. -Wall CXXFLAGS = -Ifcitx -Iui -I. -Wall BUILD = build TARGET = input FLTK_PATH = `~/workspace/fltk-1.3.0/fltk-config --cxxflags --ldflags` C_SRCS = fcitx/py.c fcitx/pymap.c fcitx/punc.c CPP_SRCS = main.cpp ui/MwButton.cpp ui/MwLabel.cpp ui/PopupWidget.cpp fcitx/KeyboardWidget.cpp fcitx/Fcitx.cpp OBJS = $(BUILD)/main.o $(BUILD)/py.o $(BUILD)/pymap.o $(BUILD)/punc.o $(BUILD)/MwButton.o $(BUILD)/MwLable.o \ $(BUILD)/PopupWidget.o $(BUILD)/KeyboardWidget.o $(BUILD)/Fcitx.o TARGET : OBJS $(CXX) $(OBJS) -o $(TARGET) -L$(FLTK_PATH) rm -rf build OBJS: $(CC) $(CFLAGS) -c $(C_SRCS) $(CXX) $(CXXFLAGS) $(FLTKA_PATH) -c $(CPP_SRCS) if ! [ -d build ]; then mkdir build; fi mv *.o $(BUILD) all : TARGET DECTS DICTS_DIR = /home/edan/mnt/input_dict DICTS: if ! [ -d $(DICTS_DIR)]; then mkdir $(DICTS_DIR); cp ./dicts/* $(DICTS_DIR) if .PHONY : then clean: rm $(OBJS) $(TARGET)
Refference:
(1)《Linux程序设计》 -Neil Matthew Richard Stones著 第4版第9章开发工具
(2)《跟我一起写 Makefile》-陈皓
(3)makefile官方文档:http://www.gnu.org/software/make/manual/make.html#Introduction