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

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

3天内不再提示

【Makefile】通用模板

_light 来源:_light 作者:_light 2023-04-15 12:47 次阅读

工程目录

假如我们有以下目录结构:

.
├── inc
│   ├── add.h
│   └── sub.h
├── main.c
└── src
    ├── add.c
    └── sub.c

文件中的内容如下:

//main.c
#include 
#include "add.h"
#include "sub.h"

int main()
{
    int x = 9;
    printf("x = %d\\\\\\\\\\\\\\\\n", add_one(x));
    printf("x = %d\\\\\\\\\\\\\\\\n", sub_one(x));
    return 0;
}

//add.h
int add_one(int x);

//add.c
int add_one(int x)
{
    return x + 1;
}

//sub.h
int sub_one(int x);

//sub.c
int sub_one(int x)
{
    return x - 1;
}

对于上述这样的多.c文件,又不在同一个目录下的大型工程中,借助makefile可以来减轻工作任务

(上述是一个很小很小的工程)

准备工作

在使用gcc 将 源文件 main.c编译成 可执行目标程序 总共需要4步:

在这里插入图片描述

平常在编译项目时,预处理与编译器这两步会省略,是先将源文件 .c 编译成 .o 文件,然后再链接 .o 文件

gcc -c main.c -o main.o
gcc main.o -o main.exe/main.out

编写Makefile

接下来会一步一步的编写一个Makefile文件,这个文件可以适配于大部分C/C++工程,让我们开始吧!

1. 定义可执行文件名、GCC类型

先定义一个最终可执行文件名的变量:

TARGET = main

变量值可以随意定义。

gcc分为很多种,常见的有:gcc、arm-linux-gcc、arm-none-eabi-gcc等等,所以为了Makefile适配更多的C/C++项目,可以将编译器定义一个变量,这后续更改起来很方便。我这里使用的gcc:

CC = gcc

2. 中间文件的路径的变量

由前文可知,在编译过程中会编译出很多的 .o 文件,一般将这些编译过程中产生的文件单独放到一个文件夹下,文件夹的名字大多叫做 build ,定义一个变量 BUILD_DIR 该变量的值就是build,用来存放中间产物,在后续编译过程中会用到:

BUILD_DIR = build

3、.c 源文件的路径

事先需要将工程中所用到的源文件 .c 的路径,这样在后续中就可直接得到 .c 文件,定义一个变量 SRC_DIR 来存放源文件 .c 的路径

SRC_DIR =     \\\\\\\\\\\\\\\\
	./    \\\\\\\\\\\\\\\\
	./src

4、 头文件的路径

接着得到所有用到的头文件路径:

INC_DIR = \\\\\\\\\\\\\\\\
	./inc

这gcc选项中有这个参数 -I 是告诉编译器头文件的路径,在后续中会使用Makefile的一个函数为每个头文件路径添加 -I

5、为头文件路径添加 -I

当所引用的头文件与源文件不在同一级目录下时需要添加 -I 选项指定头文件路径,在第四步中已经获取到头文件的路径,下面借助一个Makefile中的一个函数在每个头文件前面添加 -I

首先看一下函数 patsubst 的介绍。

$(patsubst ,,)
  • 名称:模式字符串替换函数。

  • 功能:查找 中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式 ,如果匹配的话,则以 替换。这里, 可以包括通配符 % ,表示任意长度的字串。如果 中也包含 % ,那么, 中的这个 % 将是 中的那个 % 所代表的字串。(可以用 **** 来转义,以 % 来表示真实含义的 % 字符)

  • 返回:函数返回被替换过后的字符串。

  • 示例:

    $(patsubst %.c, %.o, x.c.c bar.c)
    

    把字串 x.c.c、bar.c 符合模式 %.c 的单词替换成 %.o ,返回结果是 x.c.o bar.o-

定义一个变量 INCLUDE

INCLUDE	= $(patsubst %, -I %, $(INC_DIR))

这样就会在每个头文件路径前加入 -I 了。

6、得到带路径的源文件

在第三步中,我们得到了 .c 文件的存放路径,这一步我们得到带有路径的 .c 文件,简单来说就是,假如在src目录下有一个foo.c的文件,在第3步中只得到了 src 这个目录,这一步得到的是 src/foo.c

得到目录下的 .c 文件需要用到Makefile中的两个函数,foreach函数wildcard 函数

1、wildcard 函数

$(wildcard PATTERN...)

在Makefile中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表

2、foreach函数

$(foreach < var >,< list >,< text >)

这个函数的意思是,把参数 中的单词逐一取出放到参数 所指定的变量中,然后再执行 所包含的表达式。每一次 会返回一个字符串,循环过程中, 的所返回的每个字符串会以空格分隔,最后当整个循环结束时, 所返回的每个字符串所组成的整个字符串(以空格分隔)将会是foreach函数的返回值。

