代碼的編譯過程: php
ps: 當中間文件太多的時候編譯起來就很不方便,一般咱們會將其打包,windows 下叫庫文件(Library File , .lib 文件),Unix 下 是 Archive File , 也就是 .a 文件html
Make 概念node
Make 這個詞,英語意思爲「製做」。 Make 命令直接使用了這個意思,就是要作出某個文件出來。 好比我要作出文件 a.txt ,就能夠直接使用 c++
make a.txt
可是,若是你真的輸入這條命令,它不會起到任何做用,由於 Make 自己並不知道,如何作出 a.txt 須要有人告訴它怎麼去作出a.txt 文件。shell
用例一:windows
a.txt 文件依賴於 b.txt 和 c.txt 這兩個文件,是後面兩個文件內容的集合。那麼make 須要知道下面的規則。bash
a.txt: b.txt c.txt #第一步 cat b.txt c.txt > a.txt #第二步
解析:函數
第一步:確認 b.txt 和 c.txt 這兩個文件必須已經存在。工具
第二步: 使用cat 命令將這兩個文件合併起來了,輸出爲新文件。ui
總結:
Makefile 文件格式
構建規則都寫在了Makefile 文件中,要學會如何Make 命令,就必須先學會如何編寫Makefile 文件
概述:
Makefile 文件由一系列規則構成,格式:
<target> :<prerequisites>
<commands>
解析:
target : 目標,必須的
prerequisites: 前置條件,可選
commands: 命令,它前面須要跟着一個空格 , 可選
目標<target>
一個目標(target) 就構成了一個規則,目標一般是文件名,指明make 命令所要構建的對象,好比上文的 a.txt 。 目標能夠是一個文件名,也能夠是多個文件名,之間用空格隔開。
除了文件名,目標還能夠是操做名或任意字母組成的字符串,這類目標稱爲 「僞目標」 (phony target)
上面代碼的目標是 bb , 它不是文件名 , 而是一個操做名,屬於「僞目標」 , 做用是列舉根目下的文件或目錄
ps : 僞目標,系統是不會去檢查bb 文件是否存在的。而是每次執行都執行對應的命令。
可是,若是目錄中恰好有個文件就叫bb , 那麼bb 也就變成了「目標」了,避免這種狀況,能夠使用以下命令
.PHONY: bb clean: ls /
經過 ".PHONY" 能夠用來指定僞目標,這種內置目標名還有不少,能夠查看手冊
前置條件 prerequisites
前置條件一般是一組文件名,之間用空格分隔。它指定了「目標」 , 是否重現構建的判斷標準:只要有一個前置文件不存在或者被更新了,目標就須要從新構建
用例一:
result.txt: source.txt cp source.txt result.txt
解析:
上面的代碼中,構建了目標 result.txt , 它的前置條件是 source.txt 已經存在了,那麼 make result.txt 能夠正常運行,不然必須再寫一條規則,來生成 source.txt
用例二:
source.txt: echo " hello world" > source.txt
解析:
上面的代碼中,source.txt 後面沒有前置條件 , 這意味着這個「source.txt」目標不依賴其它文件,直接運行它的 commands 就行了。只要source.txt 不存在 , 每次調用 make source.txt 它都會生成 source.txt
若是你連續執行兩次 make source.txt , 它只會執行第一次就不會再執行了
用例三:
source: a.txt b.txt c.txt
解析:
source 是一個僞目標 , 它只有三個前置條件 , 沒有任何的 commands 。做用:一次性建立a.txt , b.txt , c.txt 這三個文件。
命令(commands)
命令就是一堆shell 命令組成的,它是構建「目標」 的具體指令,它的運行結果一般就是生成目標文件。每行命令以前必須有一個tab鍵,若是要使用其它鍵,能夠使用內置變量 .RECIPEPREFIX 聲明
用例一:
.RECIPEPREFIX = > all: > echo Hello , world
解析:
上面的代碼就是用 .RECIPEPREFIX 來指定了每行命令開頭 是 > 而再也不是 tab鍵了
用例二:
var-lost: export foo=bar echo "foo=[$$foo]"
解析:
commands 中的每條獨自換行的命令都是獨立分開的,不在同一個進程了,因此數據也是不共享的
第一行的 定義的變量 foo 是不能在 第二行中使用的
解決辦法就是:
var-kept: export foo=bar; echo "foo=[$$foo]" 或者 var-kept: export foo=bar;\ echo "foo=[$$foo]" 或者 .ONESHELL var-kept: export foot=bar; echo "foo=[$$foo]"
Makefile 文件的語法
註釋 :
在Makefile 文件中 註釋符是: #
回聲:
正常狀況下 , make 會 打印每條命令 , 而後再執行, 這叫作回聲(echoing).
若是你想關閉回聲,直接在shell 命令行前面加一個 @ 符號就行了
test: # this is test @echo TODO
通配符:
通配符 , 用來指定符合條件的文件名的。 Makefile 的通配符與Bash 一致 , 主要有 * , ? 好比以o 結尾的文件 能夠這樣 *.o
模式匹配
Make 命令 容許 對文件名 , 進行相似正則運算的匹配 , 主要用到的匹配符是 % , 好比須要找到 當前目錄下 有 f1.c 和 f2.c 兩個源碼文件,須要將它們編譯僞對應的對象文件
%.o : %.c 等同於 f1.o :f1.c f2.o :f2.c
變量和賦值符
Makefile 中容許使用 等號 「 = 」 來 自定義變量
bb = Hello World test: @echo $(bb) @echo $$HOME
解析:
變量bb 等於 Hello World , 變量調用是經過 $() 來調用的。
若是你要調用Shell 自帶的一些變量,那麼你須要在 美圓符號前面再加一個美圓符號, 由於 Makefile 中會對美圓符號進行轉義
Makefile 中提供了 4中賦值運算符( = , := , ?= , += ) :
#執行時擴展, 容許遞歸擴展 key = value #定義是擴展 key := value #只有在該變量爲空時才設置值 key ?= value #將值追加到變量的尾端 key += value
內置變量
Make 命令提供了一系列的內置變量,詳情見手冊
自動變量
Make 命令提供了一些自動變量,他們的值與當前規則有關。
(1)$@ :
代指當前目標,就是Make 命令當前構建的那個目標, 好比 make foo 的 $@ 就值 foo
a.txt b.txt: touch $@ 等同於 a.txt: touch a.txt b.txt: touch b.txt
(2) $<
代指第一個前置條件
a.txt : b.txt c.txt cp $< $@ 等同於 a.txt: b.txt c.txt cp b.txt a.txt
(3) $?
代指比目標更新的全部前置條件 【即:更新的依賴文件】,好比, t1: p1 p2 , p2 的時間戳比t1 新,那麼 $? 就代指 p2
(4) $^
代指全部前置條件
(5) $*
代指匹配符 % 匹配成功的部分 , 好比 %匹配 f1.txt 中的 f1 , 那麼 $* 就表示 f1
(6) $(@D) 和 $(@F)
$(@D) 和 $(@F) 分別代指: $@ 的目錄名和文件名
(7)$(<D) 和 $(<F)
$(<D) 和 $(<F) 分別代指: $< 的目錄名和文件名
其它的自動變量請查看手冊
用例一:
dest/%.txt: src/%.txt @[ -d dest ] || mkdir dest cp $< $@
解析:
1. 上面的代碼就是將 src 目錄下的 txt 文件 拷貝到 dest 目錄下 。
2. @[-d dest] || mkdir dest : 是說 判斷 dest 目錄是否存在,不存在則建立之 , 而且關閉回聲
3. cp $< $@ : 將 src 中的 txt 拷貝到 dest 目錄下
判斷和循環
Makefile 使用Bash 語法,完成了 判斷和循環
用例一:
ifeq ($(CC),gcc) libs=$(libs_for_gcc) else libs=$(normal_libs) endif
用例二:
LIST = one two three test: for i in $(LIST); do \ echo $$i; \ done 等同於 test: for i in one two three; do \ echo $i; \ done
函數
Makefile 還能夠使用函數 , 它許多內置函數 見手冊
格式:
$(function arguments) 或者 ${function arguments}
Makefile 實例
編譯 c語言項目
edit: main.o kbd.o command.o display.o cc -o edit main.o kbd.o command.o display.o main: main.c defs.h cc -c main.c kdb.o: kbd.c defs.h command.h cc -c kbd.c command.o: command.c defs.h command.h cc -c command.c display.o: display.c defs.h cc -c display.c clean: rm edit main.o kbd.o command.o display.o .PHONY: edit clean