www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號(hào)精選 > 嵌入式案例Show
[導(dǎo)讀]點(diǎn)擊上方藍(lán)字關(guān)注我哦~ 01 前言 今天抽空研究了下 Makefile,在這里整理一下各處搜到的資料,以備將來復(fù)習(xí)時(shí)快速上手,同時(shí)也幫助和我一樣的初學(xué)者們節(jié)約時(shí)間。 02 準(zhǔn)備工作 首先,假設(shè)我們有如下幾個(gè)代碼文件:main.cpp functions.h function1.cpp function

點(diǎn)擊上方藍(lán)字關(guān)注我哦~

01

前言


今天抽空研究了下 Makefile,在這里整理一下各處搜到的資料,以備將來復(fù)習(xí)時(shí)快速上手,同時(shí)也幫助和我一樣的初學(xué)者們節(jié)約時(shí)間。

02

準(zhǔn)備工作


首先,假設(shè)我們有如下幾個(gè)代碼文件:main.cpp functions.h function1.cpp function2.cpp

--- functions.h ---// functions.hvoid print_hello();int factorial(int n);--- function1.cpp ---// function1.cpp#include "functions.h"int factorial(int n){ if (n!=1) return n*factorial(n-1); else return 1;}--- function2.cpp ---//function2.cpp#include<iostream>#include "functions.h"void print_hello(){ std::cout << "hello world" << std::endl;}--- main.cpp ---//main.cpp# include<iostream># include "functions.h"
int main(){ print_hello(); std::cout << "this is main" << std::endl; std::cout << "The factorial of 5 is " << factorial(5) << std::endl; return 0;}

03

不用makefile如何編譯

如果不用 makefile,則需要按照下面的方式編譯上述代碼:

g++ -c function1.cppg++ -c function2.cppg++ -c main.cppg++ -o hello main.o function1.o function2.o

其中,g++ -c function1.cpp 會(huì)將源碼編譯成名為 function1.o 對(duì)象文件。如果不想采用默認(rèn)的命名,也可以自定義文件名,例如:

g++ -c function1.cpp -o fun1.o

也可以用一行命令整合編譯、鏈接的步驟:

g++ -o hello main.cpp function1.cpp function2.cpp

這種方式有很多弊端,例如:

  1. 每次編譯、鏈接都需要手動(dòng)敲的很多命令。

  2. 當(dāng)工程量很大時(shí),編譯整個(gè)工程需要花很久。而我們往往并不是每次都修改了所有源文件,因此希望程序自動(dòng)編譯那些被修改的源碼,而沒被修改的部分不要浪費(fèi)時(shí)間重新編譯。

為了解決上述第一個(gè)問題,我們可以把所有編譯需要的命令保存到文件中,編譯時(shí)一鍵執(zhí)行。針對(duì)第二個(gè)問題,我們希望有一個(gè)軟件,自動(dòng)檢測哪些源文件被修改過,然后自動(dòng)把它們挑出來選擇性地編譯。而 make 命令通過檢測代碼文件的時(shí)間戳,決定是否編譯它。

04

使用Makefile編譯

第一版Makefile

首先需要確定 Makefile 的名字,需要設(shè)置成 Makefile 或者 makefile,而不能是其它版本(MakeFile, Make_file, makeFile,... )。其次,需要注意的是 Makefile 是縮進(jìn)敏感的,在行首一定不能隨便打空格。下面我們看一下第一版 Makefile。

Makefile (#號(hào)為注釋)all: g++ -o hello main.cpp function1.cpp function2.cppclean: rm -rf *.o hello


(注意上面代碼片段的縮進(jìn),是一個(gè)<tab>而不是4個(gè)或者8個(gè)空格。)

其中 all 、clean的術(shù)語為 target,我也可以隨意指定一個(gè)名字,例如 abc,真正執(zhí)行編譯的是它下面縮進(jìn)行的命令。我們可以看到,這個(gè)命令和我們?cè)诿钚兄惺謩?dòng)敲的沒有任何區(qū)別。因此,通過這個(gè)簡單的 Makefile,就可以省去了每次手動(dòng)敲命令的痛苦:只需要在命令行敲下 make 回車,即可完成編譯。

clean 表示清除編譯結(jié)果,它下方就是普通的命令行刪除文件命令。命令行輸入 make 將默認(rèn)執(zhí)行第一個(gè) target (即 all)下方的命令;如要執(zhí)行清理操作,則需要輸入 make clean,指定執(zhí)行 clean 這個(gè) target 下方的命令。

這個(gè) Makefile 雖然可以省去敲命令的痛苦,卻無法選擇性編譯源碼。因?yàn)槲覀儼阉性次募家还赡X塞進(jìn)了一條命令,每次都要編譯整個(gè)工程,很浪費(fèi)時(shí)間。第二版 Makefile 將解決這個(gè)問題。

第二版Makefile

既然我們希望能夠選擇性地編譯源文件,就不能像上一節(jié)那樣把所有源文件放在一條命令里編譯了,而是要分開寫:

