跳至正文

Makefile编写

内容目录

Makefile编写

本文大致介绍Makefile编写规则,已完成部分基础介绍,进阶使用待更新中。

概述

  • 注意每条命令前tab缩进,为makefile语法要求

默认情况下,makefile以第一个文件为最终文件,但可以通过all指定需要生成的文件。此后可以只使用make命令,会自动搜索all并将其后文件列为其依赖项

e.g.

all:target1 target2 target3

target1:
    #...

target2:
    #...

target3:
    #...
  • 当编写使用bash命令时可以在命令前加上@可以使得其该命令不会输出而只会回显结果
  • 合理利用echo命令可以更好地提示当前进程

变量

使用$来标识变量,当变量名超过一个字符时,需使用()。

其他一些使用如下:

  • $^ 表示所有依赖文件
  • $@ 表示生成的目标文件
  • $< 表示第一个依赖文件

e.g.

../$(OBJS_DIR)/main.o:main.c              
    @$(CC) -c $^ -o $@  

此例中,\$^为main.c而\$@为../\$(OBJS_DIR)/main.o

赋值

:=

:=为直接赋值,直接赋予变量当前值

?=

?=为当被赋值变量未被赋值时,则赋值,否则该赋值语句无效

+=

+=为变量添加+=后符号的值

=

=为赋予变量=后符号Makefile最终确定的值

  • 需与:=作比较,:=直接赋予变量在当前位置所代表的值,但=则是在Makefile结束时变量值确定下来后再赋值即可能不是当前位置值可能是后续更新的新值

函数

wildcard

通配符,用以匹配当前目录下所有符合制定规则文件

e.g.

SRC = $(wildcard ./*.c)

例为将当前目录下所有.c后缀结尾文件名赋值给SRC

patsubset

按指定参数替换

e.g.

OBJ = $(patsubst %.c, %.o, $(SRC))

例为将SRC值中文件.c后缀替换为.o后缀后赋给OBJ变量

filter-out

CPP_FILES := $(filter-out %_test.cpp, $(wildcard *.cpp))

例如这个命令首先使用wildcard函数获取当前目录下所有的.cpp文件,然后使用filter-out函数排除掉以_test.cpp结尾的文件。这样,CPP_FILES变量就包含了除了以_test.cpp结尾的所有.cpp文件。

notdir

CPP_FILES := $(notdir $(filter-out %_test.cpp, $(wildcard *.cpp)))

这个命令首先使用wildcard函数获取当前目录下所有的.cpp文件,然后使用filter-out函数排除掉以_test.cpp结尾的文件。最后,notdir函数被用来移除路径前缀,使得CPP_FILES变量中包含的文件名不带路径。

addprefix

CPP_FILES := $(notdir $(filter-out %_test.cpp, $(wildcard *.cpp)))
CPP_FILES := $(addprefix $(PATH), $(CPP_FILES))

这样,CPP_FILES变量中的每个文件名都会加上$(PATH)作为前缀。

clean

使用clean命令进行代码清理,清理make命令产生的所有文件

e.g.

clean:
    rm -rf $(OBJ)

嵌套执行Makefile

在大型工程中常将其他不同模块源文件放在不同目录下,我们可以在父目录下Makefile中编写总的Makefile而在不同子目录下编写各自的Makefile使得整体Makefile编译规则更整洁

e.g.

subsystem:
            cd subdir && $(MAKE)
其等价于:
subsystem:
            $(MAKE) -C subdir

该命令前往subdir目录并执行make命令

  • $(MAKE)是宏变量,整合参数,便于维护

  • 此类父目录下Makefile称为总控Makefile,其中的变量可以传递到下级Makefile中,但不会覆盖下级Makefile中定义的变量

    • 除非指定-e参数则会覆盖下级Makefile中定义的变量

    • 若需要(不需要)传递变量到下级Makefile中,可以使用export(unexport)

    • e.g.

    • export variable = value

指定头文件和库文件路径

编译时常通过"-I"(大写i)来指定头文件路径

e.g.

-I /home/headerfile/include

编写Makefile时,可以定义为一个变量,后续引用即可

e.g.

CFLAGS= -I /home/headerfile/include

yourapp:*.c
    gcc $(CFLAGS) -o yourapp

编译时常通过"-L"(大写l)来指定库文件路径

编写规则类似

LDFLAGS=-L/usr/lib -L/path/to/your/lib

简易示例

# 定义编译器
CC = gcc
# 定义编译选项
CFLAGS = -Wall -Wextra -g

# 定义源文件目录
SRCDIR = src
# 定义目标文件目录
OBJDIR = obj
# 定义可执行文件目录
BINDIR = bin

# 定义源文件和目标文件列表
SRCS = $(wildcard $(SRCDIR)/*.c)
OBJS = $(SRCS:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
EXEC = $(BINDIR)/my_program

# 默认目标为可执行文件
all: $(EXEC)

# 生成可执行文件
$(EXEC): $(OBJS)
    $(CC) $(CFLAGS) $^ -o $@

# 生成目标文件
$(OBJDIR)/%.o: $(SRCDIR)/%.c
    $(CC) $(CFLAGS) -c $< -o $@

# 清理命令
clean:
    rm -rf $(OBJDIR)/*.o $(BINDIR)/*

参考文档

  1. Makefile入门(超详细一文读懂) https://blog.csdn.net/ZBraveHeart/article/details/123187908