24fahed

博客园 首页 新随笔 联系 订阅 管理

makefile概述

makefile是一个自动化工具,在C语言项目管理中常用。以下整个笔记是为了应用而记录的,用词可能不准确。

makefile语法和组成部分

makefile由Macros和基础语法组成。

  • Macros: 一些宏定义,在整个makefile中可以使用$({MacrosName})对MacrosName进行引用

    Macros的定义和赋值是绑定在一起的。赋值有两种方式

    MACROS = VALUE $(OTHERMACROS)
    MACROS := VALUE $(OTHERMACROS)
    

    VALUE是一个字面量,OTHERMACROS是另一个MACROS。:= 表示立即展开赋值,MACROS的值将在之后不会根据OTHERMACROS改变而改变,=将记录整个展开过程,将在引用MACROS进行展开,确定当前MACROS的值。即如果需要MACROS是一个全局不会改变的确定值,则使用 := 。

  • 基本语法:

    {TARGET}: [{PREREQUEST}]
    <tab> 	{COMMEND}
    ...
    

    TARGET:生成目标

    PREREQUEST:在生成目标前,需要提前满足的需求

    需要执行的COMMEND前面必须使用tab进行缩进

    makefile可以由多个这样的基本语法结构组成。在makefile中第一个出现的TARGET将会被当作make的默认目标,即make会自动执行这个结构

    在下文中,每一个满足这种语法的结构,记为“make单元”。

makefile怎样执行

如果一个makefile中存在多个这样的语法结构,则执行方式如下:

  1. make工具是否指定TARGET

    如果make工具在运行时指定了make的TARGET(例如make test将查找TARGET为test的结构)将会从TARGET和make指定相同的make单元开始执行,否则默认执行在makefile中第一个出现的make单元

  2. 执行顺序

    如果A make单元的TARGET被B make单元作为PREREQUEST需要,则针对A make单元:

    • 建立TARGET:如果TARGET文件不存在,则这个make单元一定要执行
    • 更新TARGET:如果PREREQUEST文件的修改事件比TARGET文件新,则这个make单元执行
    • 否则:不执行

    这个过程是一个可以递归的过程,表示为:

    DEF execute_makefile (target) {
    	make_unit: {__target, __prerequest, __command} = find_global_makefile(target)
    	IF target NOT EXIST: // 对应第一条规则
    		execute_makefile(make_unit.__prerequest)
    		shell(make_unit.__command)
    		RETURN
    	ELSE IF make_unit.__prerequest.getEditTime() > make_unit.__target.getEditTime(): // 对应第二条规则
    		execute_makefile(make_unit.__prerequest)
    		shell(make_unit.__command)
    		RETURN
    	ELSE:
    		RETURN
    }
    

makefile的其他组成部分

makefile的其他常用组成部分包括

  • 其他符号

    $@

    当前make单元中的TARGET

    $<

    当前make单元中的PREREQUEST。

    如果TARGET或者PREREQUEST是由空格分割的多个值,例如TARGET=file1 file2 file3,则使用$@只能输出file1

  • 其他语法结构(例如循环结构等)

    makefile也有循环结构和判断结构。其中循环结构为:

    for {D} in {VALUES} ; do \
      {具体执行的事务} ; \
    done
    
  • 伪目标

    语法: .PHONY: FANKTARGET, ...

    功能:FANKTARGET代替TARGET

  • 引用

    语法:$(MacrosName)

    功能:引用定义的Macros

    特殊: $$转义$,即$$等于一个文本意义上的$

  • 内置函数

    $(wildcard <pattern>)

    功能:这个函数将查找pattern中指定的文件,文件名之间用空格分割

    返回:查找到的所有文件

    示例:

    FILES = $(wildcard ./testdir/*.c)

    FILES为 ./testdir/test.c ./testdir/main.c


    $(addprefix <prefix>, <names...>)

    名称:加前缀函数——addprefix。

    功能:把前缀<prefix>加到中的每个单词后面。

    返回:返回加过前缀的文件名序列。

    示例:$(addprefix src/,foo bar)返回值是“src/foo src/bar”。


    $(addsuffix <suffix>, <names...>)

    名称:加后缀函数——addsuffix。

    功能:把后缀<suffix>加到<names>中的每个单词后面。

    返回:返回加过后缀的文件名序列。

    示例:$(addsuffix .c,foo bar)返回值是“foo.c bar.c”


    $(filter <pattern...>, <text>)

    名称:过滤函数——filter。

    功能:以<pattern>模式过滤<text>字符串中的单词,保留符合模式<pattern>的单词。可以有多个模式。

    返回:返回符合模式的字串。


    $(patsubst <pattern>, <replacement>, <text>)

    名称:子串替换——patsubst

    功能:将<text>中匹配<pattern>的子串替换为<replacement>

    返回:替换过后的<text>

    示例:

    FILES := ./testdir/test1.c ./testdir/test2.c
    OUT_FILES := $(patsubst %.c,%.o,$(patsubst ./testdir/%,./out/%,$(FILES)))
    

    OUT_FILES结果为:./out/test1.o ./out/test2.o


具体场景

  1. TARGET和PREREQUEST包含多个文件

    这种情况一般出现在多个文件编译过程中。例如:

    SRC_C = ./test.c ./happend.c
    OBJ_O = ./test.o ./happend.o
    
    SRC_ASM = ...
    OBJ_ASM = ...
    
    .PHONY: all clean
    
    all: $(OBJ_O) $(OBJ_ASM)
        $(LD) $(OBJ_O) $(OBJ_ASM) $(LDFLAGS) -o $(PROJECT).elf
    
    $(OBJ_O):%o: %c
    	$(CC) $(CFLAG) $< -o $@
    $(OBJ_ASM):%o: %s
        $(AS) $(ASFLAG) $< -o $@
    

    上述流程为C语言项目编译过程中常用,命令行输入make all会将每一个在OBJ_O中的文件,首先将其后缀.o更换为.c作为PREREQUEST,再执行指定COMMAND;在OBJ_ASM中的文件也是用羊的处理方法。这段makefile首先会将指定的SRC_C和SRC_O进行编译并将编译结果存放在和源文件相同的文件夹中,之后再将他们链接生成一个{PROJECT}.elf文件。

参考资料

比较详细的makefile快速入门(有C语言情况下的具体使用情况)

stm32f10x项目的一个makefile示例

不知道那个大学的快速入门makefile的pdf

一个很好的stm32入门书籍的开源代码,其中包含makefile更加高级的用法

posted on 2024-04-09 12:14  24-Fahed  阅读(91)  评论(0)    收藏  举报