Makefile知识总结

Makefile知识总结

程序的编译和链接过程

img
  • 编译
    • 编译概念:一般情况下,源文件(.c.cpp)会被编译成中间代码文件,在 Windows 下也就是 .obj 文件,UNIX 下是 .o 文件,即 Object File,这个动作叫做编译。
    • 编译执行内容:检验语法、函数与变量声明是否正确。
    • 编译结果:每个源文件都应对应一个中间目标文件(.obj.o),由于编译生成的中间目标文件太多,我们要给中间目标文件打个包,在 Windows 下这种包叫“库文件”(Library File),也就是 .lib 文件,在 UNIX 下,是 Archive File,也就是 .a 文件。
  • 链接
    • 链接执行内容:主要链接函数与全局变量。此阶段,链接器会在所有的 Object File 中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在 VC 下,这种错误一般是:Link 2001 错误,意思说是说,链接器未能找到函数的实现。

makefile规则

  • makefile规则

    • target ... : prerequisites ...
      	recipe
      	...
      	...
      
    • 核心内容:target 这一个或多个的目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command 中。prerequisites中如果有一个以上的文件比target文件要新的话,recipe所定义的命令就会被执行。

    • 关键字解释:

      1. target:可以是一个 object file(目标文件),也可以是一个可执行文件,还可以是一个标签(label)。
      2. prerequisites(先决条件):生成该 target 所依赖的文件和/或 target。
      3. recipe(内容):该 target 要执行的命令(任意的 shell 命令)。

makefile示例1

  • 示例

    • 前提:假定一个工程有 3 个头文件和 8 个 C 文件,makefile需要依照下列规则进行编译和链接。

      1. 如果这个工程没有编译过,那么我们的所有 c 文件都要编译并被链接
      2. 如果这个工程的某几个 c 文件被修改,那么我们只编译被修改的 c 文件,并链接目标程序。
      3. 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的 c 文件,并链接目标程序。
    • makefile内容(反斜杠\ 是换行符的意思):

      edit : main.o kbd.o command.o display.o \
      		insert.o search.o files.o utils.o
      	gcc -o edit main.o kbd.o command.o display.o \
      		insert.o search.o files.o utils.o
      main.o : main.c defs.h
      	gcc -c main.c
      kbd.o : kbd.c defs.h command.h
      	cc -c kbd.c
      command.o : command.c defs.h command.h
      	gcc -c command.c
      display.o : display.c defs.h buffer.h
      	gcc -c display.c
      insert.o : insert.c defs.h buffer.h
      	gcc -c insert.c
      search.o : search.c defs.h buffer.h
      	gcc -c search.c
      files.o : files.c defs.h buffer.h command.h
      	gcc -c files.c
      utils.o : utils.c defs.h
      	gcc -c utils.c
      clean :
      	rm edit main.o kbd.o command.o display.o \
      		insert.o search.o files.o utils.o
      

make工作过程

  • 输入make命令后
    1. make 会在当前目录下找名字叫“Makefile”或“makefile”的文件。
    2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
    3. 如果 edit 文件不存在,或是 edit 所依赖的后面的 .o 文件的文件修改时间要比 edit 这个文件新, 那么,他就会执行后面所定义的命令来生成 edit 这个文件。
    4. 如果 edit 所依赖的 .o 文件也不存在,那么 make 会在当前文件中找目标为 .o 文件的依赖性,如果找到则再根据那一个规则生成 .o 文件。
    5. 当然,你的 C 文件和头文件是存在的,于是 make 会生成 .o 文件,然后再用 .o 文件生成 make 的终极任务,也就是可执行文件 edit 了。

$@, $^, $< , $?, $%, $+

  • $@ :表示目标文件
  • $^ :表示所有的依赖文件
  • $< :表示第一个依赖文件
  • $? :表示比目标还要新的依赖文件列表
  • $% :仅当目标是函数库文件时,表示规则中的目标成员名。例如,如果一个目标是“foo.a(bar.o)”,那么,“$%”就是“bar.o”,“$@”就是“foo.a”。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。
  • $+ :类似$^,也是所有依赖目标的集合。只是它不去除重复的依赖目标。

包含其他文件(mk/sh)

  • 示例

    include foo.make *.mk $(bar)
    #等价于
    include foo.make a.mk b.mk c.mk bish bash
    

    环境变量 .INCLUDE_DIRS 包含当前 make 会寻找的目录列表。

make执行步骤

  1. 读入所有的 Makefile。

  2. 读入被 include 的其它 Makefile。

  3. 初始化文件中的变量。

  4. 推导隐式规则,并分析所有规则。

  5. 为所有的目标文件创建依赖关系链。

  6. 根据依赖关系,决定哪些目标要重新生成。

  7. 执行生成命令。

    1-5 步为第一个阶段,6-7 为第二个阶段。第一个阶段中,如果定义的变量被使用了,那么,make 会 把其展开在使用的位置。

列出所有文件*

  1. 列出一确定文件夹中的所有 .c 文件:objects := $(wildcard *.c)

  2. 列出 (1) 中所有文件对应的 .o 文件,在(3)中我们可以看到它是由 make 自动编译出的:$(patsubst %.c,%.o,$(wildcard *.c))

  3. 由 (1)(2) 两步,可写出编译并链接所有 .c.o 文件:

    objects := $(patsubst %.c,%.o,$(wildcard *.c))
    foo : $(objects)
    cc -o foo $(objects)
    

