Makefile学习笔记
make用起来
- 练习文件准备
// main.c
#include<stdio.h>
#include "input.h"
#include "calcu.h"
int main(int argc, char *argv[])
{
int a, b, num;
input_int(&a, &b);
printf("%d + %d = %d", a, b, calcu(a, b));
return 0;
}
// calcu.c
#include "calcu.h"
int calcu(int a, int b)
{
return (a+b);
}
// input.c
#include<stdio.h>
#include "input.h"
void input_int(int *a, int *b)
{
printf("input two number:");
scanf("%d %d", a, b);
printf("\r\n");
}
// input.h
#ifndef _INPUT_H
#define _INPUT_H
void input_int(int *a, int *b);
#endif
// calcu.h
#ifndef _CALCU_H
#define _CALCU_H
int calcu(int a, int b);
#endif
- 用gcc命令将上面的文件编译成可执行文件
gcc main.c input.c calcu.c -o demo
弊端:用一个gcc命令去将.c文件编译并链接起来,文件少的话问题不大,但是如果一个project有上万个源代码文件的话,一来这么长的gcc命令很操蛋,二来最大的问题就是每一次修改某一个源文件的代码,会导致所有的文件都被重新的编译一遍,而如果文件太多的话,每一次修改一个小的部分,将会导致整个项目又被重新编译一遍,属于是放屁脱裤子——自找麻烦了,项目大的话,小小的改动编译一次十分费力,简直就是吃力不讨好
- 用下面的命令替代上面的命令
gcc -c main.c
gcc -c input.c
gcc -c calcu.c
gcc -o demo main.o input.o calcu.o
使用这个命令之后,就不用使得每一个小改动就去编译全部源文件了,只需要将相应改动的源文件编译一下生成对应的
.o
文件,然后再链接一下就行了,但是问题是,每次还是要敲这么长的命令
- make
为了再次避免上面的问题,于是再Linux下出现了一个make命令,make依据Makefile里面的内容调用gcc对整个项目进行自动化的编译即一次编写,一劳永逸
make的原理
- 对于源文件的修改,只需要将这个源文件重新编译一次再链接生成一个新的可执行文件即可
- 对于头文件的修改,就编译所有引用到这个头文件的模块
# Makefile怎么写
demo: main.o input.o calcu.o
gcc -o demo main.o input.o calcu.o
main.o: main.c
gcc -c main.c
input.o: input.c
gcc -c input.c
calcu.o: calcu.c
gcc -c calcu.c
clean:
rm *.o
rm ./demo
写好Makefile之后,在当前文件夹下打开shell,输入make命令,就可以达到自动化编译的效果
怎么用好make
用好make就要掌握Makefile的一些写法
Makefile的组成
目标: 依赖
shell命令
......
makefile就是由目标,依赖,命令组成的,一个Makefile一定会由一个最终目标,一般这个最终目标就是生成一个可执行文件,而默认情况下这个最终目标就是第一个,然后其它的辅助目标可以乱序,顺序不用考虑
main: main.o input.o calcu.o
gcc -o demo main.o input.o calcu.o
上面一条makefile语句就是,main这个目标依赖于
main.o input.o calcu.o
这三个文件,调用这个目标就是执行这个目标语句下面的一系列shell语句,这个目标下面的语句就是,将main.o input.o calcu.o链接起来,生成可执行文件demo
Makefile变量
Makefile里面的变量全部是字符串
# 定义变量
var = value
# 使用变量
$(var)
怎么用变量
# 前面的makefile可以简化成
obj = main.o input.o calcu.o
main: $(obj)
gcc -o demo $(obj)
赋值语句
# =
value = hello
currval = $(value)
value = hello1
main:
echo $(currval) # 输出hello1
# :=
value = hello
currval := $(value)
value = hello1
main:
echo $(currval) # 输出hello
# ?=
value ?= hello # 如果value已经被定义了,那么就不操作,如果没有,则value=hello
# +=
value = hello
value += world # 追加
模式规则
main.o: main.c
gcc -c main.c
input.o: input.c
gcc -c input.c
calcu.o: calcu.c
gcc -c calcu.c
# 上面的操作太复杂,可以这样替代
%.o: %.c # %代表匹配所有字符
gcc -c $<.c
a%.o: a%.o # 所有a开头的.o文件依赖于a开头的.c文件
自动化变量
自动化变量 | 说明 |
---|---|
$@ | 表示规则的目标文件名。如果目标是一个文档文件(Linux 中,一般成 .a 文件为文档文件,也成为静态的库文件), 那么它代表这个文档的文件名。在多目标模式规则中,它代表的是触发规则被执行的文件名。 |
$% | 当目标文件是一个静态库文件时,代表静态库的一个成员名。 |
$< | 规则的第一个依赖的文件名。如果是一个目标文件使用隐含的规则来重建,则它代表由隐含规则加入的第一个依赖文件。 |
$? | 所有比目标文件更新的依赖文件列表,空格分隔。如果目标文件时静态库文件,代表的是库文件(.o 文件)。 |
$^ | 代表的是所有依赖文件列表,使用空格分隔。如果目标是静态库文件,它所代表的只能是所有的库成员(.o 文件)名。 一个文件可重复的出现在目标的依赖中,变量“ ” 只 记 录 它 的 第 一 次 引 用 的 情 况 。 就 是 说 变 量 “ ^”只记录它的第一次引用的情况。就是说变量“ ”只记录它的第一次引用的情况。就是说变量“^”会去掉重复的依赖文件。 |
$+ | 类似“$^”,但是它保留了依赖文件中重复出现的文件。主要用在程序链接时库的交叉引用场合。 |
$* | 在模式规则和静态模式规则中,代表“茎”。“茎”是目标模式中“%”所代表的部分(当文件名中存在目录时, “茎”也包含目录部分)。 |
伪目标
.PHONY:目标名称
# 没有.PHONY
clean:
rm *.o
rm ./demo
# 如果当前文件夹下由clean这个文件,执行make clean就会报错
# 有.PHONY
.PHONY:clean
clean:
rm *.o
rm ./demo
# 就算有clean文件,执行make clean也不会报错
在命令前面加@
# 在命令前面加@代表在执行这条命令是不会在shell窗口上输出我执行了什么
zouren@ubuntu:~/Cmake_Learn$ make
echo hello1 # 没加@,显示执行的命令
hello1
zouren@ubuntu:~/Cmake_Learn$ make
hello1 # 加了@,不显示执行的命令
zouren@ubuntu:~/Cmake_Learn$