• Linux学习笔记——Makefile的使用


    虽然我们编译几个程序的指令很短,但是我们在一个大的项目中可能会存在成百上千个文件,那么手写指令可能需要很长很长时间,且这一种工作可能需要多次重复,那么可以使用Makefile来取代这项工作。




    下面从四个版本的Makefile文件来由浅入深学习Makefile的使用:

    第一种写法

    首先,我们从最简单的Makefile文件的编写入手,我们有如下文件

    getmin.c getmin.h main.c sort.c sort.h
    
    • 1

    那么编译生成可执行文件就可以用如下指令

    gcc *.c -o app
    
    • 1

    我们也可以使用Makefile来完成这项任务,文件命名必须为 makefileMakefile,其基本规则如下:

    目标:依赖文件1 依赖文件2 依赖文件3 ....
    	命令
    
    • 1
    • 2

    注意这里命令前的不是空格,而是 Tab 键,那么Makefile就可编写如下

    app:getmin.c sort.c main.c
            gcc getmin.c sort.c main.c -o app
    
    • 1
    • 2

    然后在命令行执行 make 命令即可


    第二种写法

    第一种写法有个问题是,如果一个 .c 文件被修改,那么 make 就需要重新从所有 .c 文件开始编译,那么可能会需要花费很长的时间。

    最理想的做法是目标可执行文件只依赖 .o 文件,那么一个 .c 文件被修改,只需要将该 .c 文件编译生成 .o 文件即可,其他 .o 文件不需要重新编译了,那么 Makefile 文件的第二种写法就如下

    app:getmin.o sort.o main.o
            gcc getmin.o sort.o main.o -o app
    
    getmin.o:getmin.c
            gcc -c getmin.c
    
    sort.o:sort.c
            gcc -c sort.c
    
    main.o:main.c
            gcc -c main.c
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这里不得不提到Makefile的另一项规则,Makefile 会把规则中的第一个目标作为终极目标,查看其依赖条件,对比其依赖条件是否存在或是否需要被更新,如果是,则会跳到下面相应的规则执行命令。比如 getmin.o 不存在或者 getmin.c 被更新了,那么其就会重新执行 gcc -c getmin.c 指令,然后再执行 gcc getmin.o sort.o main.o -o app 这样只编译了 getmin.c 文件,效率就提高了不少。


    第三种写法

    Makefile 还可以指定普通变量来替代一些文件或命令,也可以使用自动变量来指代规则中的目标、依赖条件等。

    obj=getmin.o sort.o main.o
    target=app
    CC = gcc
    $(target):$(obj)
            $(CC) $(obj) -o $(target)
    
    %.o:%.c
            $(CC) -c $< -o $@
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这里将 getmin.o sort.o main.o 赋值给变量 obj,将 app 赋值给变量target 变量在使用的时候使用 $(变量),除此之外还将 gcc 赋值给变量 CC

    这里还可以使用通配符 %.o 指代所有 .o 文件,%.c 指代所有 .c 文件,当需要一个 .o 文件,比如 getmin.o其到 %.o:%.c 会自动生成相应规则 getmin.o:getmin.c

    除此之外,这里还用 $< 指代依赖的第一项,$@ 指代目标。还可以用 $^ 指代所有依赖项。


    第四种写法

    上面的写法还有一个不够效率的地方就是,如果当前文件夹有很多文件,那么比如给变量 obj 赋值时就需要写很长的一段代码,这时就可以用 Makefile 里的函数来进行简化操作。

    src=$(wildcard ./*.c)
    obj=$(patsubst ./%.c, ./%.o, $(src))
    target=app
    CC = gcc
    $(target):$(obj)
            $(CC) $(obj) -o $(target)
    
    %.o:%.c
            $(CC) -c $< -o $@
    
    clean:
            rm $(obj) $(target)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这里的 wildcard ./*.c 表示提取当前文件夹下所有 .c 文件生成变量,因为是生成变量,所以需要使用 $() 才能取得对应的值,我们将其赋值给 src

    patsubst ./%c, ./%.o, $(src) 表示从 src 变量中提取的值,将所有 .o 变为 .c 赋值给变量,然后将其赋值给 obj 变量。

    这里还用到了 clean 目的是清除所有 .o 文件与目标可执行文件,至于为什么要这样做,有一种情况是,.h 文件更新了,但按照 Makefile 的编写不需要重新编译更新目标可执行文件,所以我们需要 clean 以使得可以正常更新,当然还有其他情况这里暂不详细说明。

    因为 clean 不是首要目标,所以需要再命令行中使用 make clean 来执行命令。如果没有 .o 文件和目标文件,可能会出现提示信息,如果不想显示,可以在 rm 后加上 -f

    还有一种情况就是,如果当前文件夹存在 clean 文件,那么 make clean 会有问题,因为已经存在最新的 clean 文件了,所以我们要增加伪目标声明:

    .PHONY:clean
    clean:
    	rm $(obj) $(target) -f
    
    • 1
    • 2
    • 3

    当然除了 clean 我们也可以添加其他的伪目标:

    hello:
    	echo "hello, makefile"
    
    • 1
    • 2

    然后命令行执行 make hello 就能打印这段话。如果一个规则由多条命令组成,其中一条命令出现问题,之后的命令就不会执行,这时可以通过在前面加上 - 来继续执行后面的指令,比如:

    -rm $(obj) $(target) -f
    
    • 1

    即使该命令出现问题,后面的命令也会继续执行。

  • 相关阅读:
    全程免费的ssl证书申请——七步实现网站https
    【网安神器篇】——Crunch字典生成工具
    SpringMVC获取请求参数
    【SpringCloud微服务--Eureka服务注册中心】
    AWS DAS认证考点整理(Redshift篇)
    completefuture造成的rpc重试事故
    「微服务网关实战三」详细理解 SCG 路由、断言与过滤器
    ThreadLocal优化
    基于langchain的开源大模型应用开发1
    经纬度坐标转换为工程坐标
  • 原文地址:https://blog.csdn.net/weixin_44491423/article/details/126878945