Makefile简介
Makefile作用
Makefile是一个用于自动构建过程的文件,常用于在Uinx/Linux环境下管理编译和链接源代码的步骤。它是Make工具的输入文件,定义了如何从源文件生成目标文件(例如可执行文件或库),以及清理构建目录等任务。通过Makefile,可以简化和自动化编译过程,避免手动输入繁琐的编译命令。
自动化构建过程
定义构建规则和依赖关系,自动化执行编译和链接步骤。
通过一条简单的命令(通常是 make),完成从源代码到可执行文件的构建。
依赖管理
追踪文件之间的依赖关系,只重新编译那些发生改变的文件,节省编译时间。
通过检查文件的时间戳,确保所有依赖关系都得到满足。
提高效率
减少重复工作,避免手动输入多个编译命令。
提供一种一致且可重复的构建方法,降低出错的可能性。
灵活性和可扩展性
可以通过变量和条件判断增加灵活性,适应不同的构建环境和配置。
支持自定义规则和目标,可以扩展构建过程,例如生成文档、运行测试等
Makefile 的基本结构
变量定义
- 用于简化命令和路径的管理,例如源文件列表,编译器选项等
CC = gcc
CFLAGS = -Wall -g -O2
SRCS = main.c utils.c
OBJS = $(SRCS:.c=.o)
规则定义
- 每个规则定义了如何从一组依赖文件生成目标文件。规则由目标(target)、依赖(dependencies)和命令(commands)组成。
target: dependencies
command
- 示例
all: my_program
my_program: main.o utils.o
$(CC) -o my_program main.o utils.o $(CFLAGS)
main.o: main.c
$(CC) -c main.c $(CFLAGS)
utils.o: utils.c
$(CC) -c utils.c $(CFLAGS)
自动变量
- 在规则的命令部分使用的特殊变量,例如:
$@:目标文件名。
$<:第一个依赖文件名。
$^:所有依赖文件名
main.o: main.c
$(CC) -c $< -o $@ $(CFLAGS)
伪目标
- 不生成文件的目标,用于定义一些常见任务,如清理构建目录。
.PHONY: clean
clean:
rm -f my_program *.o
示例
CC = gcc
CFLAGS = -Wall -g -O2
SRCS = main.c utils.c
OBJS = $(SRCS:.c=.o)
TARGET = my_program
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) -o $@ $^ $(CFLAGS)
%.o: %.c
$(CC) -c $< -o $@ $(CFLAGS)
.PHONY: clean
clean:
rm -f $(TARGET) $(OBJS)
在这个例子中:
- CC 定义了编译器。
- CFLAGS 定义了编译器选项。
- SRCS 列出了源文件。
- OBJS 将源文件名转换为对象文件名。
- TARGET 定义了目标可执行文件的名称。
- all 是默认目标,用于构建整个项目。
- $(TARGET)依赖于所有对象文件,描述了如何链接这些对象文件生成可执行文件
- %.o: %.c 是一个模式规则,用于描述如何从源文件生成对象文件。
- clean 是一个伪目标,用于清理构建生成的文件。
Makefile各种文件说明和关系
源文件
- 定义:源文件是用编程语言(如C、C++)编写的文本文件,包含程序的代码和逻辑
- 扩展名:通常以 .c、.cpp、.h 等为扩展名。
- 作用:源文件是编译器的输入,编译器将源文件转换为目标文件。
目标文件
- 定义:目标文件是编译器将源文件编译后的中间文件,包含机器代码和数据,但还未链接成可执行文件。
- 扩展名:通常以 .o 或 .obj为扩展名。
- 作用:目标文件是链接器的输入,链接器将多个目标文件和库文件链接成可执行文件
依赖文件
- 定义:依赖文件描述了源文件之间的依赖关系,通常由编译器生成,用于确定哪些文件需要重新编译。
- 扩展名:通常以 .d 为扩展名。
- 作用:依赖文件用于构建系统(如 Makefile)来管理源文件的编译过程,确保当源文件改变时,相关的目标文件能够被重新编译。
静态库文件
- 定义:静态库文件是多个目标文件的集合,打包成一个归档文件,可以在链接时被包含到可执行文件中。
- 扩展名:通常以 .a为扩展名(在Windows上为 .lib)。
- 作用:静态库文件用于代码重用和模块化,可以被多个项目共享和链接。
文件之间的关系和编译流程
- 源文件到目标文件:编译器将源文件(如 main.c)编译成目标文件(如 main.o)。
- 目标文件和依赖文件:编译过程中生成依赖文件(如 main.d),用于跟踪文件依赖关系。
- 目标文件到可执行文件:链接器将多个目标文件(如 main.o、utils.o)和静态库(libmylib.a)链接成可执行文件(如 my_program)。
- 静态库的使用:静态库(如libmylib.a)是多个目标文件的集合,可以在链接时被包含到可执行文件中,提供所需的功能和代码。
具体Makefile文件解析
#-----------------------------------变量定义-------------------------------#
#使用wildcard函数来获取符合路径模式的所有.c文件,包括./phy目录下的二级和三级子目录中的.c文件,以及./l1c目录下的所有子目录中的.c文件
PHY_LIB = $(wildcard ./phy/*/*/*.c) $(wildcard ./phy/*/*.c) $(wildcard ./l1c/*/*.c)
#包含所有的源文件,即前面定义的PHY_LIB和当前目录下的rrm_meas_test.c文件。
SRCS = $(PHY_LIB) rrm_meas_test.c
#将所有的.c文件扩展名替换为.o,生成对象文件列表
OBJS = $(SRCS:.c=.o)
#定义编译器为icc
CC = icc
# 定义链接时需要的库,包括数学库、线程库、MKL(Intel Math Kernel Library)、OpenMP库、实时库和NLopt优化库
LIBS = -lm -lpthread -lmkl_rt -mkl=sequential -fopenmp -lrt -lnlopt
#定义编译选项
CCFLAGS = -Wall -g -O3 -march=core-avx2 -std=c99 -m64 -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -D_LARGE_FILE_SOURCE -MMD #附加参数
#定义生成的可执行文件名为rrm_meas_test
OUTPUT = rrm_meas_test
#-----------------------------------规则定义---------------------------------#
#默认目标是生成$(OUTPUT)
all: $(OUTPUT)
#依赖所有对象文件$(OBJS)。使用Intel编译器将这些对象文件链接成可执行文件,并添加指定的库和-pg(生成用于gprof的分析信息)选项
$(OUTPUT): $(OBJS)
$(CC) $^ -o $@ $(LIBS) -pg
#包括所有的依赖文件(.d文件),这些文件由编译器生成,用于描述各源文件的依赖关系。-include表示即使这些文件不存在,Make也不会报错
-include *.d
-include ./phy/*/*.d
-include ./phy/*/*/*.d
-include ./l1c/*/*.d
#任何.c文件对应的.o文件,使用Intel编译器进行编译。$<代表依赖文件(.c文件),$@代表目标文件(.o文件),使用$(CCFLAGS)作为编译选项
%.o: %.c
$(CC) -c $< -o $@ $(CCFLAGS)
#没有命令,这个目标仅仅用于依赖可执行文件。
run: $(OUTPUT)
#依赖可执行文件,使用valgrind进行内存泄漏检查,输出详细的内存泄漏信息和错误来源。
memcheck: $(OUTPUT)
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./$(OUTPUT)
#-----------------------------------伪目标--------------------------------------#
#声明为伪目标。clean是一个伪目标,用于清理构建生成的文件
.PHONY: clean
#删除所有生成的目标文件(.o)、依赖文件(.d)和可执行文件(rrm_meas_test),包括不同目录下的文件。
clean:
rm -rf reg_rw *.o *.d *.bin rrm_meas_test
rm -rf ./phy/*/*.o
rm -rf ./phy/*/*.d
rm -rf ./phy/*/*/*.o
rm -rf ./phy/*/*/*.d
rm -rf ./l1c/*/*.o
rm -rf ./l1c/*/*.d