Makefile程式設計快速上手

2021-10-03 21:00:17 字數 4395 閱讀 9436

makefile是乙個工程管理器,可以在makefile檔案中給工程,定義一系列的規則來指定哪些檔案需要先編譯,哪些檔案需要後編譯,哪些檔案需要重新編譯,甚至於進行更複雜的功能操作。簡而言之就是告訴系統如何去編譯這個工程。

其中我們使用makefile還有乙個很重要的原因,就是make工程管理器根據時間戳,來自動發現更新過的檔案從而減少編譯的工作量。

就這點文字說明可能體會不到這個make工程管理器減少編譯的工作量,我舉個例子說明一下,假如編譯器編譯乙個原始檔需要0.1s,假如你的工程有10個原始檔,那個工程編譯耗時1s,然後後面我們寫的大型工程**往往有成千上萬個原始檔,那麼每次編譯都需要100~1000s中,這裡花費的時間就特別的多,後期**出現bug,簡單的修改一次,就得編譯一次,不用make工程管理器的話,你的時間就都花在了編譯**上了。

make工程管理器是怎麼通過時間戳來減少編譯的工作量?我們應該了解一下程式編譯的過程:預處理,編譯,彙編,鏈結

預處理:新增標頭檔案,展開巨集定義,處理條件編譯的命令,刪除注釋

編譯:把預處理後的檔案轉換成特定的彙編**(中間包含程式的語法查錯)。

彙編:將上一步得到的彙編**進一步生成機器執行**,生成目標檔案。

工程中的所有原始檔都得單獨進行預處理、編譯、彙編過程,最後在一起進行鏈結過程。

make工程管理器,根據原始檔和目標檔案的時間戳來判斷原始檔是否修改過,若修改過,則重新生成對應的目標檔案,然後將生成的目標檔案重新鏈結得到新的可執行檔案。這裡沒有修改的原始檔則make工程管理器不會對其進行操作。

我們改了那個檔案,工程管理器就只重新編譯那個原始檔,而不用編譯工程中的所有原始檔,大大的減小了編譯的工作量。

不同產商的make各不相同,也有不同的語法,但其本質都是在「檔案依賴性」上做文章,這裡,我僅對gnu的make進行講述。

預設情況下,我們在終端使用make命令,系統會自動在當前目錄下按順序尋找檔名為「 gunmakefile 」,「 makefile 」,「makefile」的檔案,並且執行檔案中的命令,也就是說我們只要將makefile檔案設定為上面三個檔名中的乙個即可。

makefile的格式如下:

target1 : dependent_file

(tab)command 1

(tab)command 2

..

.目標1 : 依賴檔案

(tab)命令1

(tab)命令2

(tab)命令3

目標2 : 依賴檔案

(tab)命令4

(tab)命令5

目標3 : 依賴檔案

(tab)命令6

這個格式類似於乙個函式,先寫目標和依賴檔案,依賴檔案可為空,然後換行寫命令,切記命令前一定要用一次tab鍵,否則會出錯。命令根據實際情況編寫。

把最上面的目標1稱為是終級目標。預設情況下,make命令執行結果的就是完成終級目標對應的命令。在執行這些命令前,先去檢視相關的依賴檔案是否存在。如果依賴檔案不存在,先去執行其它目標,產生當前依賴檔案。

定義變數 :

obj=1.o 2.o 3.o

使用變數:$

直接展開:

x := var1

y := $(x) bar

x := var2

等價於:

y = var1 bar

x = var2

條件賦值操作

前面沒有定義該變數,現在定義使用現在賦的值。如果前面已經定義了該變數,則使用之前的值,不改變

例:immediate ?= deferred 

自定義變數:

$@目標名

$<

第乙個依賴目標. 如果依賴目標是多個, 逐個表示依賴目標

$^所有依賴目標的集合, 會去除重複的依賴目標

$?比目標新的依賴目標的集合

$%當目標是函式庫檔案時, 表示其中的目標檔名

$*這個是gnu make特有的, 其它的make不一定支援