特殊变量

  • VPATH:存储大项目中许多源文件的路径。
    • 示例:VPATH = src:../headers。定义指定两个目录,“src”“../headers”,make 会按照这个顺序进行搜索。目录由“冒号” 分隔。(当前目录永远是最高优先搜索的地方)
  • MAKE:make的一系列命令
    • cd subdir && $(MAKE):是先进入“subdir”目录,然后执行 make 命令。
  • MAKELEVEL:如果make 有一个嵌套执行的动作,那么这个变量会记录了当前 Makefile 的调用层数。

伪目标

  • 伪目标:“伪目标”并不是一个文件,只是一个标签,所以 make 无法生成它的依赖关系和决定它是否要执行。只有通过显式地指明这个“目标”才能让其生效。

    “伪目标”的取名不能和文件名重名,为了避免和文件重名的这种情况,我们可以使用一个特殊的标记“.PHONY”来显式地指明 一个目标是“伪目标”。

函数汇总及部分函数解析

  • addsuffix:
  • basename:
  • call:
  • dir:
  • findstring
    • 语法格式:$(findstring ,<find>,<in>)
    • 名称:查找字符串函数
    • 功能:在字串<in>中查找<find>字串。
    • 返回:如果找到,那么返回<find> ,否则返回空字符串。
    • 示例:$(findstring a,a b c)$(findstring a,b c)。第一个函数返回 a 字符串,第二个返回空字符串
  • filter
    • 语法格式:$(filter <pattern...>,<text>)
    • 名称:过滤函数
    • 功能:以<pattern...>模式过滤<text>字符串中的单词,保留符合模式<pattern...>的单词。可以有多个模式。
    • 返回:返回符合模式<pattern...>的字串。
    • 示例:sources := foo.c bar.c baz.s ugh.h$(filter %.c %.s,$(sources)) 返回的值是 foo.c bar.c baz.s
  • filter-out:
  • firstword:
  • foreach:
  • join:
  • notdir
    • 语法格式:$(notdir <name...>)
    • 名称:取文件函数
    • 功能:从文件名序列<names>中取出非目录部分,非目录部分是指最后一个反斜杠/之后的部分。
    • 返回:返回文件名序列<name>的非目录部分。
    • 示例1:$(notdir src/foo.c hacks)返回值是foo.c hacks
    • 示例2:notdir $(SRCS):去除SRCS路径的绝对路径部分,只保留文件名。
  • origin:
  • patsubst
    • 语法格式:$(patsubst ,<pattern>,<replacement>,<text>)
    • 名称:模式字符串替换函数。
    • 功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。这里, <pattern>可以包括通配符 % , 表示任意长度的字串。如果<replacement>中也包含 % ,那么,<replacement>中的这个 % 将是<pattern>中的那个 % 所代表的字串。(可以用 \ 来转义,以 \% 来表示真实含义的 % 字符)
    • 返回:函数返回被替换过后的字符串。
    • 示例1:$(patsubst %.c,%.o,x.c.c bar.c)。把字串 x.c.c bar.c 符合模式 %.c 的单词替换成 %.o ,返回结果是 x.c.o bar.o
    • 示例2:patsubst %cpp, %o, text:把text字符串中符合%cpp形式的文本替换为%o
  • subst
    • 语法格式:$(subst <from>,<to>,<text>)
    • 名称:字符串替换函数
    • 功能:把字串<text>中的<from>字符串替换成<to>
    • 返回:函数返回被替换过后的字符串。
    • 示例:$(subst ee,EE,feet on the street)。把 feet on the street 中的 ee 替换成 EE ,返回结果是 fEEt on the strEEt
  • suffix:
  • sort:
  • strip
    • 语法格式:$(strip <string>)
    • 名称:去空格函数。
    • 功能:去掉<string>字串中开头和结尾的空字符。
    • 返回:返回被去掉空格的字符串值。
    • 示例:$(strip a b c ),把字串去掉开头和结尾的空格,结果是 a b c。
  • shell
    • 功能:shell 函数把执行操作系统命令后的输出作为函数返回。
  • word:
  • wildcard
    • 作用:
      • 可以使用“$(wildcard *.c)”来获取工作目录下的所有的.c文件列表。
      • 可以使用“$(patsubst %.c,%.o,$(wildcard *.c))”,首先使用“wildcard”函数获取工作目录下的.c文件列表;之后将列表中所有文件名的后缀.c替换为.o
    • 示例:wildcard ./*.cpp。获取指定路径下的所有cpp文件。
  • wordlist:
  • words:

:=, =, ?=和+=的含义

  • :=:表示直接赋值,赋予当前位置的值。

    • 示例:VIR_B的值是A B,即根据当前位置进行赋值。

      VIR_A := A
      VIR_B := $(VIR_A) B
      VIR_A := AA
      
  • =: 使用”=”进行赋值,变量的值是整个makefile中最后被指定的值。

    • 示例:VIR_B的值是AA B,而不是A B。

      VIR_A = A
      VIR_B = $(VIR_A) B
      VIR_A = AA
      
  • ?=:表示如果该变量没有被赋值,则赋予等号后的值。

  • +=:表示将等号后面的值添加到前面的变量上

makefile示例2

CC = g++
LIBS = -lpthread
INCLUDE = -I../webserver/head

SRCDIR = ../webserver/src
HEADDIR = ../webserver/head

SRCFILES = $(SRCDIR)/*.cpp
HEADFILES = $(HEADDIR)/*.h

TARGET = webserver

$(TARGET): $(SRCFILES) $(HEADFILES)
        $(CC) $(INCLUDE) $(LIBS) $(SRCFILES) -o $(TARGET)

clean:
        rm -f $(TARGET)
posted on 2024-01-22 10:22  WilliamMoa  阅读(18)  评论(0)    收藏  举报