Back_To_Home..
Makefile
January 2015 (644 Words, 4 Minutes)
Makefile是用于自动化构建项目的文件,其中包含了一系列规则和指令,用于告诉make工具如何编译和链接程序。以下是Makefile的基本语法:
规则语法
- 基本格式:规则是Makefile的核心,其基本格式为
targets : prerequisites,其中targets是目标文件或执行动作的名称,prerequisites是生成目标所依赖的文件或其他目标,二者用冒号:分隔。如main : main.o utils.o,表示main目标依赖于main.o和utils.o文件。 - 命令:规则的下一行是命令,用于指定如何从依赖项生成目标。命令必须以制表符
\t开头,如:
main : main.o utils.o
gcc -o main main.o utils.o
注释与引用
- 注释:以
#开头的行是注释行,会被make工具忽略,如# 这是一个注释。 - 引用其他Makefile:使用
include关键字可以引入其他Makefile文件,如include other.mk。
变量
- 定义变量:可以使用
=、:=、?=和+=等操作符定义变量。如CC := gcc定义了一个名为CC的变量,其值为gcc。 - 使用变量:使用
$(变量名)或${变量名}的形式来引用变量,如$(CC) -o main main.o。
模式规则
- 定义:模式规则使用通配符
%来匹配一类文件,用于定义如何从一类源文件生成一类目标文件。如%.o : %.c表示所有的.o文件都可以从对应的.c文件生成。 - 自动变量:在模式规则的命令中,可以使用自动变量。常见的自动变量有
$@(表示目标文件)、$<(表示第一个依赖文件)、$^(表示所有的依赖文件)等。如:
%.o : %.c
$(CC) -c $< -o $@
伪目标
- 定义:伪目标不是真正的文件,而是用于执行特定的命令或动作。通常使用
.PHONY特殊目标来声明伪目标,如.PHONY : clean。 - 使用:伪目标常用于定义一些辅助操作,如清理编译生成的文件。示例如下:
.PHONY : clean
clean:
rm -f *.o main
条件判断与函数
- 条件判断:使用
ifeq、ifneq、ifdef和ifndef等关键字进行条件判断。如ifeq ($(OS),Windows),判断变量OS的值是否为Windows。 - 函数:Makefile支持一些内置函数,用于字符串处理、文件操作等。如
wildcard函数用于获取指定目录下的文件列表,src_files := $(wildcard src/*.c)将src目录下所有.c文件的文件名赋值给src_files变量。
以下是几个不同场景下的 Makefile 示例,帮助你理解它的基本结构和用法:
简单的编译 C 语言程序示例
假设你有一个 main.c 文件,代码如下:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
对应的 Makefile 可以这样写:
# 定义编译器
CC = gcc
# 定义编译选项
CFLAGS = -Wall -g
# 目标文件(最终生成的可执行文件)
my_program: main.o
$(CC) $(CFLAGS) -o my_program main.o
# 依赖规则,如何生成 main.o
main.o: main.c
$(CC) $(CFLAGS) -c main.c
CC变量定义了使用的编译器,这里指定为gcc。CFLAGS变量设置了编译选项,-Wall表示开启所有警告,-g便于后续调试。my_program是最终要生成的可执行文件目标,它依赖于main.o,后面跟着的命令就是如何从依赖文件生成目标文件(使用gcc进行链接操作)。main.o又依赖于main.c,其对应的命令是用gcc进行编译生成对应的目标文件(.o文件)。
编译多个源文件的 C 语言项目示例
假设有 main.c、func1.c 和 func2.c 三个源文件,代码示例如下:
main.c:
#include <stdio.h>
#include "func1.h"
#include "func2.h"
int main() {
printf("Result: %d\n", func1() + func2());
return 0;
}
func1.c:
#include "func1.h"
int func1() {
return 10;
}
func1.h:
#ifndef FUNC1_H
#define FUNC1_H
int func1();
#endif
func2.c:
#include "func2.h"
int func2() {
return 20;
}
func2.h:
#ifndef FUNC2_H
#define FUNC2_H
int func2();
#endif
对应的 Makefile 如下:
# 定义编译器
CC = gcc
# 定义编译选项
CFLAGS = -Wall -g
# 目标文件(最终生成的可执行文件)
my_project: main.o func1.o func2.o
$(CC) $(CFLAGS) -o my_project main.o func1.o func2.o
# 依赖规则,如何生成 main.o
main.o: main.c func1.h func2.h
$(CC) $(CFLAGS) -c main.c
# 依赖规则,如何生成 func1.o
func1.o: func1.c func1.h
$(CC) $(CFLAGS) -c func1.c
# 依赖规则,如何生成 func2.o
func2.o: func2.c func2.h
$(CC) $(CFLAGS) -c func2.c
# 清理生成的目标文件和可执行文件的规则
clean:
rm -f *.o my_project
- 同样先定义了
CC和CFLAGS。 my_project目标依赖于三个.o文件,通过gcc链接它们生成最终可执行文件。- 分别为每个
.o文件定义了依赖和生成的规则,指明了对应的源文件和头文件情况。 - 新增了
clean目标,用于清理编译过程中生成的中间目标文件(.o文件)和最终的可执行文件,方便重新编译等操作。
编译 Python 项目示例(将 Python 代码打包成可执行文件,假设使用 PyInstaller)
假设有一个简单的 main.py 文件,内容如下:
print("Hello from Python!")
对应的 Makefile 如下:
# 定义使用的打包工具(这里是 PyInstaller)
PYINSTALLER = pyinstaller
# 目标文件(最终生成的可执行文件,这里假设是在 Linux 系统下生成单文件可执行文件)
my_python_app:
$(PYINSTALLER) --onefile main.py
# 清理生成的相关文件和目录的规则
clean:
rm -rf build dist __pycache__
- 首先定义了
PYINSTALLER变量为pyinstaller。 my_python_app目标执行pyinstaller命令将main.py打包成可执行文件,根据配置生成在dist目录下(--onefile表示生成单个可执行文件)。clean目标用于清理pyinstaller打包过程中生成的build目录、dist目录以及__pycache__等相关文件和目录。
这些 Makefile 示例展示了基本的构建逻辑,你可以根据实际项目的复杂程度、编程语言、构建需求等进行更多扩展和调整。