Linux 之 makefile

一.makefile简介以及一些基本规则:

        在linux环境下编译文件的时候,我们经常用 gcc  xxx.c -o xxx 类似这样去生成可执行文件,当我们遇到要同时编译很多个文件的时候该怎么办呢,总不能一个一个罗列出来吧,这就引出了我们今天的主角 makefile ,makefile就是可以把很多个.c文件放一起编译,只需要将源文件放入一个新编辑的文件中,以后只对那一个文件进行编译即可,每次只要执行一个make命令就能完成所有的编译,能够提高编译的效率。下面就让我们来介绍一下makefile的一些简单规则吧。

        目标:依赖

       (tab)命令    (注意:此处的tab指的是按一次tab键,不能用空格代替)

        目标:就是要生成的文件 (比如gcc main.c -o main 这里的main就是目标文件)

        依赖:目标文件由哪些文件生成(上面的 mian 就是通过mian.c生成的)

        命令:就是生成目标文件需要执行那些命令。

二.第一个版本的makefile

        介绍完makefile的规则就可以来看makefile的第一个版本了。在当前文件夹下编写main.c fun1.c fun2.c sum.c 这几个文件,用来测试的我们就写简单一点:

main.c:

#include<stdio.h>
int main()
{
        int i=0;
        for(i=0;i<5;i++)
        {
                printf("%d ",i);
        }
        printf("\n");
        fun1();
        fun2();
        int d=sum(5);
        printf("sum=%d\n",d);
        return 0;
}

fun1.c:

#include<stdio.h>
void fun1()
{
printf("makefile第一个版本\n");
}

fun2.c

#include<stdio.h>
void fun2()
{
printf("Hello world\n");
}

sum.c:

#include<stdio.h>
int sum(int n)
{
        int sum=1;
        int i=1;
        for(i=1;i<=n;i++)
        {
                sum*=i;
        }
        return sum;
}

        以上就是所有的测试代码文件,后面会一直用这几个文件,读者也可以自己指定自己的文件。下面我们就在当前文件下创建一个makefile文件(注意:文件名就叫makefile)

vim makefile

        然后根据我们上面说到的规则就可以开始写正式的代码了,接下来就是第一个版本的makefile文件的编写:

main: main.c fun1.c fun2.c sum.c
        gcc  main.c fun1.c fun2.c sum.c -o main

        main就是我们的目标文件,main.c fun1.c fun2.c sum.c 就是依赖,注意下面的那个 tab 键,后面就是生成目标文件需要执行的命令 gcc main.c fun1.c fun2.c sum.c -o main .要生成目标文件,我们要执行的命令就是:

make

15a315cbc5ee43379e114fdeba5aa5db.png

        执行完make命令后我们我们查看当前目录会发现生成了可执行文件文件 main ,我么就可以通过直接执行main来同时执行四个文件,如果有文件被修改了,我们就需要把这个可执行文件先删除了,然后再重新执行make命令重新进行编译(删除用的是 rm -f main)。

        以上就是第一个版本,但是当我们只对其中一个文件进行更改时,再次使用make编译的时候会将所有的文件都再编译一遍,那么如何避免这种重复编译呢?这时候就需要引入我们的第二个版本。

三.第二个版本的makefile

        在了解第二个版本之前先了解一下makefile的工作原理:

如果想要生成目标,会先检查所有的依赖是否存在,如果依赖不存在,会先向下搜索相应的规则看生成该依赖的命令是否存在,如果存在就会执行相应的命令,如果不存在,就会报错。如果全部依赖都存在,那么就会检查目标和依赖的更新时间,哪个时间大,哪个是最新的,

        如果目标的时间 > 依赖的时间              那么就不要更新

        如果目标的时间 < 依赖的时间              那么就需要更新

        在了解完makefile的原理之后就来看看第二个版本的makefile吧。

对于第一个版本只是对.c文件的编译,我们可以从.c文件之前考虑一下,直接从.o文件开始处理,并将其分开处理。

main: main.o fun1.o fun2.o sum.o
        gcc  main.o fun1.o fun2.o sum.o -o main
main.o: main.c
        gcc -c main.c
fun1.o: fun1.c
        gcc -c fun1.c
fun2.o: fun2.c
        gcc -c fun2.c
sum.o:  sum.c
        gcc -c sum.c

生成的目标文件还是main ,刚开始会检查是否有main.o文件,没有就通过下面的.c文件来生成,其他.o文件也是这种操作,集齐所有的.o文件后就会执行生成目标文件main的那条命令。

8dea5b72f3a94803b1aa19ce372a03ba.png

        执行make命令会发现几个.c文件是一个一个生成的,最后生成目标文件,当我们更改其中 一个文件时,看看会执行那几条命令。

