简介
- make,即make工程管理器
- Linux下的管理编译的工具(编译好了=代码可以运行了=工程管理好了)
- Linux没有IDE(集成开发环境)用于代码编写编译和运行
- 直接拿着gcc搞有点难受,相当于用
make+Makefile
封装了一下
- 通过读入 Makefile文件(规则),执行编译工作
make
命令是就是执行这个文件里的语句- make 可以理解成一种编程语言(python .py)
- 优点
- 对于维护一些具有相互依赖关系的文件特别有用(自动关联)
- 自动确定需要重新编译的文件,不做无用功,提高效率
- 起初适用于C语言,但只要提供相应的编译器,mM可以满足你的所有幻想(管理编译器的工具)
- 一般直接拿着gcc搞是怎样的过程呢?
- 准备几个文件:add.c add.h sub.c sub.h main.c
# main.c #include<stdio.h> #include "add.h" #include "sub.h" int main(){ int a,b; printf("请输入a和b的值:"); scanf("%d%d",&a,&b); printf("%d + %d = %d\n",a,b,add(a,b)); printf("%d + %d = %d\n",a,b,sub(a,b)); return 0; } # add.c int add(int a, int b){ return a+b; } # sub.c int sub(int a, int b){ return a-b; } # add.h int add(int, int); # sub.h int sub(int, int);
- 执行命令
gcc -c main.c gcc -c add.c gcc -c sub.c # 生成.o文件 # 生成可执行文件 gcc *.o -o calc.exe ./calc.exe
- 准备几个文件:add.c add.h sub.c sub.h main.c
- 以上就是使用gcc的完整过程,很麻烦吧,使用Makefile直接写好,make执行
- 详细的gcc过程可以参看这里,主要是预处理-编译-汇编-链接
机制
- make命令参数
- make 使用格式:
make [options] [target]
- make命令的执行过程分析
- make命令的执行过程分析
目标
- Makefile基本语法格式
target:relys command ... # target 目标文件,多个文件的话以空格分开,make后会生成这个名字的文件 # relys 生成目标所依赖的文件 # command 命令行,这里的命令和/bin/bash类似,很多也是直接交给shell执行,所以... # command可以跟人rely后使用分号隔开,也可以换行+Tab(必须用Tab!!!) # 一行太长都可以用 \ 换行
- 就是依靠一个接一个目标完成编译过程的
- 程序片段,说明语法
all : main.o keys.o gcc main.o -o all # 使用gcc编译,生成all文件,必须Tab # 可以使用通配符 * ? [...] clean : rm -f *.o # 删除所有当前的.o文件 # 或者直接使用 $(RM) $(OBJS) $(TARGET) # 这里的clean是伪目标,后面没有依赖并不会直接生成clean文件 # 为了防止和目标文件重名,可以 .PHONY : clean # 伪目标特性:总是被执行,所以比较新,如下: all : p1 p2 p3 .PHONY : all p1 : xxx xxx p2 : ... # 这里all被定义为伪目标,每次重新编译会被执行而且最新,所以后面的依赖也会重新编译! # 赋值 objects = *.o # 这里是单纯的将 *.o 给变量,不展开 # 如果要展开 objects := $(wildcard *.o) # 这样赋值; wildcard是mk的关键字
- 伪目标一般会跟着make命令一起,例如:
make clean all
,如果不指定就从头到尾执行 - clean删除已生成的编译文件,all刷新(重编译)
# make 会按顺序执行Mf中的文件 # 如果要让上一条命令的结果应用于下一条,必须使用分号 exec : cd /home/dev pwd # 这里显示的还是Mf所在的目录 # 应该这样 exec : - cd /home/dev; pwd # 有点像管道 # - 作用是不管是否出错都认为是成功的,不中断执行
- 伪目标一般会跟着make命令一起,例如:
- 具体说说,Makefile文件包含的内容:
- 显式规则,生成目标的格式
- 隐式规则,自动编译依赖文件,得到.o
- 变量定义,函数调用
- 文件引用,引用其他Makefile,
include <filename>
- 注释
变量
- 作为语言,自然有变量/流程控制等等
- 变量命名规则和其他语言类似,但是可以数字开头
- 声明变量必须赋初值,使用时需要加上
$
符号,可以使用()/{}
括起来objects = main.o test.o fun.o program : $(objects) cc -o p1 ${objects}
- 如果以
:=
赋值,代表只能使用之前声明的变量y := ${x}bar x := foo all : echo ${y} # 输出的是bar,而不是foobar
- 类似的,
?=
表示如果之前有这个变量就啥也不做,如果没有就按现在这么定义foo ?= hello.o # 相当于带了条件判断语句
- 使用
+=
追加objects = main.o test.o objects += hello.o # 会继承之前的赋值操作符,例如之前使用 := ,加上之后就还是 :=
- 几个特殊的变量
- 类似简介部分的例子,编写Makefile
# first_make # $^ 依赖项,不重复 # $@ 目标名称 # @ 不在控制台显示此编译命令 # - 失败后继续执行后面的代码 TARGET=first_make LIBS=-lpthread # 库,一般都叫这个名 $(TARGET):first_make.cpp xdata.cpp -@rm test @$(CXX) $^ -o $@ $(LIBS) echo "$(TARGET) build success!" # 代码区间 @# 代码区间的注释也要加@,不然也会显示出来
预定义变量
- 预定义变量可以简化Makefile文件
CXX
和CXXFLAGS
常用到,对于使用c++程序而言
自动推导
- 也叫隐式规则
- 还是以上面的代码为例,我们可以不写依赖项(first_make.cpp xdata.cpp),make可以自动推导,编译出对应的.o文件
- 但是,例如当我们的代码中使用了其他路径下的.h文件,需要使用
CXXFLAGS
,指明参数,改进如下:TARGET=first_make LIBS=-lpthread # 库,一般都叫这个名 OBJS=first_make.o xdata.o CXXFLAGS=-I../test.h # -I 选项 指明头文件路径 $(TARGET):$(OBJS) -@rm test @$(CXX) $^ -o $@ $(LIBS) echo "$(TARGET) build success!" # 代码区间
- 其实可以发现,这种推导很有限,直说得了!
函数
- 格式;这里都是调用,没说定义,记着第一个是函数名就行,可以再查
$(func_name arg1,arg2,arg3...) # 也可以是 {},不同 参数都要逗号隔开的
- 字符串替换函数
$(subst <from>, <to>, <text>) # 将text中的from替换成to,返回被替换的字符串
- 查找字符串函数
$(findstring <find>, <in>) # 在字符串in中查找find
- 过滤函数
$(filter <pattern>, <text>) # 过滤出符合pattern规则的字符串 # 例子 foo := main.c test.c hello.s fuck.h source : $(foo) cc $(filter %.c %.s, $(foo)) -o source # 要用()就都用()
filter-out
函数返回去除掉符合pattern的字符串
- 排序函数
$(sort <list>) # 按字母排序list中的元素
- 取单词函数
$(word <n>, <text>) # 取text中第n个单词 # 取单词串 $(wordlist <s>,<e>,<text>) # 取出text中从s个到e个的单词,单词计数都是从1开始 # 单词个数统计函数 $(words <text>) # 取首单词函数 $(firstword <text>)
- 取目录函数
$(dir <dirs>) # 例子 $(dir /usr/local/bin/nginx hello.c) # /usr/local/bin ./ hello是当前目录下的,所以返回./ # 取文件名函数 $(notdir <names>) # 例子 $(dir /usr/local/bin/nginx, hello.c) # nginx hello.c # 取后缀名函数 $(suffix <names>) # 如果文件没有后缀名返回空字符 # 取前缀函数 # 例子 $(basename usr/local/bin/nginx hello.c /etc/yum.repo.d) # usr/local/bin/nginx hello /etc/yum.repo
- 类似的,有加后缀加前缀函数
- 循环函数
names := a,b,c,d files := $(foreach i, $(names), $(i).c) # 得到a.c b.c c.c d.c # 类似python的for i in some,这里将表达式直接放到最后而已
- 条件判断函数
# 格式 $(if <condition>, <then-part>, <else-part>) # 只有一个part会被执行
链接库
- 包括动态库和静态库,以项目说明
- 先准备动态库的文件,xthread
- 使用
-shared
参数编译成动态库,编写Makefile文件:
LDFLAGS
指定链接选项,LD
指定链接器,一般是ld
- 使用编译的动态库,在xserver中,编译时指定链接的动态库(LIBS),运行时还要指定动态库路径(位置)
- 静态库:待续
小结
- make是管理编译器的语言,使用该语言编写Makefile文件,即可按规则编译,得到需要的目标文件(可执行文件)