all: hellohello: main.o function1.o function2.o g++ main.o function1.o function2.o -o hellomain.o: main.cpp g++ -c main.cppfunction1.o: function1.cpp g++ -c function1.cppfunction2.o: function2.cpp g++ -c function2.cpp
clean: rm -rf *.o hello

上面的 Makefile 包含了一條重要的語法:<target>:<dependencies>。即,目標(biāo):目標(biāo)依賴的文件。

順著代碼捋一下邏輯:

  1. 命令行輸入 make ,將默認(rèn)執(zhí)行 all 這個(gè) target;

  2. 而 all 這個(gè) target 依賴于 hello,hello 在當(dāng)前目錄下并不存在,于是程序開始往下讀取命令..……終于找到了 hello 這個(gè) target;

  3. 正待執(zhí)行 hello 這個(gè) target 的時(shí)候,卻發(fā)現(xiàn)它依賴于 main.o,function1.o,function2.o 這三個(gè)文件,而它們?cè)诋?dāng)前目錄下都不存在,于是程序繼續(xù)向下執(zhí)行;

  4. 遇到 main.o target,它依賴于 main.cpp。而 main.cpp 是當(dāng)前目錄下存在的文件,終于可以編譯了,生成 main.o 對(duì)象文件。后面兩個(gè)函數(shù)以此類推,都編譯好之后,再回到 hello target,連接各種二進(jìn)制文件,生成 hello 文件。

第一次編譯的時(shí)候,命令行會(huì)輸出:

g++ -c main.cppg++ -c function1.cppg++ -c function2.cppg++ main.o function1.o function2.o -o hello

證明所有的源碼都被編譯了一遍。假如我們對(duì) main.cpp 做一點(diǎn)修改,再重新 make(重新 make 前不要 make clean),則命令行只會(huì)顯示:

g++ -c main.cppg++ main.o function1.o function2.o -o hello

這樣,我們就發(fā)揮出 Makefile 選擇性編譯的功能了。下面,將介紹如何在 Makefile 中聲明變量(declare variable)

第三版Makefile

我們希望將需要反復(fù)輸入的命令整合成變量,用到它們時(shí)直接用對(duì)應(yīng)的變量替代,這樣如果將來需要修改這些命令,則在定義它的位置改一行代碼即可。

CC = g++CFLAGS = -c -WallLFLAGS = -Wall
all: hellohello: main.o function1.o function2.o $(CC) $(LFLAGS) main.o function1.o function2.o -o hellomain.o: main.cpp $(CC) $(CFLAGS) main.cppfunction1.o: function1.cpp $(CC) $(CFLAGS) function1.cppfunction2.o: function2.cpp $(CC) $(CFLAGS) function2.cpp
clean: rm -rf *.o hello

上面的 Makefile 中,開頭定義了三個(gè)變量:CC,CFLAGS,和 LFLAGS。其中 CC 表示選擇的編譯器(也可以改成 gcc);CFLAGS 表示編譯選項(xiàng),-c 即 g++ 中的 -c,-Wall 表示顯示編譯過程中遇到的所有 warning;LFLAGS 表示鏈接選項(xiàng),它就不加 -c 了。這些名字都是自定義的,真正起作用的是它們保存的內(nèi)容,因此只要后面的代碼正確引用,將它們定義成阿貓阿狗都沒問題。容易看出,引用變量名時(shí)需要用 $() 將其括起來,表示這是一個(gè)變量名。

第四版Makefile

第三版的 Makefile 還是不夠簡潔,例如我們的 dependencies 中的內(nèi)容,往往和 g++ 命令中的內(nèi)容重復(fù):

hello: main.o function1.o function2.o $(CC) $(LFLAGS) main.o function1.o function2.o -o hello

我們不想敲那么多字,能不能善用 <target>:<dependencies> 中的內(nèi)容呢?這就需要引入下面幾個(gè)特殊符號(hào)了(也正是這些特殊符號(hào),把 Makefile 搞得像是天書,嚇退了很多初學(xué)者):

$@ ,$<,$^

例如我們有 target: dependencies 對(duì):all: library.cpp main.cpp

$@ 指代 all ,即 target

$< 指代 library.cpp, 即第一個(gè) dependency

$^ 指代 library.cpp 和 main.cpp,即所有的 dependencies

因此,本節(jié)開頭的 Makefile 片段可以改為:

hello: main.o function1.o function2.o $(CC) $(LFLAGS) $^ -o $@

而第四版 Makefile 就是這樣的:

CC = g++CFLAGS = -c -WallLFLAGS = -Wall
all: hellohello: main.o function1.o function2.o $(CC) $(LFLAGS) $^ -o $@main.o: main.cpp $(CC) $(CFLAGS) $<function1.o: function1.cpp $(CC) $(CFLAGS) $<function2.o: function2.cpp $(CC) $(CFLAGS) $<
clean: rm -rf *.o hello

