(5)Makefile详解
?? ? Makefile是一个自动化的编译工具,关系到整个工程的编译规则,极大的提高了软件开发的效率。 ? ??? (1)Makefile的编译规则 //Makefile 也可以写作 makefile
? ??? (2)Makefile的书写规则 ??? 规则的三个要素:目标、依赖、命令 //Makefile格式
? ??? (3)Makefile的工作原理 //当执行make命令生成目标文件时
1.make命令使用时会在当前目录下寻找名为Makefile或者makefile的文件; 2.如果找到,它将会把文件中第一个target当作最终的目标文件; 3.如果target已然存在,并且它所依赖的所有文件的修改时间都没更改,则返回;否则执行后面的内容; 4.一层一层往前推,类似一个堆栈,直到到达所依赖的文件是.h和.c或者.cpp文件为止,此时便可以链式推导生成最终的目标文件了;
? ??? (4)gcc和g++命令概述 ??? 1)gcc和g++简介 ??? GCC(GNU Compiler Collection)??? GNU编译器套件 ??? gcc和g++均是GCC的一部分,gcc是GNU的c编译器,g++是GNU的c++编译器; ??? gcc将后缀名为.c的文件当作c程序,将后缀名为.cpp的文件当作c++程序; ??? g++将后缀名为.c和cpp的均当作c++程序; ??? 2)区别 ??? g++和gcc均可以编译链接c或者c++程序,使用方法有稍微的区别,g++编译链接c程序时可能调用gcc;gcc编译链接c++时,由于c++是c的超集,需要指定使用c++的动态库libstdc++.so; ??? 范例如下,是等价的 // 存在一个main.cpp g++ main.cpp -o test ??? 3) GNU的编译步骤 1.预处理(Preprocessing) ? ??? (5)Makefile使用简介 ??? 1)变量的定义和使用 #定义编译器 cc = gcc 或者 cc = g++ #定义编译参数 -w 不显示任何警告信息 -W 只显示错误警告信息 -Wall 显示所有警告信息 CFLAG = -g -Wall -W ??? 在变量的使用过程中,分为两种,递归展开式变量和直接展开式变量 //递归展开式变量 A = $(B) B = $(C) C = ME 则 A = ME,其优点是前面使用的变量可以使用后续定义的变量的值,缺点是有可能陷入无限循环 //直接展开式变量 A = aaa B := $(A)bbb A = ccc 则 B的值为 aaabbb,后续定义的A的值与其无关 如果写法为 A = aaa B = $(A)bbb A = ccc 则最终 B的值为 cccbbb ??? 2)$相关变量 $^ 所有的依赖目标的集合。以空格分隔。如果在依赖目标中有多个重复的,那个这个变量会去除重复的依赖目标,只保留一份。 [email?protected] 表示规则中的目标文件集。在模式规则中,如果有多个目标,那么,"[email?protected]"就是匹配于目标中模式定义的集合 $? 所有比目标新的依赖目标的集合。以空格分隔。 $< 依赖目标中的第一个目标名字。如果依赖目标是以模式(即"%")定义的,那么"$<"将是符合模式的一系列的文件集。注意,其是一个一个取出来的。 $(@D) 表示"[email?protected]"的目录部分(不以斜杠作为结尾),如果"[email?protected]"值是"dir/foo.o",那么"$(@D)"就是"dir",而如果"[email?protected]"中没有包含斜杠的话,其值就是"."(当前目录) 。 $(@F) 表示"[email?protected]"的文件部分,如果"[email?protected]"值是"dir/foo.o",那么"$(@F)"就是"foo.o","$(@F)"相当于函数"$(notdir [email?protected])" // 例1 %.o : %.c gcc -c $< -o [email?protected] 把所以的c文件编译生成对应的o文件,$<代表每次取的c文件,[email?protected]代表每次c文件对应的目标文件 // 例2 main : main.o test.o test1.o test2.o gcc -o [email?protected] $^ 把所有的o文件编译生成可执行的main文件,$^代表所以的依赖文件集合(main.o test.o test1.o test2.o),@代表目标文件(main) // 例3 lib : test.o test1.o test2.o ar r lib $? 把有更新的依赖文件重新打包到库lib中, 如果只有test1.o更新,则$?代表test1.o, 如果test.o test1.o都有更新,则$?代表test.o test1.o的集合。 ??? 3)伪目标 clean build rebuild all CC := gcc Target := helloworld.out $(Target) : func.o main.o $(CC) -o $(Target) main.o func.o main.o : main.c $(CC) -c main.c -o main.o func.o : func.c $(CC) -c func.c -o func.o .PHONY : rebuild clean build rebuild : clean build build : $(Target) # @echo "build" clean : # @echo "clean" rm *.o $(Target) ??? 在上述Makefile中,clean伪目标作用是将编译链接过程中所有生成文件全部删除,回到make执行的初始状态;build的作用是生成target,与make的作用相同;rebuild伪目标依赖于clean 和 build,当用户输入make rebuild时,实际上等效于make clean ,make build依此执行,build依赖于target,此时会将target重新生成,整个工程因此重新编译链接并生成。all伪目标适用于在同一个makefile中生成多个目标库文件时使用。 ??? 4)函数 ??? GNU make提供了很多的函数,可以在Makefile文件中调用这些函数来进行文件名、变量以及命令等的处理。 (1) patsubst 主要对字符串进行运算和分析 用法:$(patsubst pattern,replacement,text) 功能:将text文本中出现的所有pattern替换为replacement 例子:$(patsubst %.c,%.o,a.c,b.o,c.c) 输出:a.o,c.o (2) dir 主要用于获取文件的路径 用法: $(dir text) 功能: 将text中所有文件的对应目录输出 例子: $(dir main.cpp,libstdc++d) 输出 ./,/usr/lib/ (3)notdir 抽取除去路径意外的其它信息 用法: $(notdir text) 功能: 去除text中所有包含的路径,只留下文件信息 例子: $(notdir /home/perfect/Mywork/C/main.c ./Makefile) 输出: main.c Makefile (4) suffix 获得后缀名 用法:$(suffix text) 功能:将text中所有文件只留下后缀名 例子:$(suffix a.c,b.c) 输出 .c,.c (5) addsuffix 给源目标文件添加前缀 用法:$(addprefix param,text) 功能:将text中每个源文件添加上合适的前缀后缀后输出 例子:$(addprefix -l,$(LIBS)) 输出:完成的库名 (6) wildcard 扩展通配符 用法:$(wildcard PATTERN...) 功能:获取所有复合PATTERN格式的文件 例子:$(wildcard *.c) 输出:当前目录下所有的.c文件 复杂例子:$(patsubst %.c,%.o,$(wildcard *.c)) 首先获取当前路径下所有的.c文件,然后将.c后缀名更改为.o后缀名并返回; ??? 5)include关键字 ??? include命令用于将最新的子Makefile包含进当前Makefile文件,再根据当前Makefile对文件进行编译链接;适用于当系统过大时,Makefile复杂时进行拆分。 ? ??? (6)Makefile简单范例 //clac_test.h
//calc_test.cpp //make_test.h
//make_test.cpp //main.cpp #include "string.h" #include "make_test.h"
using namespace std; int main() { test::MakeTest makeTest;// = new MakeTest();
makeTest.run(); return 0; }
??? 方法1:直接使用g++命令 g++ calc_test.cpp make_test.cpp main.cpp -o test
??? 方法2:直接使用gcc命令 gcc calc_test.cpp make_test.cpp main.cpp -lstdc++ -o test
??? 方法3:全量的Makefile test : main.o calc_test.o make_test.o g++ main.o calc_test.o make_test.o -o test main.o : main.cpp g++ -c main.cpp -o main.o calc_test.o : calc_test.cpp g++ -c calc_test.cpp -o calc_test.o make_test.o : make_test.cpp g++ -c make_test.cpp -o make_test.o main.cpp : make_test.h make_test.cpp : make_test.h calc_test.h calc_test.cpp : calc_test.h .PHONY : clean clean : rm test *.o
??? 方法4:Makefile(隐晦推导) test : main.o calc_test.o make_test.o g++ main.o calc_test.o make_test.o -o test main.o : make_test.h main.cpp g++ -c main.cpp make_test.cpp make_test.o : make_test.h calc_test.h g++ -c make_test.cpp calc_test.cpp calc_test.o : calc_test.h g++ -c calc_test.cpp .PHONY: clean clean: rm test *.o
??? 方法5:Makefile(变量定义) objects = calc_test.o make_test.o main.o test:$(objects) gcc -o test $(objects) -lstdc++ $(objects):calc_test.h make_test.h .PHONY:clean clean: rm test *.o
? ??? (7)Makefile处理多目标文件 ? ? 1)单一Makefile利用伪目标 all ???? 新建文件test.cpp 编译为 temp库 //test.cpp //Makefile all : test temp temp : test.cpp gcc test.cpp -o temp -lstdc++ test : calc_test.cpp make_test.cpp main.cpp g++ calc_test.cpp make_test.cpp main.cpp -o test .PHONY : clean clean : rm test temp ??? 2)当有多个Makefile时,每个Makefile一个目标文件 ??? 新建rapidmain.cpp(当前路径下有rapidxml的所有头文件) //rapidmain.cpp ??? 新建Makefile.rapidxml文件 rapid : rapidmain.cpp g++ -I ./rapidxml/ rapidmain.cpp -o rapid .PHONY : clean rm rapid ???? 新建Makefile.maketest文件 objects = calc_test.o make_test.o main.o test:$(objects) gcc -o test $(objects) -lstdc++ $(objects):calc_test.h make_test.h .PHONY :clean clean: rm test *.o ??? 方法1.新建总Makefile(采用伪目标和make命令) all : make -f Makefile.rapidxml make -f Makefile.maketest ?? 方法2.新建总 Makefile(多个目标时,使用include包含子Makefile) all : test rapid
include Makefile.maketest
include Makefile.rapidxml
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |