Make:
工程管理器,也叫“自动编译管理器”,
“自动”的意思是指它能够根据文件更改的时间自动发现更新过的文件而减少编译的工作量,
同时,它通过读入Makefile文件的内容来执行大量的编译工作
Makefile基本结构
是Make读入的唯一配置文件
由make工具创建的目标体(target),通常是目标文件或可执行文件
要创建的目标体所依赖的文件(dependency_file)
创建每个目标体时需要运行的命令(command)
注意:命令行前面必须是一个”TAB键”,否则编译错误为:*** missing separator.Stop.
makefile基本格式:
target:dependency_files
<TAB> command
例如:
hello.o :hello.c hello.h
gcc –c hello.c –o hello.o
makefile变量
main:led.o pwm.o
gcc led.o pwm.o -o main
kang.o : pwm.c pwm.h
gcc –Wall –O -g –c pwm.c -o pwm.o
led.o : led.c led.h
gcc - Wall –O -g –c led.c -o led.o
.PHONY: clean 伪目标,防止出现与clean同名的文件
clean:
rm *.o test 删除.o文件和test文件
-Wall:编译时发出所有有用的信息
-c:只是编译不链接,生成目标文件 .o
-o file :把输出文件输出到file里(man手册查看更多)
创建和使用变量:
创建变量的目的:
用来代替一个文本字符串:
1.系列文件的名字
2.传递给编译器的参数
3.需要运行的程序
4.需要查找源代码的目录
5.你需要输出信息的目录
6.你想做的其它事情
---------定义变量的方式:----------
OBJS = led.o pwm.o
CC = gcc
CFLAGS = -Wall -O -g
main: $(OBJS)
$(CC) $(OBJS) -o main
kang.o : pwm.c pwm.h
$(CC) $(CFLAGES) pwm.c -o pwm.o
led.o : led.c led.h
$(CC) $(CFLAGES) led.c -o led.o
---------递归展开方式:----------
foo = $(bar)
bar = $(ugh)
ugh = Huh?
优点:可以向后引用变量
缺点:不能对该变量进行任何扩展:CFLAGES = $(CFLAGES) -这个会一直死循环
---------简单方式:---------------
VAR:=var
m := mm
x := $(m)
y := $(x) bar
x := later
echo $(x) $(y)
这种方式定义的变量,会在变量的定义点,按照被引用的变量的当前值进行展开
这种定义变量的方式更适合在大的编程项目中使用,因为它更像我们一般的编程语言
-------------------------------------
------用 ?= 定义变量-----------
dir := /foo/bar
FOO ?= bar 查看FOO是否被定义过,没有则定义为bar,否则什么都不做
FOO是?
-------为变量添加值---------------
使用 += 为已知的变量添加新的值
Main = demo1.o demo2.o
Main += demo3.o 相当于Main = demo1.o demo2.o demo3.o
预定义变量
AR 库文件维护程序的名称,默认值为ar。AS汇编程序的名称,默认值为as。
CC C编译器的名称,默认值为cc。
CPP C预编译器的名称,值为$(CC) –E。
CXX C++编译器的名称,默认值为g++
FC FORTRAN 编译器的名称,默认值为 f77
RM 文件删除程序的名称,默认值为 rm -f
------------------例如:----------------------
Main = led.c led.h
$(CC) -o Main led.c
clean:
$(RM)Main
-----------------------------------
ARFLAGS 库文件维护程序的选项,无默认值。
ASFLAGS 汇编程序的选项,无默认值。
CFLAGS C编译器的选项,无默认值。
CPPFLAGS C预编译的选项,无默认值。
CXXFLAGS C++编译器的选项,无默认值。
FFLAGS FORTRAN编译器的选项,无默认值。
-----------------------例如-----------------------------
OBJS = led.o pwm.o
CC = gcc
CFLAGS = -Wall -O -g
main: $(OBJS)
$(CC) $(OBJS) -o main
kang.o : pwm.c pwm.h
$(CC) $(CFLAGES) pwm.c -o pwm.o
led.o : led.c led.h
$(CC) $(CFLAGES) led.c -o led.o
--------------------------------------------------------
自动变量
$* 不包含扩展名的目标文件名称
$+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件
$< 第一个依赖文件的名称
$? 所有时间戳比目标文件晚的依赖文件,并以空格分开
$@ 目标文件的完整名称
$^ 所有不重复的目标依赖文件,以空格分开
$% 如果目标是归档成员,则该变量表示目标的归档成员名称
------------------例子--------------------
OBJS = led.o pwm.o
CC = gcc
CFLAGS = -Wall -O -g
main: $(OBJS)
$(CC) $^ -o $@
kang.o : pwm.c pwm.h
$(CC) $(CFLAGES) -c $< -o $@
led.o : led.c led.h
$(CC) $(CFLAGES) -c $< -o $@
------------------------------------------------
环境变量
make在启动时会自动读取系统当前已经定义了的环境变量,并且会创建与之具有相同名称和数值的变量
如果用户在Makefile中定义了相同名称的变量,那么用户自定义变量将会覆盖同名的环境变量
Make的使用
直接输入make
后面可跟选项
-C dir读入指定目录下的Makefile
-f file读入当前目录下的file文件作为 Makefile
-i 忽略所有的命令执行错误,比如很多文件一起编译,全部走一遍看具体哪些文件有错
-I dir指定被包含的Makefile 所在目录
-n 只打印要执行的命令,但不执行这些命令
-p 显示make变量数据库和隐含规则
-s 在执行命令时不显示命令
-w 如果make在执行过程中改变目录,打印当前目录名
Makefile的隐含规则
编译C程序的隐含规则:
name.o 的目标的依赖目标会自动推到 name.o ,并且其生成命令是
$(CC) -c $(CFLAGES) $(CFLAGES)
链接文件的隐含规则:
name 目标依赖于 name.o ,通过运行C的编译器来运行链接程序生成(一般是ld),
其生成命令是:$(CC) $(LDFLAGES) name.o $(LOADLIBES) $(LDLIBS) . 这个规则对于
只有一个源文件的工程有效,同时也对多个Object 文件(由多个不同的源文件生
成)也 有效
-----------------例如----------------------
x : x.o y.o z.o
–并且“x.c”、“y.c”和“z.c”都存在时,隐含规则将执行如下命令:
–cc -c x.c -o x.o
–cc -c y.c -o y.o
–cc -c z.c -o z.o
–cc x.o y.o z.o -o x
–如果没有一个源文件(如上例中的x.c)和你的目标名字
(如上例中的x)相关联,那么你最好写出自己的生成规则,不然,隐含规
则会报错的。
Makefile的VPATH:虚路径
在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的
源文件分类,并存放在不同的目录中。所以,当make需要去找寻文
件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个
路径告诉make,让make在自动去找。
所以特殊变量VPATH 就是完成这个功能的,如果没有指明路径,
make会在当前路径下起找依赖文件和目标文件,如果定义了这个变量
make就会从当前路径再到指定的路径的查找
写法:
VPATH = src:../header 指当前目录的src 和上一级目录的header
上面的定义指定两个目录,src 和 ../header make会按照这个顺序进
行搜索,目录由冒号分隔
Makefile的嵌套
@echo $(SUBDIRS) 打印信息,但不提示
@(RM) 删除
make -C $@ 读入指定目录下的Makefile开始编译生成目
标的完整名称
export CC OBJS BIN OBJS_DIR BIN_DIR 把这些变量传递
给子目录
Makefile模板:
CC = gcc 定义gcc
SUBDIR = f1 \ 定义所有文件路径
f2 \
main \
obj
OBJS = f1.o f2.o main.o 定义所有 .o 文件变量
BIN = myapp 定义bin名字
OBJS_DIR = obj 定义obj目录
BIN_DIR = bin 定义bin目录
export CC OBjS BIN OBJS_DIR BIN_DIR 传递变量
all:CHECK_DIR $(SUBDIRS)
CHECK_DIR:
mkdir -p $(BIN_DIR) 创建bin文件夹
$(SIBDIR):ECHO
make -C $@ 开始去各个目录编译
ECHO: 打印
@echo $(SUBDIRS) 打印SUBDIR但不提示,加上@就是不提示
@echo begin compile 打印begin compile
clean:
@$(RM) $(OBJS_DIR)/*.o 删除OBJS_DIR目录下的 .o 文件
@rm -rf $(BIN_DIR) 删除bin文件夹
f1的Makefile
../$(OBJS_DIR)/fi.o : fi.c 把f1.c生成的.o 文件放到上一级目录的 OBJS_DIR 下,也就是obj文件夹
$(CC) -c $(^) -o $@ gcc 编译所有目标文件生成目标文件完整名称的 .o 文件
main的Makefile
../$(OBJS_DIR)/main.o : main.c
$(CC) -c $^ -o $@
obj链接文件的Makefile(把.o文件链接起来放入bin文件夹下)
../$(BIN_DIR)/$(BIN):func1.o func2.o func.o
$(CC) func1.o func2.o func.o -o ../$(BIN_DIR)/$(BIN)