zoukankan      html  css  js  c++  java
  • Makefile入门教程

    Makefile简介

      很多大型项目的编译都是通过Makefile来组织各种库和代码之间的依赖关系。Makefile不仅可以用来编译项目还可以用来组织我们一些日常操作。

      Makefile是和make命令一起配合使用的,Makefile就像shell脚本一样,同时执行操作系统的命令。

      Makefile的好处就是能够使用一行命令来完成“自动编译”,编译整个工程我们要做的唯一事情就是在shell提示符下输入make命令,整个工程就会自动编译

      make是命令工具,它解释makefile指令规则。

    Makefile基本格式如下:

    target ... : prerequisites ...
        command
        ...
        ...
    • target         目标文件
    • prerequisties     生成target所需要的文件或目标
    • command         make需要执行的命令(任意的shell命令),makefile中的命令必须以缩进[tab]开头

    Makefile的初级语法

    规则主要有两个部分:依赖关系 和 生成目标的方法

    语法有以下2种

    target ... : prerequisites ...
        command
        ...

    target ... : prerequisites ; command
        command
        ...

    *注* command太长, 可以用 "" 作为换行符

    规则中的通配符

    • *     :表示任意一个或多个字符
    • ?     :表示任意一个字符
    • [...]   :[abcd] 表示a,b,c,d中任意一个字符, [^abcd]表示除a,b,c,d以外的字符, [0-9]表示 0~9中任意一个数字
    • ~     :表示用户的根目录

    路径搜索

    将源文件的路径明确在Makefile中,便于编译查找,Makefile中有个特殊的变量 VPATH 就是完成这个功能的。

    如果在当前目录中没有找到相应文件或依赖的文件, Makefile 回到 VPATH 指定的路径中再去查找

    VPATH 使用方法:

    • vpath <directories>          当前目录中找不到文件时, 就从<directories>中搜索
    • vpath <pattern> <directories>    符合<pattern>格式的文件, 就从<directories>中搜索
    • vpath <pattern>             清除符合<pattern>格式的文件搜索路径
    • vpath                    清除所有已经设置好的文件路径
    # 示例1 - 当前目录中找不到文件时, 按顺序从 src目录 ../parent-dir目录中查找文件
    VPATH src:../parent-dir   
    
    # 示例2 - .h结尾的文件都从 ./header 目录中查找
    VPATH %.h ./header
    
    # 示例3 - 清除示例2中设置的规则
    VPATH %.h
    
    # 示例4 - 清除所有VPATH的设置
    VPATH

    Makefileh中的变量

    变量定义 (= or :=)

    OBJS = programA.o programB.o
    OBJS-ADD = $(OBJS) programC.o
    # 或者
    OBJS := programA.o programB.o
    OBJS-ADD := $(OBJS) programC.o

    其中 = 和 := 的区别在于, := 只能使用前面定义好的变量, = 可以使用后面定义的变量

    测试 =

    # Makefile内容
    OBJS2 = $(OBJS1) programC.o
    OBJS1 = programA.o programB.o
    
    all:
        @echo $(OBJS2)
    
    # bash中执行 make, 可以看出虽然 OBJS1 是在 OBJS2 之后定义的, 但在 OBJS2中可以提前使用
    $ make
    programA.o programB.o programC.o

    测试 :=

    # Makefile内容
    OBJS2 := $(OBJS1) programC.o
    OBJS1 := programA.o programB.o
    
    all:
        @echo $(OBJS2)
    
    # bash中执行 make, 可以看出 OBJS2 中的 $(OBJS1) 为空
    $ make
    programC.o

    变量替换

    # Makefile内容
    SRCS := programA.c programB.c programC.c
    OBJS := $(SRCS:%.c=%.o)
    
    all:
        @echo "SRCS: " $(SRCS)
        @echo "OBJS: " $(OBJS)
    
    # bash中运行make
    $ make
    SRCS:  programA.c programB.c programC.c
    OBJS:  programA.o programB.o programC.o

    变量追加值 +=

    # Makefile内容
    SRCS := programA.c programB.c programC.c
    SRCS += programD.c
    
    all:
        @echo "SRCS: " $(SRCS)
    
    # bash中运行make
    $ make
    SRCS:  programA.c programB.c programC.c programD.c

    变量覆盖 override

    作用是使 Makefile中定义的变量能够覆盖 make 命令参数中指定的变量

    语法:

    • override <variable> = <value>
    • override <variable> := <value>
    • override <variable> += <value>
    # Makefile内容 (没有用override)
    SRCS := programA.c programB.c programC.c
    
    all:
        @echo "SRCS: " $(SRCS)
    
    # bash中运行make
    $ make SRCS=nothing
    SRCS:  nothing
    
    #################################################
    
    # Makefile内容 (用override)
    override SRCS := programA.c programB.c programC.c
    
    all:
        @echo "SRCS: " $(SRCS)
    
    # bash中运行make
    $ make SRCS=nothing
    SRCS:  programA.c programB.c programC.c

    目标变量

    作用是使变量的作用域仅限于这个目标(target), 而不像之前例子中定义的变量, 对整个Makefile都有效.

    语法:

    • <target :>  <variable-assignment>
    • <target :>  override <variable-assignment> (override作用参见 变量覆盖的介绍)
    # Makefile 内容
    SRCS := programA.c programB.c programC.c
    
    target1: TARGET1-SRCS := programD.c
    target1:
        @echo "SRCS: " $(SRCS)
        @echo "SRCS: " $(TARGET1-SRCS)
    
    target2:
        @echo "SRCS: " $(SRCS)
        @echo "SRCS: " $(TARGET1-SRCS)
    
    # bash中执行make
    $ make target1
    SRCS:  programA.c programB.c programC.c
    SRCS:  programD.c
    
    $ make target2     <-- target2中显示不了 $(TARGET1-SRCS)
    SRCS:  programA.c programB.c programC.c
    SRCS:

    Makefile 命令前缀

    Makefile 中书写shell命令时可以加2种前缀 @ 和 -, 或者不用前缀.

    3种格式的shell命令区别如下:

    • 不用前缀    输出 执行的命令 以及 命令执行的结果, 出错的话停止执行
    • 前缀 @     只输出命令执行的结果, 出错的话停止执行
    • 前缀 -     命令执行有错的话, 忽略错误, 继续执行
    # Makefile 内容 (不用前缀)
    all:
        echo "没有前缀"
        cat this_file_not_exist
        echo "错误之后的命令"       <-- 这条命令不会被执行
    
    # bash中执行 make
    $ make
    echo "没有前缀"             <-- 命令本身显示出来
    没有前缀                    <-- 命令执行结果显示出来
    cat this_file_not_exist
    cat: this_file_not_exist: No such file or directory
    make: *** [all] Error 1
    
    ###########################################################
    
    # Makefile 内容 (前缀 @)
    all:
        @echo "没有前缀"
        @cat this_file_not_exist
        @echo "错误之后的命令"       <-- 这条命令不会被执行
    
    # bash中执行 make
    $ make
    没有前缀                         <-- 只有命令执行的结果, 不显示命令本身
    cat: this_file_not_exist: No such file or directory
    make: *** [all] Error 1
    
    ###########################################################
    
    # Makefile 内容 (前缀 -)
    all:
        -echo "没有前缀"
        -cat this_file_not_exist
        -echo "错误之后的命令"       <-- 这条命令会被执行
    
    # bash中执行 make
    $ make
    echo "没有前缀"             <-- 命令本身显示出来
    没有前缀                    <-- 命令执行结果显示出来
    cat this_file_not_exist
    cat: this_file_not_exist: No such file or directory
    make: [all] Error 1 (ignored)
    echo "错误之后的命令"       <-- 出错之后的命令也会显示
    错误之后的命令              <-- 出错之后的命令也会执行

    伪目标

    伪目标并不是一个"目标(target)", 不像真正的目标那样会生成一个目标文件.

    典型的伪目标是 Makefile 中用来清理编译过程中中间文件的 clean 伪目标, 一般格式如下:

    .PHONY: clean   <-- 这句没有也行, 但是最好加上
    clean:
        -rm -f *.o

    引用其他的 Makefile

    语法: include <filename>  (filename 可以包含通配符和路径)

    # Makefile 内容
    all:
        @echo "主 Makefile begin"
        @make other-all
        @echo "主 Makefile end"
    
    include ./other/Makefile
    
    # ./other/Makefile 内容
    other-all:
        @echo "other makefile begin"
        @echo "other makefile end"
    
    # bash中执行 make
    $ ll
    total 20K
    -rw-r--r-- 1 wangyubin wangyubin  125 Sep 23 16:13 Makefile
    -rw-r--r-- 1 wangyubin wangyubin  11K Sep 23 16:15 makefile.org   <-- 这个文件不用管
    drwxr-xr-x 2 wangyubin wangyubin 4.0K Sep 23 16:11 other
    $ ll other/
    total 4.0K
    -rw-r--r-- 1 wangyubin wangyubin 71 Sep 23 16:11 Makefile
    
    $ make
    主 Makefile begin
    make[1]: Entering directory `/path/to/test/makefile'
    other makefile begin
    other makefile end
    make[1]: Leaving directory `/path/to/test/makefile'
    主 Makefile end

    查看C文件的依赖关系

    写 Makefile 的时候, 需要确定每个目标的依赖关系.

    GNU提供一个机制可以查看C代码文件依赖那些文件, 这样我们在写 Makefile 目标的时候就不用打开C源码来看其依赖那些文件了.

    比如, 下面命令显示在内核源码中 virt/kvm/kvm_main.c 中的依赖关系

    $ cd virt/kvm/
    $ gcc -MM kvm_main.c 
    kvm_main.o: kvm_main.c iodev.h coalesced_mmio.h async_pf.h   <-- 这句就可以加到 Makefile 中作为编译 kvm_main.o 的依赖关系

    make 退出码

    Makefile的退出码有以下3种:

    • 0  表示成功执行
    • 1  表示make命令出现了错误
    • 2  使用了 "-q" 选项, 并且make使得一些目标不需要更新

    指定 Makefile, 指定特定目标

    默认执行 make 命令时, GNU make在当前目录下依次搜索下面3个文件 "GNUmakefile", "makefile", "Makefile",

    找到对应文件之后, 就开始执行此文件中的第一个目标(target). 如果找不到这3个文件就报错.

    非默认情况下, 可以在 make 命令中 -f 指定特定的 Makefile 和特定的 目标.

    # Makefile文件名改为 MyMake, 内容
    target1:
        @echo "target [1]  begin"
        @echo "target [1]  end"
    
    target2:
        @echo "target [2]  begin"
        @echo "target [2]  end"
    
    # bash 中执行 make
    $ ls
    Makefile
    $ mv Makefile MyMake
    $ ls
    MyMake
    $ make                     <-- 找不到默认的 Makefile
    make: *** No targets specified and no makefile found.  Stop.
    $ make -f MyMake           <-- 指定特定的Makefile
    target [1]  begin
    target [1]  end
    $ make -f MyMake target2   <-- 指定特定的目标(target)
    target [2]  begin
    target [2]  end

    Makefile 隐含规则

    这里只列一个和编译C相关的.

    编译C时,<n>.o 的目标会自动推导为 <n>.c

    # Makefile 中
    main : main.o
        gcc -o main main.o
    
    #会自动变为:
    main : main.o
        gcc -o main main.o
    
    main.o: main.c    <-- main.o 这个目标是隐含生成的
        gcc -c main.c

    自动变量

    Makefile 中很多时候通过自动变量来简化书写, 各个自动变量的含义如下:

    自动变量

    含义

    $@ 目标集合
    $% 当目标是函数库文件时, 表示其中的目标文件名
    $< 第一个依赖目标. 如果依赖目标是多个, 逐个表示依赖目标
    $? 比目标新的依赖目标的集合
    $^ 所有依赖目标的集合, 会去除重复的依赖目标
    $+ 所有依赖目标的集合, 不会去除重复的依赖目标
    $* 这个是GNU make特有的, 其它的make不一定支持

    参考文献

    Makefile简易教程

    Makefile使用总结

    Windows安装GNU编译器使用makefile

    $ make target

    Makefile语法

    tar zxvf test.tar.gz -C test

    MinGW-w64安装教程——著名C/C++编译器GCC的Windows版本

  • 相关阅读:
    11、MyBatis教程之动态SQL
    10、MyBatis教程之一对多处理
    9、MyBatis教程之多对一处理
    8、MyBatis之使用注解开发
    7、MyBatis教程之分页实现
    6、MyBatis教程之日志实现
    5、MyBatis教程之ResultMap
    4、MyBatis教程之配置解析
    3、MyBatis教程之CURD操作
    Session的几种保存方式
  • 原文地址:https://www.cnblogs.com/LXP-Never/p/10602791.html
Copyright © 2011-2022 走看看