Linux下对Makefile的使用

Makefile概述

在Linux中,Makefile是使用make工具进行项目管理和自动化编译的核心。Makefile 是一个特殊格式的文件。Makefile 定义了一组规则和依赖关系,指导 make 如何编译和链接程序

 目的:

Makefile解决的是编译的问题。编译有什么问题呢?

例如,你有3个.c文件,分别是linked.c,hash.c,main.c,你要编译成一个app.executable,你会怎么做呢?

根据Linux程序员的惯例,凡是要一次次重新执行的命令,都应该写成脚本,变成“一个动作”。所以,把上面这个命令序列写成一个build.sh,每次编译你只要执行这个脚本问题就解决了。但这个脚本有问题,假设我修改了linked.c,但我没有修改hash.c和main.c,那么执行这个脚本是很浪费的,因为它会无条件也重新编译hash.c和main.c。如果你面对一个问题,不要尝试重新去定义这个问题,而是看它和原来的问题相比,多出来的问题是什么,尝试解决那个多出来的问题就好了。多出来的问题就是文件修改时间比较,这个就是Makefile要解决的基本问题了。我们定义一种新的“脚本语言”(只是不用bash来解释,而是用make来解释)

​app.executable : hash.o main.o linked.o        
    gcc *.o -o app.executable
hash.o : hash.c        
    gcc -c hash.c -o 
hash.olinked.o : linked.c        
    gcc -c linked.c -o linked.o
main.o : main.c        
    gcc -c main.c -o main.o
clear :        
    rm -rf *.o

Makefile的本质是一个文件,需要配合make命令进行自动化编译

Makefile文件的命名:Makefile

make是一个命令工具,用来解释makefile文件中的代码,从而实现自动化编译。编译使用的编译器本质上还是gcc。

Makefile定义了项目的构建规则和依赖关系,使得编译过程可以自动化,确保源代码及其依赖项在修改后能够正确地重新编译。

 基本组件

  • 目标(Targets)

  • 目标是 Makefile 中的构建对象,可以是可执行文件、库文件或任何其他文件。每个目标可以有一组依赖项和一个或多个构建规则。
  • 依赖项(Dependencies)

  • 依赖项是构建目标所需的文件或目标列表。当依赖项发生变化时,make 将根据规则重新构建目标
  • 规则(Rules)

  • 规则是一系列指令,定义了如何从依赖项生成目标。规则可以包括编译器调用、文件复制操作等

核心概念:

1.. 模式规则(Pattern Rules)

模式规则是一种通用的构建规则,可以根据文件扩展名或其他模式自动应用到多个目标。

2.. 伪目标(Phony Targets)

当makefile目录下有一个和目标相同的文件时,例如cleanall文件。我们在执行make命令的时候会出现错误。伪目标就是用于解决此种错误而产生。伪目标只是一个标签

伪目标不对应实际的文件,而是用于触发一系列操作,如 clean 用于清理构建生成的文件。

3.. 变量(Variables)

变量用于存储可重用的值,如编译器路径、编译选项、源文件列表等。

4.. 条件语句(Conditional Statements)

Makefile 支持简单的条件语句,如 ifeqifneqifdefifndef,允许根据不同的条件包含不同的构建规则。

5.. 函数(Functions)

