0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

什么是Makefile?

汽车电子技术 来源:嵌入式悦翔园 作者: JamesBin 2023-02-17 10:41 次阅读

前言

如果您有多个 c、c++ 和其他语言的文件,并且想通过终端命令编译它们,我们该如何编译他们呢?为了解决这类问题,Makefile就出现了。Makefile在编译大型项目的过程中,可以一次性编写大量的源文件以及需要链接器标志。废话少说咱们直接开始今天的正文!图片

什么是Makefile

Makefile是一种用于简化或组织编译代码的工具,是一组具有变量名称和目标的命令(类似于终端命令),用于创建和删除目标文件的工具。在单个 make 文件中,我们可以创建多个目标来编译和删除对象、二进制文件。您可以使用Makefile多次编译您的项目(程序)。

让我们通过一个例子来理解:

假设我们有 3 个文件main.c(主源文件)、 misc.c(包含函数定义的源文件)、misc.h(包含函数声明)。在这里,我们将声明和定义一个名为myFunc()的函数来打印一些东西——这个函数将分别在misc.cmisc.h中定义和声明。

misc.c

#include 
#include "misc.h"
 
/*function definition*/
void myFunc(void)
{
    printf("Body of myFunc function.\\n");
}

misc.h

#ifndef MISC_H
    #define MISC_H
     
    /*function declaration.*/
    void myFunc(void);
     
#endif

main.c

#include 
#include "misc.h"
 
int main()
{
    printf("Hello, World.\\n");
    myFunc();
    fflush(stdout);
 
    return 0;
}

上面这个场景是非常常见也是最简单的一个多文件系统了,我们想要编译他,并将他们链接在一起该如何做呢?显然仅仅使用gcc等这些简单的编译器是不够的,此时我们就需要用到Makefile了。

下面将内容放在一个名为Makefile的文件中,注意Makefile文件的名字只能是这几个字,而且区分大小写。

Makefile

#make file - this is a comment section
 
all:    #target name
    gcc main.c misc.c -o main
  • 保存名为Makefile
  • 插入注释,后跟#字符。
  • all是一个目标名称,在目标名称之后插入:
  • gcc是编译器名称,main.cmisc.c源文件名,-o是链接器标志,main是二进制文件名。

“注意: Makefile必须使用 TAB 而不是空格缩进,否则make会失败。

我们写好Makefile后怎么进行编译呢?下面是代码的编译过程:

没有目标名称:

make

带有目标名称:

make all

输出:

sh-4.3$ make
    gcc     main.c misc.c -o main

    sh-4.3$ ./main
    Hello, World.

    Body of myFunc function.
    sh-4.3$

此时我们就可以看到对应文件夹里已经生成了对应的可执行文件了!这就是Makefile的作用!

为什么会存在 Makefile?

Makefile 用于帮助决定大型程序的哪些部分需要重新编译。在绝大多数情况下,编译 C 或 C++ 文件。其他语言通常有自己的工具,其用途与 Make 相似。当您需要一系列指令来运行取决于哪些文件已更改时,Make 也可以在编译之外使用。本教程将重点介绍 C/C++ 编译用例。

这是您可以使用 Make 构建的示例依赖关系图。如果任何文件的依赖项发生更改,则该文件将被重新编译:图片

Makefile的语法

一个 Makefile 由一组规则组成。规则通常如下所示:

targets: prerequisites
 command
 command
 command
  • targets:是文件名,以空格分隔。通常,每条规则只有一个。
  • command:是通常用于制作目标的一系列步骤。这些需要以制表符开头,而不是空格。
  • prerequisites:先决条件也是文件名,以空格分隔。这些文件需要在运行目标命令之前存在。这些也称为依赖项

Makefile的精髓

让我们从一个 hello world 示例开始:

hello:
 echo "Hello, World"
 echo "This line will always print, because the file hello does not exist."

然后我们将运行make hello,只要hello文件不存在,命令就会运行。如果hello存在,则不会运行任何命令。

重要的是要意识到我说hello的是targetfile,那是因为两者是直接联系在一起的。通常,当运行目标时(也就是运行目标的命令时),这些命令将创建一个与目标同名的文件。在这种情况下,hello 目标不会创建hello 文件。

那么我们怎么样才能让程序全部重新生成呢?这就要用到清理目标文件的语句了,下面我们一起看一下如何清理已生成的目标文件。

清理生成的目标文件

我们还可以使用 Makefile 中的变量来概括Makefile。在此示例中,我们使用变量和干净的目标名称编写 Makefile 以删除所有对象(.o 扩展文件)和二进制文件(主文件)。

#make file - this is a comment section
 
CC=gcc  #compiler
TARGET=main #target file name
 
all:
    $(CC) main.c misc.c -o $(TARGET)
 
clean:
    rm $(TARGET)

编译:

make

此时我们想要的目标文件以及.o文件已经出现在对应的文件夹中,那我们如何删除编译出来的文件呢?是不是要使用rm语句一个一个的删除呢?其实大可不必,而且在大的工程中你也不可能一个一个的删除,所以这时候make clean就出现了,他能通过一条语句就删除刚才编译出来的所有文件,下面我们来看一下应该如何操作!

