龙空技术网

Makefile使用介绍

小问号粑粑 101

前言:

目前看官们对“c语言makefile怎么用”大约比较着重,朋友们都想要分析一些“c语言makefile怎么用”的相关知识。那么小编同时在网上搜集了一些有关“c语言makefile怎么用””的相关文章,希望大家能喜欢,同学们快快来学习一下吧!

1. 背景

工具的语法,主要用于描述源代码文件之间的依赖关系,通过规则定义了编译和链接的步骤。

随着开源软件的普及和多平台开发的需求,Makefile的使用逐渐扩展到不同的操作系统和编程语言中。GNU Make是GNU项目开发的一款强大的构建工具,它取代了早期的Make工具,并引入了更多功能和特性,如变量、条件判断、循环等,使得Makefile更加灵活和可配置。

在大型项目中,Makefile特别有用,因为它可以自动化构建和管理程序。Makefile以文本文件的形式存在,包含了一系列规则和指令,用于描述程序的依赖关系以及构建步骤。核心思想是根据文件的最后修改时间来确定哪些部分需要重新编译,以及以什么顺序来执行编译步骤。每个规则由一个目标(target)和一组依赖项(dependencies)组成,以及执行指令(commands)。这样,一旦写好Makefile,只需要一个make命令,整个工程就会完全自动编译,极大地提高了软件开发的效率。

总的来说,Makefile是一个强大的自动化构建工具,随着软件开发的复杂性和规模性的增加,其在项目管理中的作用也越发重要。

2. Makefile的基本语法

Makefile的基本语法主要包括规则、变量、函数和通配符的使用。下面是一个简要的概述:

1. 规则

Makefile的核心是规则,其一般形式为:

目标(target): 依赖项(prerequisites)<tab>命令(commands)
目标通常是要生成的文件的名称,如可执行文件或对象文件(.o文件)。依赖项是用来生成目标的文件或条件。命令是生成目标时执行的shell命令,每个命令前必须有一个Tab字符。

2. 变量

Makefile支持变量的使用,以简化书写和增强灵活性。变量名以美元符号($)开头,后面跟着变量名。可以在规则中的任何位置使用变量,Make在执行时会将其替换为变量的值。

CC = gccCFLAGS = -Wallmyprogram: main.o util.o    $(CC) $(CFLAGS) -o myprogram main.o util.o

3. 函数

Makefile还提供了丰富的函数,用于处理字符串、文件名、条件判断等复杂的操作。例如,wildcard函数可以用来获取匹配特定模式的所有文件列表。

SRCS = $(wildcard *.c)

4. 通配符

Makefile支持使用通配符来匹配文件名,常见的通配符有*(匹配任意字符序列)和?(匹配任意一个字符)。这有助于编写更通用的规则。

5. 注释