但是手動(dòng)敲文件名還是有點(diǎn)麻煩,能不能自動(dòng)檢測目錄下所有的 cpp 文件呢?此外 main.cpp 和 main.o 只差一個(gè)后綴,能不能自動(dòng)生成對(duì)象文件的名字,將其設(shè)置為源文件名字后綴換成 .o 的形式?

第五版Makefile

想要實(shí)現(xiàn)自動(dòng)檢測 cpp 文件,并且自動(dòng)替換文件名后綴,需要引入兩個(gè)新的命令:patsubst 和 wildcard。

wildcard

wildcard 用于獲取符合特定規(guī)則的文件名,例如下面的代碼:

SOURCE_DIR = . # 如果是當(dāng)前目錄,也可以不指定SOURCE_FILE = $(wildcard $(SOURCE_DIR)/*.cpp)target: @echo $(SOURCE_FILE)

make 后發(fā)現(xiàn),輸出的為當(dāng)前目錄下所有的 .cpp 文件:

./function1.cpp ./function2.cpp ./main.cpp

其中 @echo 前加 @是為了避免命令回顯,上文中 make clean 調(diào)用了 rm -rf 會(huì)在 terminal 中輸出這行命令,如果在 rm 前加了 @ 則不會(huì)輸出了。

patsubst

patsubst 應(yīng)該是 pattern substitution 的縮寫。用它可以方便地將 .cpp 文件的后綴換成 .o。它的基本語法是:$(patsubst 原模式,目標(biāo)模式,文件列表)。運(yùn)行下面的示例:

SOURCES = main.cpp function1.cpp function2.cppOBJS = $(patsubst %.cpp, %.o, $(SOURCES))target: @echo $(SOURCES) @echo $(OBJS)

輸出的結(jié)果為:

main.cpp function1.cpp function2.cppmain.o function1.o function2.o

綜合上述兩個(gè)命令,我們可以升級(jí)到第五版 Makefile:

OBJS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))CC = g++CFLAGS = -c -WallLFLAGS = -Wall
all: hellohello: $(OBJS) $(CC) $(LFLAGS) $^ -o $@main.o: main.cpp $(CC) $(CFLAGS) $< -o $@function1.o: function1.cpp $(CC) $(CFLAGS) $< -o $@function2.o: function2.cpp $(CC) $(CFLAGS) $< -o $@
clean: rm -rf *.o hello

然而這一版的 Makefile 還有提升空間,它的 main.o,function1.o,function2.o 使用的都是同一套模板,不過換了個(gè)名字而已。第六版的 Makefile 將處理這個(gè)問題。

第六版Makefile

這里要用到 Static Pattern Rule,其語法為:

targets: target-pattern: prereq-patterns

其中 targets 不再是一個(gè)目標(biāo)文件了,而是一組目標(biāo)文件。而 target-pattern 則表示目標(biāo)文件的特征。例如目標(biāo)文件都是 .o 結(jié)尾的,那么就將其表示為 %.o,prereq-patterns (prerequisites) 表示依賴文件的特征,例如依賴文件都是 .cpp 結(jié)尾的,那么就將其表示為 %.cpp。


通過上面的方式,可以對(duì) targets 列表中任何一個(gè)元素,找到它對(duì)應(yīng)的依賴文件,例如通過 targets 中的 main.o,可以鎖定到 main.cpp。

下面是第六版的 Makefile:

OBJS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))CC = g++CFLAGS = -c -WallLFLAGS = -Wall
all: hellohello: $(OBJS) $(CC) $(LFLAGS) $^ -o $@$(OBJS):%.o:%.cpp $(CC) $(CFLAGS) $< -o $@
clean: rm -rf *.o hello


05

其它

看到有的 Makefile 設(shè)置了 -lm 的 flag,查閱資料發(fā)現(xiàn)表示連街 math 庫,因?yàn)榇a中可能

#include<math.h>

例如 

g++ -o out fun.cpp -lm
CC = g++LIBS = -lmout: fun.cpp $(CC) -o $@ $^ $(LIBS)

/ The End /

本文介紹了如何寫 Makefile,主要的知識(shí)點(diǎn)有:

  1. 在 Makefile 中定義變量并引用

  2. $^,$@,$< 的含義

  3. wildcard,patsubst 的用法

  4. static pattern rule:targets: target-pattern: prereq-patterns

公眾號(hào)回復(fù):"Makefile"  獲取本文源文件下載鏈接。


推薦閱讀:

win10下使用VS Code編譯、運(yùn)行 和調(diào)試C
使用git管理嵌入式軟件版本
基于VS2015的串口開發(fā)

免責(zé)聲明:本文轉(zhuǎn)載自知乎,版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請(qǐng)與我聯(lián)系刪除。

掃碼關(guān)注我們

看更多嵌入式案例

喜歡本篇內(nèi)容請(qǐng)給我們點(diǎn)個(gè)在看

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場,如有問題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
關(guān)閉
關(guān)閉