• Linux高级编程--03.make和makfile


    Makefile语法基础

    在Linux下,自动化编译工具是通过make命令来完成的(一些工具厂商也提供了它们自己的make命令,如gmake等),make命令的基本格式如下:

    make [-f makefile] [label]
    

    它可以通过-f参数指定输入文件,当省略-f参数时,默认输入文件名为Makefile,由于我们通常不用这个-f参数,往往就用默认的Makefile文件名。

    Makefile是一个文本文件,它是基于一定的语法规则的,它的基本执行规则定义如下:

    target : [prerequisites]
        command
    
    • target :标签,用于标志当前构建的规则,它也可以是文件。
    • prerequisites :依赖项,在构建该标签的时候先执行的规则
    • command make :需要执行的命令。(任意的Shell命令)

    注意:Makefile的target是顶格写的,而Command需要加一个Tab键。

    例如,我们编写一个简单的Makefile:

    clean:
        @echo "clean"
    all:
        @echo "all"
    

    当我们直接执行make命令的时候,输出如下:

    $make
    clean
    $make all
    all
    $make clean
    clean
    

    从中我们可以看到:默认情况下构建第一个标签。可以通过在命令行参数中通过参数构建指定标签。

    然后我们再来看看依赖性是如何工作的,这次我们修改一下Makefile,让all标签依赖于clean标签:

    clean :
        @echo "clean"
    all : clean
        @echo "all"
    

    再次执行make all的时候,发现会先执行clean标签:

    $make all
    clean
    all
    

    用Makefile来构建项目

    通过对Makefile的语法有一个简单的了解后,下面就可以用Makefile简化我们的构建操作了。还是针对前面的那个stack的例子吧,首先我们来实现一个最简单的例子:

    all :
        gcc -o run main.c stack.c
    

    这样直接通过make命令就可以实现对gcc -o run main.c stack.c整条命令的执行了。

    更加一步,我们想实现增量编译,则要实现如下规则:

    • 如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
    • 如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。

    这个时候就需要前面的依赖性出马了:

    run : stack.o main.o
        gcc -o run main.o stack.o
    stack.o : stack.c
        gcc -c stack.c
    main.o : main.c
        gcc -c main.c
    

    run默认依赖于stack.o和main.o,因此,当构建run的时候,就会先构建stack.o和main.o,输出方式如下:

    make
    gcc -c stack.c
    gcc -c main.c
    gcc -o run main.o stack.o
    

    当我们只改了其中某个文件的时候,例如stack.c,这是由于main.c没有变化,因此不会重新编译main.o,只会重新构建stack.o和run,从而实现我们的增量编译的目的。(这个其实才是make比shell或脚本语言编写的批处理方式要强大的地方)

    make
    gcc -c stack.c
    gcc -o run main.o stack.o
    

    通过自动推导改进Makefile

    通过上面的例子可以看到,虽然我们可以实现增量编译,但是整个Makefile过程是非常复杂的,需要对每个.o文件编写编译脚本。如果项目文件较多,并且有增删的话,则编写Makefile文件非常麻烦。

    为了改进这个问题,makefile提供了一个自动推导的功能,通过它可以简化我们的编写过程。例如,前面的例子可以简化如下:

    CC = gcc
    objs = stack.o main.o
    run : $(objs)
        $(CC) -o run $(objs)
    

    这里我们引入了两个变量,第一个行的CC制定了编译器为gcc(如果不指定则是默认的cc),第二行制定了我们的obj文件。

    这样,只需要执行make命令即可生产我们的程序:

    make
    gcc -c -o stack.o stack.c
    gcc -c -o main.o main.c
    gcc -o run stack.o main.o
    

    可以看到,make命令会自动推导出如何根编出.o文件来。如果我们的项目文件变化了,只需要改objs变量即可,非常方便。

    不过,有的时候我们可能觉得这中自动推导的方式不够用,需要手动控制编译选项,这个时候我们可以自己指定推导规则:

    CC = gcc
    objs = stack.o main.o
    run : $(objs)
        $(CC) -o run $(objs)
    $(objs): %.o: %.c
        $(CC) -c -g $< -o $@
    

    上面的例子中,指明了我们的目标从$objs中获取, %.o 表明要所有以“.o”结尾的目标,也就是 stack.o main.o ,也就是变量$objs集合的模式,而依赖模式“%.c”则取模式 %.o 的“%”,也就是“stack main”,并为其加下“.c”的后缀,于是,我们的依赖目标就是 stack.c main.c 。而命令中的 $< 和“$@”则是自动化变量, $< 表示所有的依赖目标集(也就是 stack.c main.c ), $@ 表示目标集(也就是stack.o main.o )。于是,上面的规则展开后等价于下面的规则:

    stack.o : stack.c
        $(CC) -c -g stack.c -o stack.o
    main.o : main.c
        $(CC) -c -g main.c -o main.o
    

    试想,如果我们的 %.o 有几百个,那种我们只要用这种很简单的“静态模式规则”就可以写完一堆规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会一个很强大的功能。如果想进一步的了解,可以参考跟我一起写 Makefile这篇文章。





  • 相关阅读:
    剑指 Offer 50. 第一个只出现一次的字符
    剑指 Offer 42. 连续子数组的最大和
    剑指 Offer 41. 数据流中的中位数
    剑指 Offer 40. 最小的k个数
    剑指 Offer 39. 数组中出现次数超过一半的数字
    剑指 Offer 38. 字符串的排列
    MySQL更改密码
    WPF中的MySQLHelper
    WPF多线程
    mysql-5.7.28-winx64(压缩包)安装教程
  • 原文地址:https://www.cnblogs.com/linzhenjie/p/5485555.html
Copyright © 2020-2023  润新知