[Makefile] Makefile 及其工做原理

转自:https://www.linuxidc.com/Linux/2018-09/154071.htmlinux

 

当你须要在一些源文件改变后运行或更新一个任务时,一般会用到 make 工具。make 工具须要读取一个 Makefile(或 makefile)文件,在该文件中定义了一系列须要执行的任务。你可使用 make 来将源代码编译为可执行程序。大部分开源项目会使用 make 来实现最终的二进制文件的编译,而后使用 make install 命令来执行安装。编程

本文将经过一些基础和进阶的示例来展现 make 和 Makefile 的使用方法。在开始前,请确保你的系统中安装了 make编程语言

 

基础示例

依然从打印 “Hello World” 开始。首先建立一个名字为 myproject 的目录,目录下新建 Makefile 文件,文件内容为:函数

  1. say_hello:
  2.         echo"Hello World"

在 myproject 目录下执行 make,会有以下输出:工具

  1. make
  2. echo"Hello World"
  3. HelloWorld

在上面的例子中,“say_hello” 相似于其余编程语言中的函数名。这被称之为目标target。在该目标以后的是预置条件或依赖。为了简单起见,咱们在这个示例中没有定义预置条件。echo ‘Hello World' 命令被称为步骤recipe。这些步骤基于预置条件来实现目标。目标、预置条件和步骤共同构成一个规则。spa

总结一下,一个典型的规则的语法为:code

  1. 目标:预置条件
  2. <TAB>步骤

做为示例,目标能够是一个基于预置条件(源代码)的二进制文件。另外一方面,预置条件也能够是依赖其余预置条件的目标。htm

  1. final_target: sub_target final_target.c
  2.         Recipe_to_create_final_target
  3.        
  4. sub_target: sub_target.c
  5.         Recipe_to_create_sub_target

目标并不要求是一个文件,也能够只是步骤的名字,就如咱们的例子中同样。咱们称之为“伪目标”。递归

再回到上面的示例中,当 make 被执行时,整条指令 echo "Hello World" 都被显示出来,以后才是真正的执行结果。若是不但愿指令自己被打印处理,须要在 echo 前添加 @

say_hello:
        @echo "Hello World"
ip

从新运行 make,将会只有以下输出:

  1. make
  2. HelloWorld

接下来在 Makefile 中添加以下伪目标:generate 和 clean

  1. say_hello:
  2.         @echo"Hello World"
  3. generate:
  4.         @echo"Creating empty text files..."
  5.         touchfile-{1..10}.txt
  6. clean:
  7.         @echo"Cleaning up..."
  8.         rm*.txt

随后当咱们运行 make 时,只有 say_hello 这个目标被执行。这是由于Makefile 中的第一个目标为默认目标。一般状况下会调用默认目标,这就是你在大多数项目中看到 all 做为第一个目标而出现。all 负责来调用它他的目标。咱们能够经过 .DEFAULT_GOAL 这个特殊的伪目标来覆盖掉默认的行为。

在 Makefile 文件开头增长 .DEFAULT_GOAL

  1. .DEFAULT_GOAL := generate

make 会将 generate 做为默认目标:

  1. make
  2. Creatingempty text files...
  3. touchfile-{1..10}.txt

顾名思义,.DEFAULT_GOAL 伪目标仅能定义一个目标。这就是为何不少 Makefile 会包括 all 这个目标,这样能够调用多个目标。

下面删除掉 .DEFAULT_GOAL,增长 all 目标:

  1. all: say_hello generate
  2. say_hello:
  3.         @echo"Hello World"
  4. generate:
  5.         @echo"Creating empty text files..."
  6.         touchfile-{1..10}.txt
  7. clean:
  8.         @echo"Cleaning up..."
  9.         rm*.txt

运行以前,咱们再增长一些特殊的伪目标。.PHONY 用来定义这些不是文件的目标。make 会默认调用这些伪目标下的步骤,而不去检查文件名是否存在或最后修改日期。完整的 Makefile 以下:

  1. .PHONY: all say_hello generate clean
  2. all: say_hello generate
  3. say_hello:
  4.         @echo"Hello World"
  5. generate:
  6.         @echo"Creating empty text files..."
  7.         touchfile-{1..10}.txt
  8. clean:
  9.         @echo"Cleaning up..."
  10.         rm*.txt

make 命令会调用 say_hello 和 generate

  1. make
  2. HelloWorld
  3. Creatingempty text files...
  4. touchfile-{1..10}.txt

clean 不该该被放入 all 中,或者被放入第一个目标中。clean 应当在须要清理时手动调用,调用方法为 make clean

  1. make clean
  2. Cleaning up...
  3. rm*.txt