以井号(#)开头的行被视为注释,不会被执行。这有助于在Makefile中添加说明和调试信息。

6. 条件判断和循环

Makefile还支持条件判断和循环结构,这使得可以根据不同的条件执行不同的命令序列,或者重复执行某些命令。

7. 包含其他Makefile

使用include指令可以将其他Makefile文件的内容包含到当前Makefile中。

include anotherfile.mk

8. 伪目标

伪目标(phony targets)是用来执行一些操作的特殊目标,它们不代表真实的文件名。常用于定义一些不生成文件的操作,如clean

.PHONY: cleanclean:    rm -f *.o myprogram

在编写Makefile时,要注意规则的书写顺序、变量的命名和作用域、函数的使用方法以及通配符的正确使用,以确保Makefile能够正确、高效地执行构建任务。同时,为了可读性和可维护性,建议将Makefile写得清晰、简洁,并适当添加注释。

3. Makefile常用变量

Makefile中常用的变量主要用于存储各种信息,以便在构建过程中重复使用。这些变量可以代表文件名、路径、编译器选项、标志等,帮助简化Makefile的编写并提高构建过程的可维护性。以下是一些Makefile中常用的变量:

源文件和目标文件:SRCS:表示源文件的列表。OBJS:表示编译后生成的目标文件列表。TARGET:表示最终生成的可执行文件或库文件的名字。编译器和编译器选项:CC:C编译器的名字,通常是gccCXX:C++编译器的名字,通常是g++CFLAGS:C编译器的选项。CXXFLAGS:C++编译器的选项。LDFLAGS:链接器的选项。目录和路径:SRCDIR:源文件的目录。OBJDIR:目标文件的目录。BINDIR:可执行文件的目录。INCDIR:头文件的搜索目录。工具链和版本:AR:归档工具,用于创建静态库。RANLIB:对静态库进行索引的工具。VERSION:项目或库的版本号。调试和优化选项:DEBUG:一个标志,用于控制是否启用调试选项。OPTIMIZE:一个标志,用于控制是否启用优化选项。自动变量:$@:表示目标文件的名字。$<:表示第一个依赖文件的名字。$^:表示所有依赖文件的名字列表。$?:表示所有比目标更新的依赖文件的名字列表。$(@D)$(@F):分别表示目标文件的目录和文件名部分。

这些变量可以根据项目的需要进行自定义和扩展。在Makefile中,你可以通过赋值操作给这些变量赋具体的值,并在构建规则中使用它们。例如:

CC = gccCFLAGS = -Wall -gSRCS = main.c util.cOBJS = $(SRCS:.c=.o)TARGET = myprogramall: $(TARGET)$(TARGET): $(OBJS)    $(CC) $(LDFLAGS) -o $@ $^%.o: %.c    $(CC) $(CFLAGS) -c $< -o $@

在这个例子中,CCCFLAGSSRCSOBJSTARGET 都是变量,它们在Makefile中被定义并用于构建规则中。通过合理使用这些变量,可以极大地提高Makefile的可读性和可维护性。

4. Makefile函数

Makefile中提供了大量的函数,用于处理文件名、字符串、变量等操作,以支持复杂的构建逻辑。主要包括如下类型:

字符串处理函数文件名处理函数控制函数文本处理函数复杂操作函数自定义函数

Makefile中提供了大量的函数,用于处理文件名、字符串、变量等操作,以支持复杂的构建逻辑。以下是一些常用的Makefile函数及其介绍:

4.1. 字符串处理函数

Makefile提供了多个用于处理字符串的函数,这些函数在构建过程中非常有用,尤其是在处理文件名、变量替换、路径拼接等场景。以下是Makefile中常用的字符串处理函数:

$(subst <from>,<to>,<text>):字符串替换函数。这个函数将<text>字符串中的<from>替换为<to>。示例:$(subst ee,EE,feet on the street) 返回 "fEEt on the strEEt"。$(patsubst <pattern>,<replacement>,<text>):模式字符串替换函数。这个函数在<text>中查找符合<pattern>模式的单词,并用<replacement>替换它们。<pattern>可以包含通配符%,代表任意长度的字符串。示例:$(patsubst %.c,%.o,main.c util.c) 可能返回 "main.o util.o"。$(strip <string>):去空格函数。这个函数返回<string>去掉开头和结尾空格后的结果。示例:$(strip a b c ) 返回 "a b c"。$(findstring <find>,<in>):查找字符串函数。这个函数在<in>中查找<find>字符串,如果找到则返回<find>,否则返回空字符串。示例:$(findstring a,abcdefg) 返回 "a"。$(filter <pattern>,<text>):过滤函数。这个函数从<text>中筛选出所有符合<pattern>模式的单词,并返回这些单词组成的列表。示例:$(filter %.o,main.o util.o foo.c) 返回 "main.o util.o"。$(filter-out <pattern>,<text>):反过滤函数。这个函数从<text>中移除所有符合<pattern>模式的单词,并返回剩余单词组成的列表。示例:$(filter-out %.o,main.o util.o foo.c) 返回 "foo.c"。$(word <n>,<text>):取单词函数。这个函数返回<text>中第<n>个单词(<n>从1开始计数)。示例:$(word 2, hello world) 返回 "world"。$(wordlist <s>,<e>,<text>):取单词列表函数。这个函数返回<text>中从第<s>个到第<e>个单词组成的列表。示例:$(wordlist 1, 2, hello world) 返回 "hello world"。$(words <text>):统计单词数函数。这个函数返回<text>中的单词个数。示例:$(words hello world) 返回 "2"。

这些字符串处理函数在Makefile编写中非常有用,能够大大简化复杂的字符串操作,提高构建脚本的可读性和可维护性。在使用这些函数时,需要注意语法正确,确保传递的参数符合函数的要求。

4.2. 文件名处理函数$(wildcard <pattern>): 用于匹配指定模式的文件列表。返回所有匹配文件名的列表,文件名之间用空格分隔。示例:$(wildcard *.c) 返回当前目录下所有以.c结尾的文件名列表。$(dir <names>):取目录函数。此函数从文件名序列<names>中抽取目录部分,返回值为目录部分,指的是最后一个反斜杠之前的部分。如果没有反斜杠,将返回“./”。示例:OBJ=$(dir src/foo.c hacks) 执行make命令后返回的值是“src/ ./”,分别提取了文件foo.c的路径“/src”和文件hacks的路径“./”。$(notdir <names>):取文件函数。此函数从文件名序列<names>中取出非目录的部分,即最后一个反斜杠之后的部分。示例:如果有一个文件名序列包含“src/foo.c”和“hacks”,使用$(notdir <names>)将分别返回“foo.c”和“hacks”。$(suffix <names>):取后缀函数。此函数从文件名序列<names>中取出文件的后缀名。示例:对于文件名“example.txt”,$(suffix example.txt)将返回“.txt”。$(basename <names>):取基名函数。此函数从文件名序列<names>中去掉后缀名,返回文件名的基本部分。示例:对于文件名“example.txt”,$(basename example.txt)将返回“example”。$(addsuffix <suffix>,<names>):加后缀函数。此函数在<names>中的每个文件名后添加指定的后缀<suffix>。示例:$(addsuffix .c,foo bar)将返回“foo.c bar.c”。$(addprefix <prefix>,<names>):加前缀函数。此函数在<names>中的每个文件名前添加指定的前缀<prefix>。示例:$(addprefix src/,foo.c bar.c)将返回“src/foo.c src/bar.c”。$(join <list1>,<list2>):连接函数。此函数将<list2>中的每个元素与<list1>中对应的元素连接在一起。示例:$(join a b,.c .o)将返回“a.c b.o”。

这些文件名操作函数为Makefile提供了强大的文件名处理能力,使得构建过程中对于文件名的操作变得简单而直观。在使用这些函数时,需要确保传递的参数格式正确,并根据实际需求选择合适的函数。

4.3. 控制函数

Makefile中的控制函数主要用于实现条件判断和循环控制。以下是一些常用的控制函数:

ifeqifneqifdefifndef:这些函数用于条件判断。例如,ifeq用于比较两个字符串是否相等,ifneq用于比较两个字符串是否不相等,ifdef用于检查一个变量是否已定义,ifndef则用于检查一个变量是否未定义。根据条件判断的结果,可以选择执行不同的代码块。elseendif:这些函数用于与ifeqifneqifdefifndef等函数配合使用,构成完整的条件判断语句。else表示当条件不满足时执行的代码块,endif则标志着条件语句的结束。

通过这些控制函数,你可以在Makefile中实现复杂的构建逻辑,根据不同的条件选择性地编译或链接不同的源文件、包含不同的头文件、设置不同的编译选项等。

4.4. 变量处理函数$(value <variable>): 返回变量的值。如果变量未定义,则返回空字符串。$(origin <variable>): 查询变量的来源,返回变量是定义在环境、命令行、Makefile文件等位置。4.5. 文本处理函数$(foreach <var>,<list>,<text>): 遍历<list>中的每个元素,将<var>设置为每个元素的值,并执行<text>。4.6. 复杂操作函数$(eval <text>): 在Makefile中构造一个可变的规则结构关系。对其参数进行展开,并将展开结果作为Makefile的一部分进行解析。$(call <function>,<arg1>,<arg2>,...): 调用自定义函数或Makefile内置函数,并传递参数。4.7. 自定义函数

Makefile还支持自定义函数,通过defineendef关键字定义。自定义函数可以包含多条命令,用于执行特定的任务。

4.8. 调用函数

函数调用的一般格式为$(<function> <args>),其中<function>是函数名,<args>是传递给函数的参数,参数之间用空格或Tab分隔。

这些函数大大增强了Makefile的灵活性和处理能力,使得构建复杂的软件项目变得更加容易和高效。在编写Makefile时,可以根据需要选择合适的函数来实现构建逻辑。

5. Makefile应用5.1. 简单应用

Makefile是一个文本文件,用于自动化编译和构建项目。它包含了一系列规则,用于描述如何从源代码文件生成目标文件,以及如何处理这些文件之间的依赖关系。使用Makefile可以大大提高开发效率,减少手动编译和链接的繁琐操作。

下面是使用Makefile的基本步骤:

创建Makefile文件:在项目的根目录下创建一个名为Makefile的文件。可以使用任何文本编辑器来创建和编辑该文件。定义变量:在Makefile中,可以定义一些变量来简化命令和文件路径的书写。例如,可以定义编译器、编译选项、源文件目录和目标文件目录等变量。编写规则:Makefile的核心是规则。每个规则都描述了如何从一组源文件生成一个或多个目标文件。规则的基本语法如下:

target: dependencies    command    ...

其中,target是要生成的目标文件,dependencies是目标文件所依赖的其他文件或目标,command是用于生成目标文件的命令。

例如,一个简单的C语言项目的Makefile可能包含以下规则:

CC = gcc  # 定义编译器变量CFLAGS = -Wall  # 定义编译选项变量SRCS = main.c util.c  # 定义源文件列表变量OBJS = $(SRCS:.c=.o)  # 根据源文件列表生成目标文件列表变量all: myprogram  # 默认目标,依赖于myprogram目标myprogram: $(OBJS)  # myprogram目标依赖于OBJS变量中的目标文件    $(CC) $(CFLAGS) -o myprogram $(OBJS)  # 使用gcc编译器和CFLAGS选项链接目标文件生成可执行文件%.o: %.c  # 通配符规则,用于从.c文件生成.o文件    $(CC) $(CFLAGS) -c $< -o $@  # $<表示依赖项(即.c文件),$@表示目标(即.o文件)

在上面的示例中,CCCFLAGS是变量,SRCS是源文件列表变量,OBJS是根据源文件列表生成的目标文件列表变量。all是一个默认目标,它依赖于myprogram目标。myprogram目标依赖于OBJS变量中的目标文件,并使用gcc编译器和CFLAGS选项进行链接。最后,%.o: %.c是一个通配符规则,用于从.c文件生成.o文件。

4. 执行Makefile:在命令行中进入包含Makefile的目录,并输入make命令来执行Makefile。Make工具会自动分析Makefile中的规则,并根据依赖关系生成目标文件。如果要生成特定的目标,可以在make命令后面加上目标名,例如make myprogram

Makefile的使用非常灵活,可以根据项目的需求进行定制和扩展。通过合理编写Makefile,可以大大提高项目的构建效率和可维护性。

5.2. 子目录应用

当项目结构变得复杂,包含多个子目录和源文件时,Makefile 的编写和管理也会相应变得复杂。一种常见的做法是将编译规则分解到各个子目录的 Makefile 中,然后在顶层的 Makefile 中调用这些子目录的 Makefile。这样可以保持每个子目录的编译规则相对独立,同时又能通过顶层的 Makefile 统一管理和构建整个项目。

以下是一个简单的例子,展示如何在一个包含子目录的项目中使用 Makefile:

假设你的项目结构如下:

project/    Makefile        # 顶层的 Makefile    src/            # 源文件目录        main.c        subdir1/    # 子目录1            file1.c        subdir2/    # 子目录2            file2.c
5.2.1. 顶层的 Makefile (project/Makefile)
# 定义编译器和编译选项CC = gccCFLAGS = -Wall# 定义源文件目录和目标文件目录SRCDIR = srcBUILDDIR = build# 定义子目录列表SUBDIRS = $(SRCDIR)/subdir1 $(SRCDIR)/subdir2# 默认目标all: dirs $(SUBDIRS) myprogram# 创建构建目录dirs:    mkdir -p $(BUILDDIR)# 递归调用子目录的 Makefile$(SUBDIRS):    make -C $@# 链接所有目标文件生成最终的可执行文件myprogram: dirs $(SUBDIRS)    $(CC) $(CFLAGS) -o $@ $^# 清理构建结果clean:    rm -rf $(BUILDDIR) myprogram    for dir in $(SUBDIRS); do \        (cd $$dir && make clean); \    done.PHONY: all dirs $(SUBDIRS) myprogram clean
5.2.2. 子目录的 Makefile (例如 project/src/subdir1/Makefile)
# 定义当前子目录的源文件和目标文件目录SRCS = file1.cOBJS = $(SRCS:.c=.o)# 定义目标文件应该放置的目录(相对于当前子目录)TARGETDIR = ../$(BUILDDIR)# 生成目标文件并放置到指定的目录all: $(TARGETDIR)/$(OBJS)$(TARGETDIR)/%.o: %.c    $(CC) $(CFLAGS) -c $< -o $@# 清理当前子目录的构建结果clean:    rm -f $(TARGETDIR)/$(OBJS).PHONY: all clean

子目录 subdir2 的 Makefile 会类似地定义,只是处理的源文件不同。

在这个例子中,顶层的 Makefile 定义了编译器、编译选项、源文件目录和目标文件目录等变量。它还定义了子目录列表,并通过 $(SUBDIRS) 变量递归调用每个子目录中的 Makefile。每个子目录中的 Makefile 负责编译该目录下的源文件,并将生成的目标文件放置到顶层的构建目录中。最后,顶层的 Makefile 将所有的目标文件链接成一个可执行文件。

通过这种方式,你可以轻松地管理大型项目中的多个子目录和源文件,确保每个子目录的编译规则相对独立,同时又能通过顶层的 Makefile 统一构建整个项目。

标签: #c语言makefile怎么用