直接在Makefile对应的文件夹先输入一下命令,就会发现刚才生成的文件已经消失了。

make clean

当我们有多个文件时,我们可以在 Makefile 中编写命令来为每个源文件创建目标文件。如果你这样做 只有那些被修改的文件将被编译。

如果我们想要全部重新编译只需要先执行make clean 语句在执行make即可。

Makefile中如何使用变量

在上面的示例中,大多数目标值和先决条件值都是硬编码的,但在实际项目中,这些值被替换为变量和模式。

在 Makefile 中定义变量的最简单方法是使用=运算符。例如,要将命令分配给gcc变量CC

CC = gcc

这也称为递归扩展变量,它用于如下所示的规则中:

hello: hello.c
    ${CC} hello.c -o hello

那么实际在终端中执行的语句是下面的:

gcc hello.c -o hello

两者${CC}$(CC)都是对 gcc的有效引用。但是如果想将一个变量重新分配给它自己,它将导致一个无限循环。让我们验证一下:

CC = gcc
CC = ${CC}

all:
    @echo ${CC}

运行make将导致下面的错误:

$ make
Makefile:8: *** Recursive variable 'CC' references itself (eventually).  Stop.

为了避免这种情况,我们可以使用:=运算符(这也称为简单扩展变量)。我们运行下面的makefile应就不会出现上面的问题了:

CC := gcc
CC := ${CC}

all:
    @echo ${CC}

举个例子

下面我们通过一个实际的例子来体会一下上面讲的知识点。以下 makefile 使用了变量、模式和函数编译所有 C 程序。

# Usage:
# make        # compile all binary
# make clean  # remove ALL binaries and objects

.PHONY = all clean

CC = gcc                        # compiler to use

LINKERFLAG = -lm

SRCS := $(wildcard *.c)
BINS := $(SRCS:%.c=%)

all: ${BINS}

%: %.o
        @echo "Checking.."
        ${CC} ${LINKERFLAG} $< -o $@

%.o: %.c
        @echo "Creating object.."
        ${CC} -c $<

clean:
        @echo "Cleaning up..."
        rm -rvf *.o ${BINS}
  • #注释整行。
  • Line.PHONY = all clean定义虚假目标allclean.
  • 变量LINKERFLAG定义要在gcc中使用的标志。
  • SRCS := $(wildcard *.c):$(wildcard pattern)是文件名的功能之一。在这种情况下,所有带有.c扩展名的文件都将存储在一个变量SRCS中。
  • BINS := $(SRCS:%.c=%): 这称为替代参考。在这种情况下,如果SRCS有值'foo.c bar.c'BINS就会有'foo bar'
  • Line all: ${BINS}:虚假目标all将值${BINS}作为单独的目标调用。

让我们看一个例子来理解这个规则。假设foo是中的值之一${BINS}。然后%将匹配foo%可以匹配任何目标名称)。以下是扩展形式的规则:

foo: foo.o
  @ echo "Checking.."
  gcc -lm foo.o -o foo

如上所示,%替换为foo%.o替换为foo.o%.o被模式化以匹配先决条件,并将%匹配为目标。

下面是对上述makefile的重写,并将它被放置在具有单个文件的foo.c中:

# Usage:
# make        # compile all binary
# make clean  # remove ALL binaries and objects

.PHONY = all clean

CC = gcc                        # compiler to use

LINKERFLAG = -lm

SRCS := foo.c
BINS := foo

all: foo

foo: foo.o
        @echo "Checking.."
        gcc -lm foo.o -o foo

foo.o: foo.c
        @echo "Creating object.."
        gcc -c foo.c

clean:
        @echo "Cleaning up..."
        rm -rvf foo.o foo

这样我们就可以使用一条语句make 完成整个程序的编译了,如果想删除编译中生成的文件,可以使用make clean

多目标Makefile

制作多个目标并且您希望所有目标都运行?做一个all目标。make由于这是列出的第一条规则,如果在没有指定目标的情况下调用它,它将默认运行。

all: one two three

one:
 touch one
two:
 touch two
three:
 touch three

clean:
 rm -f one two three

自动变量和通配符

* 通配符*%Make 中都称为通配符,但它们的含义完全不同。*在您的文件系统中搜索匹配的文件名。

# Print out file information about every .c file
print: $(wildcard *.c)
 ls -la  $?
  • *可以在目标、先决条件或wildcard函数中使用。
  • *不能在变量定义中直接使用
  • *没有匹配到文件时,保持原样(除非在wildcard函数中运行)

% 通配符%确实很有用,但是由于可以使用的情况多种多样,因此有些混乱。

  • matching模式下使用时,它匹配字符串中的一个或多个字符。
  • replacing模式下使用时,它采用匹配的词干并替换字符串中的词干。
  • %最常用于规则定义和某些特定功能中。

结语

最后让我们通过一个非常多汁的 Make 示例来结束本文,它适用于中型项目。

这个 makefile 的巧妙之处在于它会自动为您确定依赖关系。您所要做的就是将您的 C/C++ 文件放入该src/文件夹中。

TARGET_EXEC := final_program