如今你应该已经对 Makefile 有了基础的了解,接下来咱们看一些进阶的示例。

 

进阶示例

 

变量

在以前的实例中,大部分目标和预置条件是已经固定了的,但在实际项目中,它们一般用变量和模式来代替。

定义变量最简单的方式是使用 = 操做符。例如,将命令 gcc 赋值给变量 CC

  1. CC =gcc

这被称为递归扩展变量,用于以下所示的规则中:

  1. hello: hello.c
  2.     ${CC} hello.-o hello

你可能已经想到了,这些步骤将会在传递给终端时展开为:

  1. gcc hello.-o hello

${CC} 和 $(CC) 都能对 gcc 进行引用。但若是一个变量尝试将它自己赋值给本身,将会形成死循环。让咱们验证一下:

  1. CC =gcc
  2. CC = ${CC}
  3. all:
  4.     @echo ${CC}

此时运行 make 会致使:

  1. make
  2. Makefile:8:***Recursive variable 'CC' references itself (eventually).  Stop.

为了不这种状况发生,可使用 := 操做符(这被称为简单扩展变量)。如下代码不会形成上述问题:

  1. CC :=gcc
  2. CC := ${CC}
  3. all:
  4.     @echo ${CC}

 

模式和函数

下面的 Makefile 使用了变量、模式和函数来实现全部 C 代码的编译。咱们来逐行分析下:

  1. #Usage:
  2. #make        # compile all binary
  3. #make clean  # remove ALL binaries and objects
  4. .PHONY = all clean
  5. CC =gcc                        # compiler to use
  6. LINKERFLAG =-lm
  7. SRCS := $(wildcard *.c)
  8. BINS := $(SRCS:%.c=%)
  9. all: ${BINS}
  10. %:%.o
  11.         @echo"Checking.."
  12.         ${CC} ${LINKERFLAG} $<-o $@
  13. %.o:%.c
  14.         @echo"Creating object.."
  15.         ${CC}-c $<
  16. clean:
  17.         @echo"Cleaning up..."
  18.         rm-rvf *.o ${BINS}
  • 以 # 开头的行是评论。
  • .PHONY = all clean 行定义了 all 和 clean 两个伪目标。
  • 变量 LINKERFLAG 定义了在步骤中 gcc 命令须要用到的参数。
  • SRCS := $(wildcard *.c)$(wildcard pattern) 是与文件名相关的一个函数。在本示例中,全部 “.c”后缀的文件会被存入 SRCS 变量。
  • BINS := $(SRCS:%.c=%):这被称为替代引用。本例中,若是 SRCS 的值为 'foo.c bar.c',则 BINS的值为 'foo bar'
  • all: ${BINS} 行:伪目标 all 调用 ${BINS} 变量中的全部值做为子目标。
  • 规则:

    1. %:%.o
    2.   @echo"Checking.."
    3.   ${CC} ${LINKERFLAG} $&lt;-o $@

    下面经过一个示例来理解这条规则。假定 foo 是变量 ${BINS} 中的一个值。% 会匹配到 foo%匹配任意一个目标)。下面是规则展开后的内容:

    1. foo: foo.o
    2.   @echo"Checking.."
    3.   gcc-lm foo.-o foo

    如上所示,% 被 foo 替换掉了。$< 被 foo.o 替换掉。$<用于匹配预置条件,$@ 匹配目标。对 ${BINS} 中的每一个值,这条规则都会被调用一遍。

  • 规则:

    1. %.o:%.c
    2.   @echo"Creating object.."
    3.   ${CC}-c $&lt;

    以前规则中的每一个预置条件在这条规则中都会都被做为一个目标。下面是展开后的内容:

    1. foo.o: foo.c
    2.   @echo"Creating object.."
    3.   gcc-c foo.c
  • 最后,在 clean 目标中,全部的二进制文件和编译文件将被删除。

下面是重写后的 Makefile,该文件应该被放置在一个有 foo.c 文件的目录下:

    1. #Usage:
    2. #make        # compile all binary
    3. #make clean  # remove ALL binaries and objects
    4. .PHONY = all clean
    5. CC =gcc                        # compiler to use
    6. LINKERFLAG =-lm
    7. SRCS := foo.c
    8. BINS := foo
    9. all: foo
    10. foo: foo.o
    11.         @echo"Checking.."
    12.         gcc-lm foo.-o foo
    13. foo.o: foo.c
    14.         @echo"Creating object.."
    15.         gcc-c foo.c
    16. clean:
    17.         @echo"Cleaning up..."
    18.         rm-rvf foo.o foo
相关文章
相关标签/搜索