0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

【Makefile】通用模板

_light ? 來源:_light ? 作者:_light ? 2023-04-15 12:47 ? 次閱讀

工程目錄

假如我們有以下目錄結(jié)構(gòu):

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

文件中的內(nèi)容如下:

//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可以來減輕工作任務(wù)

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

準(zhǔn)備工作

在使用gcc 將 源文件 main.c編譯成 可執(zhí)行目標(biāo)程序 總共需要4步:

在這里插入圖片描述

平常在編譯項目時,預(yù)處理與編譯器這兩步會省略,是先將源文件 .c 編譯成 .o 文件,然后再鏈接 .o 文件

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

編寫Makefile

接下來會一步一步的編寫一個Makefile文件,這個文件可以適配于大部分C/C++工程,讓我們開始吧!

1. 定義可執(zhí)行文件名、GCC類型

先定義一個最終可執(zhí)行文件名的變量:

TARGET = main

變量值可以隨意定義。

gcc分為很多種,常見的有:gcc、arm-linux-gcc、arm-none-eabi-gcc等等,所以為了Makefile適配更多的C/C++項目,可以將編譯器定義一個變量,這后續(xù)更改起來很方便。我這里使用的gcc:

CC = gcc

2. 中間文件的路徑的變量

由前文可知,在編譯過程中會編譯出很多的 .o 文件,一般將這些編譯過程中產(chǎn)生的文件單獨放到一個文件夾下,文件夾的名字大多叫做 build ,定義一個變量 BUILD_DIR 該變量的值就是build,用來存放中間產(chǎn)物,在后續(xù)編譯過程中會用到:

BUILD_DIR = build

3、.c 源文件的路徑

事先需要將工程中所用到的源文件 .c 的路徑,這樣在后續(xù)中就可直接得到 .c 文件,定義一個變量 SRC_DIR 來存放源文件 .c 的路徑

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

4、 頭文件的路徑

接著得到所有用到的頭文件路徑:

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

這gcc選項中有這個參數(shù) -I 是告訴編譯器頭文件的路徑,在后續(xù)中會使用Makefile的一個函數(shù)為每個頭文件路徑添加 -I

5、為頭文件路徑添加 -I

當(dāng)所引用的頭文件與源文件不在同一級目錄下時需要添加 -I 選項指定頭文件路徑,在第四步中已經(jīng)獲取到頭文件的路徑,下面借助一個Makefile中的一個函數(shù)在每個頭文件前面添加 -I

首先看一下函數(shù) patsubst 的介紹。

$(patsubst ,,)
  • 名稱:模式字符串替換函數(shù)。

  • 功能:查找 中的單詞(單詞以“空格”、“Tab”或“回車”“換行”分隔)是否符合模式 ,如果匹配的話,則以 替換。這里, 可以包括通配符 % ,表示任意長度的字串。如果 中也包含 % ,那么, 中的這個 % 將是 中的那個 % 所代表的字串。(可以用 **** 來轉(zhuǎn)義,以 % 來表示真實含義的 % 字符)

  • 返回:函數(shù)返回被替換過后的字符串。

  • 示例:

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

    把字串 x.c.c、bar.c 符合模式 %.c 的單詞替換成 %.o ,返回結(jié)果是 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中的兩個函數(shù),foreach函數(shù) 、wildcard 函數(shù)

1、wildcard 函數(shù)

$(wildcard PATTERN...)

在Makefile中,它被展開為已經(jīng)存在的、使用空格分開的、匹配此模式的所有文件列表

2、foreach函數(shù)

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

這個函數(shù)的意思是,把參數(shù) 中的單詞逐一取出放到參數(shù) 所指定的變量中,然后再執(zhí)行 所包含的表達式。每一次 會返回一個字符串,循環(huán)過程中, 的所返回的每個字符串會以空格分隔,最后當(dāng)整個循環(huán)結(jié)束時, 所返回的每個字符串所組成的整個字符串(以空格分隔)將會是foreach函數(shù)的返回值。

所以, 最好是一個變量名,\\ 可以是一個表達式,而 中一般會使用 這個參數(shù)來依次枚舉 中的單詞。

舉個例子:

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

上面的例子中, (name) 中的單詞會被挨個取出,并存到變量 n 中, (n).o 每次根據(jù) **(n) 計算出一個值,這些值以空格分隔,最后作為foreach函數(shù)的返回,所以, **(files) 的值是 a.o b.o c.o d.o 。

使用這兩個函數(shù)得到帶路徑的 .c 文件

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

7. 得到不帶路徑的 .c 文件

在上一步中我們得到了帶路徑的 .c 文件,這步借助Makefile中的函數(shù) notdir 將路勁去除,得到 "真正的.c"

notdir 函數(shù)