BUILD_DIR := ./build
SRC_DIRS := ./src

# Find all the C and C++ files we want to compile
# Note the single quotes around the * expressions. Make will incorrectly expand these otherwise.
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')

# String substitution for every C/C++ file.
# As an example, hello.cpp turns into ./build/hello.cpp.o
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)

# String substitution (suffix version without %).
# As an example, ./build/hello.cpp.o turns into ./build/hello.cpp.d
DEPS := $(OBJS:.o=.d)

# Every folder in ./src will need to be passed to GCC so that it can find header files
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
# Add a prefix to INC_DIRS. So moduleA would become -ImoduleA. GCC understands this -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))

# The -MMD and -MP flags together generate Makefiles for us!
# These files will have .d instead of .o as the output.
CPPFLAGS := $(INC_FLAGS) -MMD -MP

# The final build step.
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
 $(CXX) $(OBJS) -o $@ $(LDFLAGS)

# Build step for C source
$(BUILD_DIR)/%.c.o: %.c
 mkdir -p $(dir $@)
 $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@

# Build step for C++ source
$(BUILD_DIR)/%.cpp.o: %.cpp
 mkdir -p $(dir $@)
 $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@


.PHONY: clean
clean:
 rm -r $(BUILD_DIR)

# Include the .d makefiles. The - at the front suppresses the errors of missing
# Makefiles. Initially, all the .d files will be missing, and we don't want those
# errors to show up.
-include $(DEPS)
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C++
    C++
    +关注

    关注

    21

    文章

    2066

    浏览量

    72900
  • 编译
    +关注

    关注

    0

    文章

    615

    浏览量

    32392
  • Makefile
    +关注

    关注

    1

    文章

    121

    浏览量

    19094
收藏 人收藏

    评论

    相关推荐

    Makefile中文教程 -下载

    Makefile中文教程,免费下载哦。
    发表于 03-25 08:57 66次下载

    教你写Makefile

    教你写Makefile 什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,
    发表于 02-10 14:24 37次下载

    跟我一起学Makefile

    不坑爹欢迎下载,快快快makefile经典
    发表于 01-05 16:06 1次下载

    驾驭Makefile

    驾驭Makefile在网上你能找到很多关于Makefile的学习资料,但绝大部分给你的只是一个知识点,与将Makefile运用到项目中(尤其是大型项目)的差距非常的大。因为,将Makefile
    发表于 01-05 17:05 9次下载

    Makefile教程

    Linux开发必备的Makefile文件编译教本,讲解的很到位,新手基本上看上几天就可以上手了
    发表于 01-05 17:05 0次下载

    跟我一起学makefile

    关于makefile学习,深入了解嵌入式下makefile的编写
    发表于 05-23 18:21 0次下载

    驾驭makefile

    一本Makefile的经典教程,深入浅出,易学易懂
    发表于 06-17 16:16 9次下载

    Linux内核Makefile文件

    Makefile文件的详细编写过程
    发表于 08-28 09:25 14次下载

    linux makefile教程

     什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,makefile
    发表于 11-12 09:11 4945次阅读

    makefile的基本语法

     在Makefile中,最重要的三个概念是:目标(target)、依赖关系(dependency)和命令(command)。目标是指要干什么,即运行make后生成什么;依赖是指明目标所依赖的其他目标;命令则告诉make如何生成目标,这三个概念是通过Makefile中的规则
    发表于 11-12 10:15 1.1w次阅读

    Makefile是什么?Makefile工作原理是怎样的?Makefile经典教程免费下载

    Makefile的重要性 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力 makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件
    发表于 09-12 17:19 0次下载
    <b class='flag-5'>Makefile</b>是什么?<b class='flag-5'>Makefile</b>工作原理是怎样的?<b class='flag-5'>Makefile</b>经典教程免费下载

    嵌入式中的Makefile应用

    文章目录一.Makefile 引入二. Makefile语法1.通配符2.假象目标3.变量三.Makefile函数四.实例本文主要总结一下嵌入式开发中的Makefile,一般项目中都需
    发表于 11-03 17:06 11次下载
    嵌入式中的<b class='flag-5'>Makefile</b>应用

    MakefileMakefile与shell命令的联系

    博主最近在项目实践过程中,需要深度定制项目的Makefile,其中有些复杂的流程必须得借助shell脚本才能高效地完成,为此博主特意深入学习了在Makefile种调用shell命令的方法。
    的头像 发表于 07-11 09:06 2553次阅读
    【<b class='flag-5'>Makefile</b>】<b class='flag-5'>Makefile</b>与shell命令的联系

    Makefile】简单实用的Makefile模板来了

    【Linux + Makefile】简单实用的Makefile模板来了
    的头像 发表于 08-31 12:46 1643次阅读
    【<b class='flag-5'>Makefile</b>】简单实用的<b class='flag-5'>Makefile</b>模板来了

    Makefile可以做什么?Makefile的基本格式

    Makefile可以根据指定的依赖规则和文件是否有修改来执行命令。常用来编译软件源代码,只需要重新编译修改过的文件,使得编译速度大大加快。
    的头像 发表于 01-25 11:18 233次阅读