Linux基础(三)

一、makefile简介

makefile定义了一系列的规则来指定如何编译文件,makefile带来的好处就是"自动化编译”,一旦写好只需要一个make命令,整个工程就完全自动化编译,极大的提高了软件开发的效率;

make主要解决了两个问题:

1.大量代码的关系维护

2.减少重复编译时间

makefile: 管理项目。

命名:makefile Makefile --- make 命令

1 个规则 2 个函数 3 个自动变量

 二、makefile的1个规则

目标:依赖条件
    (一个tab缩进)命令

hello:hello.c
	gcc hello.c -o hello

1.目标的时间必须晚于依赖条件的时间,否则,更新目标。

2.依赖条件如果不存在,找寻新的规则去产生依赖条件。

新建hello.c文件如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc,char *argv[])
{
        printf("hello world\n");
        return 0;
}

新建makefile如下:

hello:hello.o
        gcc  hello.o -o hello

hello.o:hello.c
        gcc -c hello.c -o hello.o

在终端执行make命令生成hell.o文件及hello可执行文件,再执行以下命令得到输出:

./hello

out:
hello world

 修改hello.c如下所示:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int add(int ,int);
int sub(int ,int);
int div1(int ,int);

int main(int argc,char *argv[])
{
        int a = 10;
        int b = 5;
        printf("%d + %d = %d\n",a,b,add(a,b));
        printf("%d - %d = %d\n",a,b,sub(a,b));
        printf("%d / %d = %d\n",a,b,div1(a,b));
       
        return 0;
}

新建add.c、sub.c、div1.c、mul.c分别如下所示:

//add.c
int add(int a,int b)
{
        return a+b;
}

//sub.c
int sub(int a,int b)
{
        return a-b;
}

//div1.c
int div1(int a,int b)
{
        return a/b;
}

首先通过多文件联合编译生成可执行文件a.out,执行以下命令:

gcc add.c sub.c div1.c -o a.out

再执行./a.out可得出结果

其次通过makefile直接生成可执行文件,在makefile中写入:

a.out:hello.c add.c sub.c div1.c
    gcc hello.c add.c sub.c div1.c -o a.out

make之后打印:

gcc hello.c add.c sub.c div1.c -o a.out

再执行a.out同样可以得出结果

将add.c进行修改如下:

int add(int a,int b)
{
        return a+b+1;
}

此时再次使用makefile,发现:只修改add.c,但是编译的时候,其他.c文件也重新编译了

明明只改了一个,全部都重新编译了,于是将makefile改写如下:

a.out:hello.c add.c sub.c div1.c
        gcc hello.o add.o sub.o div1.o -o a.out

add.o:add.c
        gcc -c add.c -o add.o
sub.o:sub.c
        gcc - sub.c -o sub.o
div1.o:div1.c
        gcc -c div1.c -o div1.o 
hello.o:hello.c
        gcc -c hello.c -o hello.o

并且修改div1.c如下:

int div1(int a,int b)
{
        return a/b-1;
}

运行之后可以发现,仅仅是对div1.c重新编译        

makefile检测原理:

  • 修改文件后,文件的【修改时间】发生变化,会出现目标文件的时间早于作为依赖材料的时间,出现这种情况的文件会重新编译。
  • 修改sub.c后,sub.o的时间就早于sub.c ,a.out的时间也早于sub.o的时间了,于是重新编译这俩文件了。

ALL来指定终极目标

  • makefile的依赖是从上至下的,换句话说就是目标文件是第一句里的目标,如果不满足执行依赖,就会继续向下执行。如果满足了生成目标的依赖,就不会再继续向下执行了。
  • make会自动寻找规则里需要的材料文件,执行规则下面的行为生成规则中的目标。

关于makefile指定目标问题,先修改makefile如下: 

hello.o:hello.c
        gcc -c hello.c -o hello.o

add.o:add.c
        gcc -c add.c -o add.o
sub.o:sub.c
        gcc - sub.c -o sub.o
div1.o:div1.c
        gcc -c div1.c -o div1.o 
a.out:hello.c add.c sub.c div1.c
        gcc hello.o add.o sub.o div1.o -o a.out

执行make,得到如下结果:

 gcc -c hello.c -o hello.o

这是因为,makefile默认第一个目标文件为终极目标,生成就跑路,这时候可以用ALL来指定终极目标,修改如下:

ALL:a.out

hello.o:hello.c
        gcc -c hello.c -o hello.o

add.o:add.c
        gcc -c add.c -o add.o
sub.o:sub.c
        gcc - sub.c -o sub.o
div1.o:div1.c
        gcc -c div1.c -o div1.o 
a.out:hello.c add.c sub.c div1.c
        gcc hello.o add.o sub.o div1.o -o a.out

三、makefile的两个函数和clean

src = $(wildcard *.c)

匹配当前工作用户下的所有.c文件。将文件名组成列表,赋值给变量src。

obj = $(patsubset %.c,%.o, $(src))

将参数3中,包含参数1的部分,替换成参数2。即把src变量里所有后缀为.c的文件替换成.o

模拟执行加了clean部分的makefile,如下所示:

src = $(wildcard *.c)   #add.c sub.c div1.c hello.c
obj = $(patsubst %.c, %.o, $(src))  #add.o sub.c div1.o hello.o

ALL:a.out

hello.o:hello.c
        gcc -c hello.c -o hello.o

add.o:add.c
        gcc -c add.c -o add.o
sub.o:sub.c
        gcc - sub.c -o sub.o
div1.o:div1.c
        gcc -c div1.c -o div1.o 
a.out:$(obj)
        gcc $(obj) -o a.out

clean:
        -rm -rf $(obj) a.out 