881ce5b481d04c2b95531f9b8ea6ec68.png

        当我们更新了fun1.c文件时,再次编译会发现只对fun1.c文件进行了编译并且执行了最终的那条命令,这样就就很好的解决第一个版本的分开编译问题,但是当一个目录有很多文件要编译时,要加入很多条

fun1.o:fun1.c

        gcc -c fun1.c

这种命令,会显得很复杂。逐步往下看,接下来看看第三个版本的makefile。

四.第三个版本的makefile

        这里我们就要引入makefile里的变量:自定义变量,自带变量,自动变量

自定义变量:就是用户自己定义的变量,用$()引用 (例如:var=hello)

自带变量:CC 指定编译器是gcc,   CPPFLAGS 预处理时的参数 -I./  CFLAGS C编译器的选项 -Wall-g-c LDFLAGS 链接器选项 -L -l 

自动变量:$@ 表示目标文件(就是要生成的目标文件)  $< 表示规则中的第一个条件 $^ 表示规则中的所有条件 (这三个自动变量只能在命令中使用)

解释完这些我们就可以自己来实操了,自己定义变量来简化代码。我们先把目标和依赖定义出来

target=main
object=main.o fun1.o fun2.o sum.o

定义完之后就可以进行替换了:

fd0c34a9166741dd8fafba11a3fb2c75.png

        这都是用户自定义的变量的替换

df369a76aa6442558cc5c19075380ba2.png

        成功编译,下面进行自带变量和自动变量的替换

5698cb8b647a4c3ab93d414e43848b88.png

        替换完成,并且可以编译执行,但是看到这个makefile文件多次重复了$(CC) -c $<这个命令,显得很冗余,那么接下来对其进行修改,这里就要用到我们的模式规则了。

模式规则:%.o = %.c    %表示一个或多个,简单的说就是:xxx.o依赖xxx.c

target=main
object=main.o fun1.o fun2.o sum.o
$(target): $(object)
        $(CC)  $^ -o $@
%.o: %.c
        $(CC) -c $<

82bb34e42a5d431cbde96fb2f35fe504.png

        全部替换完成,看上去和之前比简洁了很多,但是他们的效果都是一样的。记住上面的写法,完全可以当成一个模板来记忆,只要替换target 和 object就可以完成自己文件的编译。(注意自动变量只能用在命令中)

五.第四个版本的makefile

        在这里就要引入makefile的几个常用的函数,wildcard 和patsubst :

wildcard:查找只指定类型的文件 src=$(wildcard *.c) 就是查找当前目录下的所有的.c文件并赋给src。patsubst:匹配替换,object=$(patsubst *.c *.o,$(src))  把src里的.c文件全部替换成.o。

注意:makefile里的函数都是有返回值的

target=main
src=$(wildcard *.c)
object=$(patsubst %.c,%.o,$(src))
$(target): $(object)
        $(CC)  $^ -o $@
%.o: %.c
        $(CC) -c $<

带入函数的化就快接近最终的版本了,这样下次替换别的文件时,只需要将taget更改一下就行了,其他的会通过函数自动获取,非常方便快捷。

3ee931928a724dd58c7d23fbe50cb18c.png

六.第五个版本的makefile 

        因为每次编译完文件都会生成.o文件,其实这些.o文件都是可以删除的,因为你的目标文件已经生成了,只要保存好.c文件就行,这样的话我们就需要增加一个清理操作了,不然就会显得很麻烦,每次都去清理文件,有时候还容易删错。下面看如何更改。

9b190d2b98534b34b6e253061506c74b.png

        这里涉及到终极目标问题,第一个出现的目标就是终极目标。最后执行这个clean,可能会有人认为直接就把前面生成的文件都给删了,其实是不是这样的,这个编译执行,一定生成终极目标,这个清理命令是需要自己单独使用别的命令调用的,就是指定目标。 根据前面的makefile的工作原理,我们直到,每次都会根据依赖去检查目标是否是最新的,这里由于清理命令是没有依赖的,因此我们就要将其声明成伪目标,这样就不用去检查依赖也不用去更新。

.PHONY:clean #(生成一个伪目标)这样就不会检查依赖也不会检查更新

        这样我们的清理就大功告成了,所有的也就都完成了,当需要调用清理的时候,直接使用:

make clean

就可以完成调用。

target=main
src=$(wildcard *.c)
object=$(patsubst %.c,%.o,$(src))
$(target): $(object)
        $(CC)  $^ -o $@
%.o: %.c
        $(CC)  -c $<

.PHONY:clean
clean:
        rm -f $(target) $(object)

        以上就是makefile的最终版本 ,在实际使用中直接进行替换即可。

5e925a03d0a64327980e0f23303463f7.png

        当前目录如果有多个makefile文件时候,我们需要加上 -f 参数,因为make命令默认的是去找makefile文件的。

make -f makefile 文件

 12552b8f283448dcb093eb3c9f1d764d.png

        在这里对makefile文件进行改名,加上-f参数后依旧可以执行。 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值