make 提供了一系列内置函数,用于执行字符串处理、文件名生成、查找文件等操作。

  • 1.patsubst(pattern, replacement, text)
  • 说明:
  • <pattern>:要匹配的模式。
  • <replacement>:用于替换匹配到的模式的字符串。
  • <text>:要进行模式替换的原始文本。
  • 示例:
  • OBJ = $(patsubst %.c, %.o, $(txt))
    #功能:这个函数有三个参数,意思是取出txt中的源文件列表,然后将.c 替换为.o 最后赋值给OBJ变量
  • 2.wildcard(pattern...)
  • 说明:
  • <pattern>...:一个或多个匹配模式,通常使用通配符 * 和 ?* 匹配任意数量的字符,? 匹配单个字符。
  • 示例:
  • SRC = $(wildcard ./*.c)
    #功能: 匹配目录下所有.c 文件,并将其赋值给SRC变量

6. 递归

构建(Recursive Builds)

Makefile 可以调用子目录中的 Makefile,实现项目的递归构建。

7. 包含(Include)

使用 include 指令可以将一个 Makefile 的内容包含到另一个 Makefile 中,实现规则和变量的共享。

8. 清理和维护(Cleaning and Maintaining)

Makefile 通常包含清理规则,用于删除所有构建生成的文件,帮助维护项目目录的整洁。

9. 可移植性(Portability)

Makefile 旨在跨平台工作,但某些特定于平台的规则或变量可能需要额外的处理。

示例代码:

​# 定义编译器和链接器
CC=gcc
LD=ld

# 声明伪目标
.PHONY: all clean

# 定义编译选项
CFLAGS=-Wall -g
LDFLAGS=

# 定义源文件变量
SOURCES=main.c foo.c bar.c
# 通过源文件生成对象文件列表
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))

# 默认目标(伪目标)
all: my_program

# 模式规则:从 .c 文件生成 .o 文件
%.o: %.c
	$(CC) $(CFLAGS) -c $< -o $@

# 目标:可执行文件,依赖于所有的 .o 文件
my_program: $(OBJECTS)
	$(CC) $(LDFLAGS) -o $@ $^

# 伪目标:清理构建生成的文件
clean:
	rm -f my_program $(OBJECTS)

# 条件语句示例
ifeq ($(CC),gcc)
	CFLAGS += -std=c99
endif

# 包含其他 Makefile
include debug.mk

# 使用内置函数
SOURCE_NAMES=$(notdir $(SOURCES))

 CFLAGS 变量

  • CFLAGS 用于存储编译器的选项,这些选项通常用于控制编译器的行为和生成代码的方式。
  • 在代码中,CFLAGS 被设置为 -Wall -g,其中:
    • -Wall 是一个编译器选项,用于打开几乎所有的警告信息,帮助开发者发现潜在的问题。
    • -g 是一个编译器选项,用于生成调试信息。这使得在程序运行时可以使用调试器(如 gdb)进行调试。

LDFLAGS 变量

  • LDFLAGS 用于存储链接器的选项,这些选项用于控制链接器的行为和生成的可执行文件的属性。
  • 在代码中,LDFLAGS 没有被设置任何值(为空字符串),这意味着链接器将使用默认的行为。如果需要,可以添加链接器选项,如指定库的搜索路径或设置生成的可执行文件的属性。

在 Makefile中处理库

在Makefile中处理库通常涉及几个关键步骤:编译源文件为库文件、指定库文件的路径以及链接时包含库

1. 编译源文件为库文件

库文件可以是静态库(.a)或动态库(.so)

2. 指定库文件的路径

在链接阶段,你需要告诉链接器库文件的位置。可以使用 -L 选项指定库的路径,并使用 -l 选项链接库

# 定义库的路径
LIB_PATH=-L./

# 定义要链接的库
LIBS=-lmylib

3. 创建库

a.静态库

# 定义编译器
CC=gcc

# 定义编译选项
CFLAGS=-Wall -g

# 定义源文件
SOURCES=main.c foo.c bar.c

# 定义库文件
LIB_NAME=mylib
STATIC_LIB=$(LIB_NAME).a

# 规则:创建静态库
$(STATIC_LIB): $(SOURCES)
	$(AR) rcs $@ $^

# 伪目标:清理库文件和对象文件
clean:
	rm -f $(STATIC_LIB) *.o

b.动态库

# 定义动态库文件
DYNAMIC_LIB=$(LIB_NAME).so

# 规则:创建动态库
$(DYNAMIC_LIB): $(SOURCES)
	$(CC) $(CFLAGS) -shared -fPIC -o $@ $^

# 更新伪目标 clean
clean:
	rm -f $(STATIC_LIB) $(DYNAMIC_LIB) *.o

 -shared-fPIC(Position Independent Code)选项用于生成动态库

4. 链接动态库

链接动态库时,需要确保链接器能够找到库文件,并链接所需的符号。这通常通过设置 LD_LIBRARY_PATH 环境变量或使用 ldconfig 实现。

Makefile执行的主要原理和步骤

Makefile 的执行原理基于一组规则和依赖关系,这些规则和依赖关系定义了项目的构建过程

1. 解析 Makefile:

 当运行make命令时,make工具首先解析当前目录下的 Makefile(或指定的 Makefile 文件)。

2. 确定默认目标:

如果没有指定目标,make 将使用 Makefile 中的第一个目标作为默认目标。

3. 检查依赖关系:

 对于每个目标,make 检查其依赖文件的最后修改时间。如果依赖文件比目标文件新,或者目标文件不存在,则需要重新构建目标。

4. 读取规则:

make 查找与目标相关联的规则,这些规则定义了如何构建目标。

5. 执行命令:

根据规则中的命令,make逐行执行命令来构建目标。这些命令通常包括调用编译器、链接器或复制文件等。

6. 使用变量:

在执行过程中,make 替换 Makefile 中的变量为它们的值。

7. 处理函数和模式:

make处理 Makefile 中的函数调用和模式规则,以生成所需的文件名和路径。

8. 递归构建:

如果 Makefile 调用子目录中的 Makefile,make 将递归地进入子目录并执行相应的构建规则。

9. 处理伪目标:

对于伪目标(如 clean),make 执行与之关联的命令,但不会检查文件的依赖关系。

10. 使用内置规则:

如果没有为目标提供规则,make将尝试使用内置规则来构建目标,例如根据文件扩展名推断如何构建。

11. 处理条件语句:

make根据条件语句(如 ifeq、ifneq、ifdef、ifndef)包含或排除特定的构建规则

12.处理包含文件

当遇到 include 指令时,make 将读取并解析指定的文件,将它们的内容作为 Makefile 的一部分。

13. 生成隐式规则:

make 可以生成隐式规则来处理常见的编译任务,如从 .c文件编译成 .o文件。

14. 处理文件的最后修改时间:

 构建完成后,make更新目标文件的最后修改时间,以便在下次构建时正确地检查依赖关系。

15. 错误处理:

 如果在执行命令时遇到错误,make将停止构建过程,并报告错误信息。

16. 返回状态码:

make 返回状态码以指示构建成功(0)或失败(非0)。

Makefile多文件管理

Makefile的多文件管理是指在构建系统中有效组织和编译多个源文件、头文件、资源文件等,以提高构建效率和可维护性

实际开发中我们不可能只有几个文件, 类似于上图多文件,面对多文件处理,我们会把文件分为各种各样的模块来进行管理,以便于我们处理文件,在工作中提高效率

Makefile在管理多文件项目时提供了多种功能和技巧,可以帮助我们更有效地组织和构建项目。

 在主目录下建立多个子目录和主Makefile文件,子目录下存放相关文件和子Makefile文件

如上图:

主Makefile文件(主Makefile调用子Makefile)

通过Makefile实现对多文件管理

PHONY : cleanall
GCC = gcc
RMRF = rm -rf
SRC = ${shell pwd}
INCLUDE_DIR = -I $(SRC)/include/
MAIN_DIR = $(SRC)/main
FUNS_DIR = $(SRC)/funs
OBJ_DIR = $(SRC)/obj
BIN_DIR = $(SRC)/bin
SUB_DIR := funs main obj

APPEXE := app.exe

export GCC INCLUDE_DIR MAIN_DIR FUNS_DIR OBJ_DIR BIN_DIR SUB_DIR APPEXE

all : $(SUB_DIR)
        @echo "success project"
$(SUB_DIR) : MK_BIN
        make -C $@
MK_BIN :
        mkdir $(BIN_DIR)
cleanall :
        $(RMRF) $(OBJ_DIR)*.O $(BIN_DIR)

bin目录中存放最终生成的可执行文件

funs目录中存放相关.c文件(main.c除外)

funs目录下的子Makefile文件

SCR = $(wildcard ./*.c)
OBJ = $(patsubst %.c,%.o,$(SCR))

all : $(OBJ)
        @echo "success funs"
$(OBJ):%.o:%.c
        $(GCC) -c $(INCLUDE_DIR) $< -o $(OBJ_DIR)/$@

main目录中存放main.c文件

main目录下的子Makefile文件

SCR = $(wildcard ./*.c)
OBJ = $(patsubst %.c,%.o,$(SCR))

all : $(OBJ)
        @echo "success funs"
$(OBJ):%.o:%.c
        $(GCC) -c $(INCLUDE_DIR) $< -o $(OBJ_DIR)/$@

include目录中存放相关.h文件(无子Makefile文件)

obj目录中存放相关临时生成文件( .o文件等)

obj目录下的子Makefile文件

$(BIN_DIR)/$(APPEXE) : *.o
        $(GCC) $(INCLUDE_DIR) $^ -o $@

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值