$(notdir
  • 名稱:取文件函數(shù)——notdir。
  • 功能:從文件名序列 中取出非目錄部分。非目錄部分是指最後一個反斜杠( / )之后的部分。
  • 返回:返回文件名序列 的非目錄部分。
  • 示例:
    $(notdir src/foo.c hacks)
    
返回值是 foo.c hacks 。

定義一個變量 CFILENDIR 來存放不帶路徑的 .c 文件:

CFILENDIR := $(notdir  $(CFILES))

8. 將工程中的.c 文件替換成 ./build 目錄下對應(yīng)的目標(biāo)文件 .o

這一步只是簡單的字符串進行替換,對原文件不進行任何操作。我們可以先寫一個偽目標(biāo),打印一下變量 CFILENDIR 的內(nèi)容

# 打印結(jié)束后可以刪除
print:
	@echo $(CFILENDIR)

使用 make 查看一下輸出結(jié)果,會得到字符串:main.c add.c sub.c

在前面講過編譯時會在 build 目錄下得到.o文件,這個.o 文件就是由.c文件生成的,因此 main.c add.c sub.c 會對應(yīng)于 build 目錄下的 main.o add.o sub.o

由于現(xiàn)在不是編譯階段,我們只對字符串進行個簡單的替換操作,定義一個變量 COBJS 用來存放目錄 build 下的 .o 文件

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

此時變量 COBJS 的值就是:./build/main.o ./build/add.o ./build/sub.o

到目前為止已經(jīng)得到了工程中的源文件 CFILENDIR 、可重定位目標(biāo)文件 COBJS 以及帶有 -I 前綴的頭文件路徑 INCLUDE ,注意,到目前為止我們操作的只是字符串而已,還未對源文件做任何操作。

9、搜索源文件

在我們這個工程中,有兩個目錄下存放著 .c 文件,當(dāng)make需要去找尋文件的依賴關(guān)系時,可以使用變量 VPATH 讓make在自動在這兩個目錄中去找依賴文件。

VPATH = $(SRC_DIR)

10、生成可重定位目標(biāo)文件(編譯階段)

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

會將源文件 .c 編譯成可重定位目標(biāo)文件 .o

11、鏈接 .o 文件

此步驟是最后一步,將所有的 .o 文件鏈接成可執(zhí)行程序

可執(zhí)行文件可以生成到指定的目錄下,我這里生成到了 build 目錄下

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

此時,Makefile 已經(jīng)編寫完成。

當(dāng)執(zhí)行make 時,發(fā)現(xiàn)并不是預(yù)期的目標(biāo),只執(zhí)行了一句指令:

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

這是因為make會一層又一層地去找文件的依賴關(guān)系,直到最終編譯出第一個目標(biāo)文件,如是依賴存在編譯成功后就會退出執(zhí)行,若是沒有找到依賴,則會報錯并退出。

當(dāng)想達到預(yù)期的目標(biāo),共有兩種辦法:

第一種:將目標(biāo)**(BUILD_DIR)/**(TARGET).exe 寫在目標(biāo) $(COBJS) 的前面,這樣就可以達到預(yù)期的結(jié)果了。

第二種:使用關(guān)鍵字 all ,寫在關(guān)鍵字 all 后面的目標(biāo)都會執(zhí)行一次,直到所有目標(biāo)執(zhí)行完成,或者某個目標(biāo)不成立。

此時,再執(zhí)行make,就能得到預(yù)期的結(jié)果了

image.png

12、清理目標(biāo)

make編譯之后會在工程中多出很多目標(biāo)文件*.o,可以寫一個目標(biāo) clean 用來刪除工程中的目標(biāo)文件

clean:
	rm -rf $(BUILD_DIR)

Makefile全部內(nèi)容:

# 可執(zhí)行文件名
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 目錄下對應(yīng)的目標(biāo)文件 .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通用模板已經(jīng)編寫完成,文章中若有錯誤的地方請在評論區(qū)指出。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 文件
    +關(guān)注

    關(guān)注

    1

    文章

    566

    瀏覽量

    24746
  • Makefile
    +關(guān)注

    關(guān)注

    1

    文章

    125

    瀏覽量

    19184
  • make
    +關(guān)注

    關(guān)注

    0

    文章

    16

    瀏覽量

    12524
收藏 人收藏

    評論

    相關(guān)推薦

    Linux Makefile通用模板詳解

    對于Windows下開發(fā),很多IDE都集成了編譯器,如Visual Studio,提供了“一鍵編譯”,編碼完成后只需一個操作即可完成編譯、鏈接、生成目標(biāo)文件。
    發(fā)表于 08-08 09:48 ?892次閱讀
    Linux <b class='flag-5'>Makefile</b><b class='flag-5'>通用</b><b class='flag-5'>模板</b>詳解

    如何調(diào)整MPlab-ide生成的makefile?

    你好!我試圖修改MPLAB IDE生成的makefile,以便在編譯每個文件之前執(zhí)行代碼檢查器。我想修改makefile生成器或makefile模板,添加命令行來執(zhí)行這個檢查器,如果檢
    發(fā)表于 09-02 13:15

    使用Makefile+gcc編譯STM32

    方式,那時已經(jīng)使用Notepad++了,如今也一起在使用。Makefile模板如下:# STM32F103 Makefile模板# 參考來源:#
    發(fā)表于 11-22 08:10

    怎樣去編寫通用makefile文件的源碼呢

    怎樣去編寫通用makefile文件的源碼呢?其實驗結(jié)果有哪些?
    發(fā)表于 12-27 06:41

    基于模板通用文件對話框的可視化定制

    本文介紹了利用模板可視化定制通用文件對話框的實現(xiàn)方法。關(guān)鍵詞: 定制;通用文件對話框;模板;預(yù)覽在我們開發(fā)的CAD/CAM系統(tǒng)及其它應(yīng)用軟件中,經(jīng)常需要用戶進行一些
    發(fā)表于 08-21 10:08 ?14次下載

    駕馭Makefile

    駕馭Makefile在網(wǎng)上你能找到很多關(guān)于Makefile的學(xué)習(xí)資料,但絕大部分給你的只是一個知識點,與將Makefile運用到項目中(尤其是大型項目)的差距非常的大。因為,將Makefile
    發(fā)表于 01-05 17:05 ?9次下載

    linux makefile教程

     什么是makefile?或許很多Winodws的程序員都不知道這個東西,因為那些Windows的IDE都為你做了這個工作,但我覺得要作一個好的和professional的程序員,makefile
    發(fā)表于 11-12 09:11 ?5325次閱讀

    基于JasperReport通用的報表模板設(shè)計

    JasperReport是一個強大、靈活的開源報表生成工具,適用于各種Java應(yīng)用程序,是當(dāng)前Java開發(fā)者最常用的報表工具之一。利用JasperReport生成報表,首先需要設(shè)計報表模板,然后
    發(fā)表于 12-04 15:20 ?3次下載
    基于JasperReport<b class='flag-5'>通用</b>的報表<b class='flag-5'>模板</b>設(shè)計

    Makefile是什么?Makefile工作原理是怎樣的?Makefile經(jīng)典教程免費下載

    Makefile的重要性 會不會寫makefile,從一個側(cè)面說明了一個人是否具備完成大型工程的能力 makefile帶來的好處就是——“自動化編譯”,一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟件
    發(fā)表于 09-12 17:19 ?0次下載
    <b class='flag-5'>Makefile</b>是什么?<b class='flag-5'>Makefile</b>工作原理是怎樣的?<b class='flag-5'>Makefile</b>經(jīng)典教程免費下載

    Makefile的項目模板免費下載

    本文檔的主要內(nèi)容詳細介紹的是Makefile的項目模板免費下載 可根據(jù)項目名稱修改。
    發(fā)表于 06-12 08:00 ?11次下載
    <b class='flag-5'>Makefile</b>的項目<b class='flag-5'>模板</b>免費下載

    嵌入式中的Makefile應(yīng)用

    文章目錄一.Makefile 引入二. Makefile語法1.通配符2.假象目標(biāo)3.變量三.Makefile函數(shù)四.實例本文主要總結(jié)一下嵌入式開發(fā)中的Makefile,一般項目中都需
    發(fā)表于 11-03 17:06 ?11次下載
    嵌入式中的<b class='flag-5'>Makefile</b>應(yīng)用

    一個STM32編譯Makefile模板

    一個STM32編譯Makefile模板
    發(fā)表于 11-13 20:06 ?10次下載
    一個STM32編譯<b class='flag-5'>Makefile</b><b class='flag-5'>模板</b>

    AVR-GCC Makefile 中文翻譯

    所有的想要的功能都可以實現(xiàn)。但是我是個愛鉆牛角尖人,呵呵。看了一下 WINAVR Makefile模板我覺得寫得很好,學(xué)習(xí)了一下,翻譯了一下,加上很多我的理解和注釋。把它共享給大家,共同進步。這個M...
    發(fā)表于 11-15 13:36 ?35次下載
    AVR-GCC <b class='flag-5'>Makefile</b> 中文翻譯

    Makefile】簡單實用的Makefile模板來了

    【Linux + Makefile】簡單實用的Makefile模板來了
    的頭像 發(fā)表于 08-31 12:46 ?1906次閱讀
    【<b class='flag-5'>Makefile</b>】簡單實用的<b class='flag-5'>Makefile</b><b class='flag-5'>模板</b>來了

    三個Makefile模板分享

    ????本文分享三個Makefile模板:編譯可執(zhí)行程序、編譯靜態(tài)庫、編譯動態(tài)庫。 1 寫在前面 ????對于Windows下開發(fā),很多IDE都集成了編譯器,如Visual Studio,提供了
    的頭像 發(fā)表于 07-10 09:07 ?1028次閱讀
    三個<b class='flag-5'>Makefile</b><b class='flag-5'>模板</b>分享