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

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

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

01

前言


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

02

準備工作


首先,假設我們有如下幾個代碼文件: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 會將源碼編譯成名為 function1.o 對象文件。如果不想采用默認的命名,也可以自定義文件名,例如:

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

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

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

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

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

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

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

04

使用Makefile編譯

第一版Makefile

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

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


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

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

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

這個 Makefile 雖然可以省去敲命令的痛苦,卻無法選擇性編譯源碼。因為我們把所有源文件都一股腦塞進了一條命令,每次都要編譯整個工程,很浪費時間。第二版 Makefile 將解決這個問題。

第二版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>。即,目標:目標依賴的文件。

順著代碼捋一下邏輯:

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

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

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

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

第一次編譯的時候,命令行會輸出:

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

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

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

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

第三版Makefile

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

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

第四版Makefile

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

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

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

$@ ,$<,$^

例如我們有 target: dependencies 對:all: library.cpp main.cpp

$@ 指代 all ,即 target

$< 指代 library.cpp, 即第一個 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

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

第五版Makefile

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

wildcard

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

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

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

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

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

patsubst

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

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

綜合上述兩個命令,我們可以升級到第五版 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 使用的都是同一套模板,不過換了個名字而已。第六版的 Makefile 將處理這個問題。

第六版Makefile

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

targets: target-pattern: prereq-patterns

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


通過上面的方式,可以對 targets 列表中任何一個元素,找到它對應的依賴文件,例如通過 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 設置了 -lm 的 flag,查閱資料發(fā)現(xiàn)表示連街 math 庫,因為代碼中可能

#include<math.h>

例如 

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

/ The End /

本文介紹了如何寫 Makefile,主要的知識點有:

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

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

  3. wildcard,patsubst 的用法

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

公眾號回復:"Makefile"  獲取本文源文件下載鏈接。


推薦閱讀:

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

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

掃碼關(guān)注我們

看更多嵌入式案例

喜歡本篇內(nèi)容請給我們點個在看

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

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