所以, 最好是一个变量名,\\ 可以是一个表达式,而 中一般会使用 这个参数来依次枚举 中的单词。

举个例子:

names := a b c d
files := $(foreach n,$(names),$(n).o)

上面的例子中, (name) 中的单词会被挨个取出,并存到变量 n 中, (n).o 每次根据 **(n) 计算出一个值,这些值以空格分隔,最后作为foreach函数的返回,所以, **(files) 的值是 a.o b.o c.o d.o 。

使用这两个函数得到带路径的 .c 文件

CFILES := $(foreach dir, $(SRC_DIR), $(wildcard $(dir)/*.c))

7. 得到不带路径的 .c 文件

在上一步中我们得到了带路径的 .c 文件,这步借助Makefile中的函数 notdir 将路劲去除,得到 "真正的.c"

notdir 函数

$(notdir
  • 名称:取文件函数——notdir。
  • 功能:从文件名序列 中取出非目录部分。非目录部分是指最後一个反斜杠( / )之后的部分。
  • 返回:返回文件名序列 的非目录部分。
  • 示例:
    $(notdir src/foo.c hacks)
    
返回值是 foo.c hacks 。

定义一个变量 CFILENDIR 来存放不带路径的 .c 文件:

CFILENDIR := $(notdir  $(CFILES))

8. 将工程中的.c 文件替换成 ./build 目录下对应的目标文件 .o

这一步只是简单的字符串进行替换,对原文件不进行任何操作。我们可以先写一个伪目标,打印一下变量 CFILENDIR 的内容

# 打印结束后可以删除
print:
	@echo $(CFILENDIR)

使用 make 查看一下输出结果,会得到字符串:main.c add.c sub.c

在前面讲过编译时会在 build 目录下得到.o文件,这个.o 文件就是由.c文件生成的,因此 main.c add.c sub.c 会对应于 build 目录下的 main.o add.o sub.o

由于现在不是编译阶段,我们只对字符串进行个简单的替换操作,定义一个变量 COBJS 用来存放目录 build 下的 .o 文件

COBJS = $(patsubst %, ./$(BUILD_DIR)/%, $(patsubst %.c, %.o, $(CFILENDIR)))

此时变量 COBJS 的值就是:./build/main.o ./build/add.o ./build/sub.o

到目前为止已经得到了工程中的源文件 CFILENDIR 、可重定位目标文件 COBJS 以及带有 -I 前缀的头文件路径 INCLUDE ,注意,到目前为止我们操作的只是字符串而已,还未对源文件做任何操作。

9、搜索源文件

在我们这个工程中,有两个目录下存放着 .c 文件,当make需要去找寻文件的依赖关系时,可以使用变量 VPATH 让make在自动在这两个目录中去找依赖文件。

VPATH = $(SRC_DIR)

10、生成可重定位目标文件(编译阶段)

$(COBJS) : $(BUILD_DIR)/%.o : %.c
	@mkdir -p $(BUILD_DIR)
	$(CC) $(INCLUDE) -c -o $@ $< 

会将源文件 .c 编译成可重定位目标文件 .o

11、链接 .o 文件

此步骤是最后一步,将所有的 .o 文件链接成可执行程序

可执行文件可以生成到指定的目录下,我这里生成到了 build 目录下

$(BUILD_DIR)/$(TARGET).exe : $(COBJS)
	$(CC) -o $@ $^

此时,Makefile 已经编写完成。

当执行make 时,发现并不是预期的目标,只执行了一句指令:

gcc  -I ./inc -c -o build/main.o main.c

这是因为make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件,如是依赖存在编译成功后就会退出执行,若是没有找到依赖,则会报错并退出。

当想达到预期的目标,共有两种办法:

第一种:将目标**(BUILD_DIR)/**(TARGET).exe 写在目标 $(COBJS) 的前面,这样就可以达到预期的结果了。

第二种:使用关键字 all ,写在关键字 all 后面的目标都会执行一次,直到所有目标执行完成,或者某个目标不成立。

此时,再执行make,就能得到预期的结果了

image.png

12、清理目标

make编译之后会在工程中多出很多目标文件*.o,可以写一个目标 clean 用来删除工程中的目标文件

clean:
	rm -rf $(BUILD_DIR)

Makefile全部内容:

# 可执行文件名
TARGET = main

# gcc类型
CC = gcc

# 存放中间文件的路径
BUILD_DIR = build

#存放.c 源文件的文件夹
SRC_DIR = \\\\\\\\\\\\\\\\
	./    \\\\\\\\\\\\\\\\
	./src

# 存放头文件的文件夹
INC_DIR = \\\\\\\\\\\\\\\\
	./inc

# 在头文件路径前面加入-I
INCLUDE	= $(patsubst %, -I %, $(INC_DIR))

# 得到带路径的 .c 文件
CFILES := $(foreach dir, $(SRC_DIR), $(wildcard $(dir)/*.c))

# 得到不带路径的 .c 文件
CFILENDIR := $(notdir  $(CFILES))

# 将工程中的.c 文件替换成 ./build 目录下对应的目标文件 .o
COBJS = $(patsubst %, ./$(BUILD_DIR)/%, $(patsubst %.c, %.o, $(CFILENDIR)))

# make 自动在源文件目录下搜索 .c 文件
VPATH = $(SRC_DIR)

$(BUILD_DIR)/$(TARGET).exe : $(COBJS)
	$(CC) -o $@ $^

$(COBJS) : $(BUILD_DIR)/%.o : %.c
	@mkdir -p $(BUILD_DIR)
	$(CC) $(INCLUDE) -c -o $@ $<  

clean:
	rm -rf $(BUILD_DIR)

至此,Makefile通用模板已经编写完成,文章中若有错误的地方请在评论区指出。

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 文件
    +关注

    关注

    1

    文章

    561

    浏览量

    24692
  • Makefile
    +关注

    关注

    1

    文章

    125

    浏览量

    19161
  • make
    +关注

    关注

    0

    文章

    16

    浏览量

    12511
收藏 人收藏

    评论

    相关推荐

    Linux Makefile通用模板详解

    对于Windows下开发,很多IDE都集成了编译器,如Visual Studio,提供了“一键编译”,编码完成后只需一个操作即可完成编译、链接、生成目标文件。
    发表于 08-08 09:48 831次阅读
    Linux <b class='flag-5'>Makefile</b><b class='flag-5'>通用</b><b class='flag-5'>模板</b>详解

    如何调整MPlab-ide生成的makefile

    你好!我试图修改MPLAB IDE生成的makefile,以便在编译每个文件之前执行代码检查器。我想修改makefile生成器或makefile模板,添加命令行来执行这个检查器,如果检
    发表于 09-02 13:15

    使用Makefile+gcc编译STM32

    方式,那时已经使用Notepad++了,如今也一起在使用。Makefile模板如下:# STM32F103 Makefile模板# 参考来源:#
    发表于 11-22 08:10

    怎样去编写通用makefile文件的源码呢

    怎样去编写通用makefile文件的源码呢?其实验结果有哪些?
    发表于 12-27 06:41

    基于模板通用文件对话框的可视化定制

    本文介绍了利用模板可视化定制通用文件对话框的实现方法。关键词: 定制;通用文件对话框;模板;预览在我们开发的CAD/CAM系统及其它应用软件中,经常需要用户进行一些
    发表于 08-21 10:08 14次下载

    驾驭Makefile

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

    linux makefile教程

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

    基于JasperReport通用的报表模板设计

    JasperReport是一个强大、灵活的开源报表生成工具,适用于各种Java应用程序,是当前Java开发者最常用的报表工具之一。利用JasperReport生成报表,首先需要设计报表模板,然后
    发表于 12-04 15:20 3次下载
    基于JasperReport<b class='flag-5'>通用</b>的报表<b class='flag-5'>模板</b>设计

    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的项目模板免费下载 可根据项目名称修改。
    发表于 06-12 08:00 11次下载
    <b class='flag-5'>Makefile</b>的项目<b class='flag-5'>模板</b>免费下载

    嵌入式中的Makefile应用

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

    一个STM32编译Makefile模板

    一个STM32编译Makefile模板
    发表于 11-13 20:06 10次下载
    一个STM32编译<b class='flag-5'>Makefile</b><b class='flag-5'>模板</b>

    AVR-GCC Makefile 中文翻译

    所有的想要的功能都可以实现。但是我是个爱钻牛角尖人,呵呵。看了一下 WINAVR Makefile模板我觉得写得很好,学习了一下,翻译了一下,加上很多我的理解和注释。把它共享给大家,共同进步。这个M...
    发表于 11-15 13:36 35次下载
    AVR-GCC <b class='flag-5'>Makefile</b> 中文翻译

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

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

    三个Makefile模板分享

        本文分享三个Makefile模板:编译可执行程序、编译静态库、编译动态库。 1 写在前面     对于Windows下开发,很多IDE都集成了编译器,如Visual Studio,提供了
    的头像 发表于 07-10 09:07 931次阅读
    三个<b class='flag-5'>Makefile</b><b class='flag-5'>模板</b>分享