【参考资料】
【1】《跟我一起写Makefile》
【2】https://www.cnblogs.com/wanghuaijun/p/8092747.html
【3】https://www.ibm.com/developerworks/cn/linux/l-makefile/
【4】http://www.laruence.com/2009/11/18/1154.html
备注:项目需要重新梳理下Makefile知识点,主要是《跟我一起写Makefile》,该文写得非常清楚,上一次看份文档的时候都快十年,当时的笔记也没了,很多事情真是恍若隔世。
笔记最后增加了一些autoconf和automake是参考其他资料
自动化编译依赖于IDE内部的make工具,例如Delphi的make、Visual C++的nmake和Linux GNU的make。它们解析了makefile里的命令。
编译: 把源文件变成中间文件,例如windows下的obj,Unix下的.o
链接: 把大量的中间文件变成执行文件
库文件: 把一部分中间目标文件打包,例如windows下的lib和Unix下的.a
target ... : prerequisites ...
command
target: 目标文件,可以是obj文件,也可以是执行文件
prerequisites: 生成target所需要的目标
command: make需要执行的命令
edit: main.o kbd.o
cc -o edit main.o kbd.o
main.o: main.c main.h
cc -c main.c
kbd.o: kbd.c defs.h command.h
cc -c kbd.c
clean:
rm edit main.o kbd.o
object = main.o kbd.o
#这里使用$(object)这个变量来替代main.o kbd.o
edit: $(object)
内容 | 功能 |
---|---|
显示规则 | 如何生成一个或多个目标文件 |
隐晦规则 | makefile的自动推导机制 |
变量定义 | 用于引用的字符串变量 |
文件指示 | makefile之间的引用包含,类似include |
注释 | 与shell脚本一样采用# |
include foo.make *.mk $(bar)
#这里$(bar)代表e.mk、f.mk,因此等价于
include foo.make a.mk b.mk c.mk e.mk f.mk
makefile引用的路径规则:
- make命令时有-I或者–include-dir,在此参数下查找
- 如果存在/include,make也会去查找
- 可以用-include 来忽略找不到引用的错误
foo.o: foo.c defs.h #foo 模块
cc -c -g foo.c
通配符 | 作用 |
---|---|
* | 代替零个、单个或多个字符 |
? | 代替单个字符,其中$?代表比目标还要新的文件列表 |
[…] | 匹配括号内的任一字符 |
VPATH=src:../headers
#定义了src和../headers两个目录,make在此目录下搜索
#1. 为符合pattern规则的文件制定搜索路径
#vpath
vpath %.h ../headers
#2. 清除符合pattern规则的文件设置好的搜索目录
#vpath
#3. 清除所有设置好的搜索目录
#vpath
例如clean不是真正意义上的目标,而是一个标签。通常可以加
.PHONY: clean
clean:
rm *.o temp
也可以利用伪目标实现多目标生成,例如:
all: prog1 prog2 prog3
.PHONY : all
#注意:伪目标本身也可以作为依赖
多个目标同时依赖于一个文件,例如
bigoutput littleoutput : text.g
generate text.g -$(subst output, ,$@) > $@
#此处bigoutput和littleoutput都依赖于text.g
#上述规则等价于
bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput
#上面$@表示目标的集合,-$(subst output, ,$@)表示执行makefile的一个函数,函数名是subst,
#是用来截取字符串,把bigoutput中的output去掉
语法规则如下
: :
objects = foo.o bar.o
all: $(objects)
#下面的%就是上面的foo bar
$(objects) : %.o: %.c
# $< 表示所有依赖集,即foo.c bar.c
$(CC) -c $(CFLAGS) $< -o $@
#上面规则等价如下
foo.o: foo.c
$(CC) -c $(CFLAGS) foo.c -o foo.o
bar.o: bar.c
$(CC) -c $(CFLAGS) bar.c -o bar.o
cc -M main.c
#-M会自动把依赖的头文件加进来,实际输出
main.o: main.c defs.h
#注意:若使用GNU,则必须使用-MM参数,否则会增加一系列系统头文件
@echo 正在编译…
显示 “正在编译…”
echo 正在编译…
显示 echo 正在编译… 正在编译…
make参数加 -n 或者-just-print 只显示命令
make参数加 -s 或者-slient 全面禁止命令显示
如果下一条命令要用到上一条命令的结果,则用;分割,例如
cd /home/test;pwd
#此时pwd打印的是cd 命令的结果,否则则是当前makefile的目录
make加-i或者-ignore-errors,表示忽略全部错误
make加-k或者-keep-going,表示中止当前出错规则,执行下一规则
#用subsystem关键字
subsystem:
cd subdir && $(MAKE)
#等价于
subsystem:
$(MAKE) -C subdir
#传递variable这边变量
export variable = value
#不传递某个变量
unexport variable
#类似宏定义
define run-yacc
yacc $(firstword $^)
mv y.tab.c$@
#endif
#使用时
$(run-ycc)
变量采用 ( ) 或 者 ()或者 ()或者()包起来,用$ 表 示 真 实 的 表示真实的 表示真实的
++注意:Makefile里用shell变量必须是${}++
ifeq (0, ${MAKELEVEL})
curdir := $(shell pwd)
whoami := $(shell whoami)
host-type := $(shell arch)
MAKE := ${MAKE} hosttype=${host-type} whiami=${whoami}
endif
= 表示: 基本的赋值操作
:= 表示: 覆盖之前的值
?= 表示: 如果之前没有赋值,则用等号后的值
+= 表示: 追加等号后的值
x = foo
y = $(x) bar
x = xyz
#这个例子里y是xyz bar
x := foo
y := $(x) bar
x := xyz
#这个例子里y是foo bar
foo := a.o b.o c.o
bar := $(foo: .o = .c)
#把.o后缀变成.c后缀
x=y
y=z
$(a) := $($(x))
#此时$(a)的值是z
主要是ifeq、ifneq、ifdef三个。
其中ifdef时若变量值为非空,则表达式真,例如:
bar =
foo = $(bar)
ifdef foo
froboss = yes
else
froboss = no
endif
语法: $(subst, , , )
功能: 把text中的from换成to,返回结果
语法: $(patsubst, , , )
功能: 模式字符串替换
$(patsubst, %.c, %.o, x.c, bar.c)
#将x.c, bar.c换成x.o,bar.o
语法: $(strip )
功能: 去掉开头和结尾的空格
语法: $(findstring, , )
功能: 在in里找find字符串,找到则返回find,否则返回空字符串
语法: $(filter
功能: 以pattern模式过滤text字符串,返回符合模式的字符串
sources := foo.c bar.c baz.s gh.h
foo: $(sources)
cc $(filter %c %.s, $(sources)) -o foo
#这里gh.h被过滤掉
语法: $(filter-out
功能: 反过滤,与fileter相反
语法: $(sort )
功能: 返回排序后的字符串
$(sort foo bar lose) #返回bar foo lose
语法: $(word , )
功能: 取text中的第n个单词
语法: $(word-list , , )
功能: 取text中的从第s个到第e个单词
语法: $(words )
功能: 返回text包含的单词数
语法: $(firstwords )
功能: 返回text中第一个单词
注意:下面的语法中names代表多个
语法: $(dir
功能: 取出name中的文件路径部分,即最后一个反斜杠之前的部分
语法: $(nodir
功能: 取出name中文件部分,与dir相反
语法: $(sufix
功能: 取出names中的后缀部分
语法: $(basename
功能: 取出names中的前缀部分
语法: $(addsuffix ,
功能: 增加后缀
语法: $(addprefix ,
功能: 增加前缀
语法: $(join ,)
功能: 逐次拼接list1和list2
$(join aaa bbb, 111 222 333)
#返回aaa111 bbb222 333
names := a b c d
files := $(foreach n, $(names), $(n).o)
#执行结束后a.o b.o c.o d.o
用call来向表达式传递参数,例如
reverse = $(1) $(2)
foo = $(call reverse, a, b)
#此时foo的值是a b,理解是call调用了reverse,调用时参数是a,b,然后返回结果
执行系统的shell命令
files := $(shell echo *.c)
makefile的错误输出,其中error的输出会让makefile停止
$(error
$(warning
目标 | 功能 |
---|---|
all | 编译所有目标 |
clean | 删除make所有创建的文件 |
install | 安装已经编译好的程序 |
列出改变过的源文件 | |
tar | 打包tar文件 |
dist | 把tar压缩成Z文件或者gz文件 |
autoconf、automake工具帮助程序员快速生成一个符合开源规范的Makefile文件,用户只需要采用./confiure, make make, install命令就可以进行编译、安装。
准备一个空的文件里,创建一个c文件如下:
#include
int main(int argc, char** argv){
printf("%s", "Hello, Linux World!\n");
return 0;
}
autoscan命令产生autoscan.log和configure.scan两个文件,其中configure.scan是autoscan根据目录下源码生成的一个configure模板。在此我们将configure.scan更名为configure.in,并做修改如下:
aclocal命令: 生成aclocal.m4,包含后续automake要用到的一些其他宏定义
autoconf命令: 产生configure这个执行文件
后续automake会根据此文件创建Makefile.in,填写内容如下
UTOMAKE_OPTIONS=foreign
bin_PROGRAMS=helloworld
helloworld_SOURCES=helloworld.c
执行命令automake --add-missing
错误1:
Makefile.am: error: required file ‘./NEWS’ not found
Makefile.am: error: required file ‘./README’ not found
Makefile.am: error: required file ‘./AUTHORS’ not found
Makefile.am: error: required file ‘./ChangeLog’ not found
则提前创建上述文件即可
错误2:
/usr/share/automake-1.15/am/depend2.am: error: AMDEP does not appear in AM_CONDITIONAL
安装libtool即可 apt install libtool
这个步骤中configure命令将上一步中的Makefile.in变成符合标准的Makefile。
这步就是常规意义上的make,最终会生成执行文件helloworld。