rm前面的-,代表出错依然执行。

删除不存在文件不加这-,就会报错,告诉你有一个文件找不到。加了-就不会因为这个报错。

由于没有文件变动,a.out的时间戳晚于所有依赖文件,这里make就没干活

于是,执行时加新指令,先模拟执行clean部分

make clean -n
需要删除则执行:
make clean

 四、makefile3个自动变量和模式规则

3个自动变量:

 $@ :在规则命令中,表示规则中的目标
 $< :在规则命令中,表示规则中的第一个条件,如果将该变量用在模式规则中,它可以将依赖条件列表中的依赖依次取出,套用模式规则
 $^ :在规则命令中,表示规则中的所有条件,组成一个列表,以空格隔开,如果这个列表中有重复项,则去重

用自动变量修改makefile,如下:

src = $(wildcard *.c)   #add.c sub.c div1.c hello.c
obj = $(patsubst %.c, %.o, $(src))  #add.o sub.c div1.o hello.o

ALL:a.out

a.out:$(obj)
        gcc $^ -o $@

add.o:add.c
        gcc -c $< -o $@
sub.o:sub.c
        gcc -c $< -o $@
div1.o:div1.c
        gcc -c $< -o $@ 
hello.o:hello.c
        gcc -c $< -o $@ 

clean:
        -rm -rf $(obj) a.out 

上面这个makefile,可扩展性不行。

比如,要添加一个乘法函数,就需要在makefile里面增加乘法函数的部分。不科学,所以,模式规则就来了

%.o:%.c
	gcc -c $< -o $@

修改makefile如下:

src = $(wildcard *.c)   #add.c sub.c div1.c hello.c
obj = $(patsubst %.c, %.o, $(src))   #add.o sub.c div1.o hello.o

ALL:a.out

a.out:$(obj)
        gcc $^ -o $@

%.o:%.c
        gcc -c $< -o $@ 

clean:
        -rm -rf $(obj) a.out

继续优化makefile,使用静态模式规则,就是指定模式规则给谁用,这里指定模式规则给obj用,以后文件多了,文件集合会有很多个,就需要指定哪个文件集合用什么规则

$(obj):%.o:%.c
	gcc -c $< -o $@

修改后makefile如下:

src = $(wildcard *.c)   #add.c sub.c div1.c hello.c
obj = $(patsubst %.c, %.o, $(src))   #add.o sub.c div1.o hello.o

ALL:a.out

a.out:$(obj)
        gcc $^ -o $@

$(obj):%.o:%.c
        gcc -c $< -o $@ 

clean:
        -rm -rf $(obj) a.out

当前文件夹下有ALL文件或者clean文件时,会导致makefile瘫痪,用伪目标来解决,添加一行:

.PHONY: clean ALL

以及编译时的参数,-g,-Wall, ... 这些,可以放在makefile里面:

src = $(wildcard *.c)   #add.c sub.c div1.c hello.c
obj = $(patsubst %.c, %.o, $(src))   #add.o sub.c div1.o hello.o

myArgs = -Wall -g
ALL:a.out

a.out:$(obj)
        gcc $^ -o $@

$(obj):%.o:%.c
        gcc -c $< -o $@ 

clean:
        -rm -rf $(obj) a.out

.PHONY: clean ALL

练习1:

  • 源码add.c,sub.c这些在src目录下,.o文件要放在obj目录下,头文件head.h在inc目录下。
  • 将hello.c中的头文件单独拿出来

 新建src、obj、inc目录并放入相关文件,修改makefile如下,主要是注意%是匹配的意思,只匹配文件名,目录位置要手动添加

src = $(wildcard ./src/*.c)  #./src/add.c ./src/sub.c ...
obj = $(patsubst ./src/%.c,./obj/%.o, $(src)) #./obj/add.o 
inc_path = ./inc

myArgs = -Wall -g

ALL:a.out

a.out:$(obj)
        gcc $^ -o $@ $(myArgs)

$(obj):./obj/%.o:./src/%.c
        gcc -c $< -o $@ $(myArgs) -I $(inc_path)

clean:
        -rm -rf $(obj) a.out    

.PHONY:clean ALL

 注意:

如果makefile的名字有变化,如,叫m6

用m6执行makefile, make -f m6

用m6执行clean make -f m6 clean

练习2:

将当前目录下所有.c文件编译为可执行程序

 hello.c文件代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int add(int ,int);
int sub(int ,int);
int div1(int ,int);
int mul(int ,int);

int main(int argc,char *argv[])
{
        int a = 10;
        int b = 5;
        printf("%d + %d = %d\n",a,b,add(a,b));
        printf("%d - %d = %d\n",a,b,sub(a,b));
        printf("%d × %d = %d\n",a,b,mul(a,b));
        printf("%d / %d = %d\n",a,b,div1(a,b));
        return 0;
}

int add(int a,int b)
{
        return a+b;
}

int sub(int a,int b)
{
        return a-b;
}

int mul(int a,int b)
{
        return a*b;
}

int div1(int a,int b)
{
        return a/b;
}

其中add.c、sub.c、div1.c、mul.c分别如下结构所示:

//add.c
#include<stdio.h>

int add(int a,int b)
{
        return a+b;
}

int main(void)
{
        int a = 10; int b = 5;

        printf("%d + %d = %d\n",a,b,add(a,b));

        return 0;
}

makefile代码如下: 

src = $(wildcard *.c) 
target = $(patsubst %.c,%, $(src))      

ALL:$(target)

%:%.c
        gcc $< -o $@

clean:
        -rm -rf $(target)       

.PHONY:clean ALL

 make之后,会生成add、sub、div1、mul、hello的可执行文件

  • 20
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值