$+所有依賴目標的集合, 不會去除重複的依賴目標

常用的為紅色字型的前三個。

下面根據**講解一下makefile程式設計上的語法

標頭檔案head.h

#ifndef _head_h_

#define _head_h_

#include #include #include void test1(void);

void test2(void);

void test3(void);

#endif

主函式原始檔main.c

#include "head.h"

int main(int argc, const char *ar**)

工程的的驅動原始檔test1.c

#include "head.h"

void test1(void)

工程的的驅動原始檔test2.c

#include "head.h"

void test2(void)

工程的的驅動原始檔test2.c

#include "head.h"

void test3(void)

makefile檔案

obj=main.o test1.o test2.o test3.o

gcc=gcc -c

output : $

gcc $ -o output

main.o : main.c head.h

$ main.c -o main.o

test1.o : test1.c head.h

$ test1.c -o test1.o

test2.o : test2.c head.h

$ test2.c -o test2.o

test3.o : test3.c head.h

$ test3.c -o test3.o

.phony : clean

clean:

rm *.o

這一種寫法是最基本的寫法,每個目標檔案都由依賴檔案編譯所得。在這裡有時候我們的標頭檔案並不是在同乙個位置,這裡我們就需要我們自己利用gcc相關命令對頭檔案路徑進行指定。變數的使用就是為了減少書寫的工作量以及**檢視更直觀。

obj=main.o test1.o test2.o test3.o

gcc=gcc -c

output : $

gcc $^ -o $@

main.o : main.c head.h

$ $< -o $@

test1.o : test1.c head.h

$ $< -o $@

test2.o : test2.c head.h

$ $< -o $@

test3.o : test3.c head.h

$ $< -o $@

.phony : clean

clean:

rm *.o

這一種寫法是利用自動變數來減少書寫工作量。

還有一種是利用makefile的自動推到規則,

obj=main.o test1.o test2.o test3.o

gcc=gcc -c

output : $

gcc $^ -o $@

main.o : main.c head.h

test1.o : test1.c head.h

test2.o : test2.c head.h

test3.o : test3.c head.h

.phony : clean

clean:

rm *.o

//超級簡寫 把依賴關係也省略掉

obj=main.o test1.o test2.o test3.o

gcc=gcc -c

output : $

gcc $^ -o $@

.phony : clean

clean:

rm *.o

只要make看到乙個[.o]檔案,它就會自動的把[.c]檔案加在依賴關係中,如果make找到乙個main.o,那麼mian.c,就會是main.o的依賴檔案。並且 cc -c main.c 也會被推導出來,於是,我們的makefile再也不用寫得這麼複雜。我們的是新的makefile又出爐了。

在makefile程式設計中依賴檔案中注意要將標頭檔案寫上,不寫的話,標頭檔案修改,make工程管理器並不會自動檢測標頭檔案,在後面鏈結的時候會出現錯誤。

倉促成文,不當之處,尚祈方家和讀者批評指正。聯絡郵箱[email protected]

Python程式設計快速上手 實踐專案

例如,字典值 意味著玩家有1條繩索 6個火把 42枚金幣等。寫乙個名為displayinventory 的函式,它接受任何可能的物品清單,並顯示如下 inventory 12 arrow 42 gold coin 1 rope 6 torch 1 dagger total number of ite...

《Python遊戲程式設計快速上手》 導讀

在寫作本書的過程中,我意識到,像python這樣的現代語言使得程式設計更加容易,並且為新一代的程式設計師提供了更多的功能。python擁有平緩的學習曲線,而且是供專業程式設計師使用的一種正規語言。目前的程式設計書籍大多分為兩種型別。第一種,與其說是教程式設計的書,倒不如說是在教 遊戲製作軟體 或教授...

Python程式設計快速上手 global 語句

如果要在乙個函式內修改全域性變數,就使用 global 語句。如果在函式的頂部有 global eggs 這樣的 它就告訴python 這個函式中,eggs 指的是全域性變數,所以不要用這個名字建立乙個區域性變數。示例 samename2.py def spam global eggs eggs s...