Makefile簡單入門

最近工作編譯程序一直在用別人寫的Makefile茶宵,但是沒有系統(tǒng)的學(xué)習(xí)過,趁著放假學(xué)一波


makefile

0x00 Makefile 概述

一個(gè)企業(yè)級項(xiàng)目而克,通常會有很多源文件,有時(shí)也會按功能怔毛、類型员萍、模塊分門別類的放在不同的目錄中,有時(shí)候也會在一個(gè)目錄里存放了多個(gè)程序的源代碼拣度。

這時(shí)碎绎,如何對這些代碼的編譯就成了個(gè)問題。Makefile 就是為這個(gè)問題而生的抗果,它定義了一套規(guī)則筋帖,決定了哪些文件要先編譯,哪些文件后編譯冤馏,哪些文件要重新編譯日麸。

整個(gè)工程通常只要一個(gè) make 命令就可以完成編譯、鏈接逮光,甚至更復(fù)雜的功能赘淮。可以說睦霎,任何一個(gè) Linux 源程序都帶有一個(gè)Makefile 文件梢卸。

0x01 Makefile 的優(yōu)點(diǎn)

管理代碼的編譯,決定該編譯什么文件副女,編譯順序蛤高,以及是否需要重新編譯;節(jié)省編譯時(shí)間。如果文件有更改戴陡,只需重新編譯此文件即可塞绿,無需重新編譯整個(gè)工程;一勞永逸恤批。Makefile 通常只需編寫一次异吻,后期就不用過多更改。

0x02 編譯知識

Makefile最初是為了編譯C/C++而誕生的喜庞, 所以它里面的很多隱藏規(guī)則都是針對 C/C++的诀浪。在講 Makefile 之前有必要對 C/C++的編譯有一點(diǎn)了解

過程如下:
compile

預(yù)處理器:將.c 文件轉(zhuǎn)化成 .i 文件,使用的 gcc 命令是:gcc –E延都,對應(yīng)于預(yù)處理命令 cpp雷猪;
編譯器:將.c/.h 文件轉(zhuǎn)換成.s 文件,使用的 gcc 命令是:gcc –S晰房,對應(yīng)于編譯命令 cc –S求摇;
匯編器:將.s 文件轉(zhuǎn)化成 .o 文件,使用的 gcc 命令是:gcc –c殊者,對應(yīng)于匯編命令是 as与境;
鏈接器:將.o 文件轉(zhuǎn)化成可執(zhí)行程序,使用的 gcc 命令是: gcc猖吴,對應(yīng)于鏈接命令是 ld嚷辅;
加載器:將可執(zhí)行程序加載到內(nèi)存并進(jìn)行執(zhí)行,loader 和 ld-linux.so距误。

0x03 Makefile規(guī)則

Target...:   Prerequsites...
Command
Command
...

Targets: Prerequisites;Command
Command
...

下面會稱 Target 為目標(biāo), Prerequisites 為目標(biāo)依賴扁位, Command 為規(guī)則的命令行
Command 必須以[Tab]開始, Command 可以寫成多行准潭,通過來繼行,但行尾的后不能有空格域仇。
規(guī)則包含了文件之間的依賴關(guān)系和更新此規(guī)則 target 所需要的 Command

targets 可以使用通配符刑然, 如果格式是"A(M)"表示檔案文件(.a)中的成員“

在需要用本義的時(shí)候,使用兩個(gè)$$來表示暇务。
當(dāng)規(guī)則的 target 是一個(gè)文件泼掠,它的任何一個(gè)依賴文件被修改后,在執(zhí)行 make <target>時(shí)這個(gè)目標(biāo)文件都會被重新編譯或重新連接垦细。如果有必要此 target 的一個(gè)依賴文件也會被先重新編譯择镇。

0x04偽目標(biāo)

Makefile 中把那些沒有任何依賴只有執(zhí)行動作的目標(biāo)稱為“偽目標(biāo)“(Phony targets)

.PHONY : clean
clean :
-rm edit $(objects

通過.PHONY 將 clean 聲明為偽目標(biāo),避免當(dāng)目錄下有名為“clean”文件時(shí)括改,clean 無法執(zhí)行
這樣的目標(biāo)不是為了創(chuàng)建或更新程序腻豌,而是執(zhí)行相應(yīng)動作。

0x05自動推導(dǎo)規(guī)則

在使用 make 編譯.c 源文件時(shí),編譯.c 源文件規(guī)則的命令可以不用明確給出吝梅。這是因?yàn)?make 本身存在一個(gè)默認(rèn)的規(guī)則虱疏,能夠自動完成對.c 文件的編譯并生成對應(yīng)的.o 文件。它執(zhí)行命令“cc -c”來編譯.c 源文件苏携。在 Makefile 中我們只需要給出需要重建的目標(biāo)文件名(一個(gè).o 文件)做瞪,make 會自動為這個(gè).o 文件尋找合適的依賴文件(對應(yīng)的.c 文件。對應(yīng)是指:文件名除后綴外右冻,其余都相同的兩個(gè)文件)装蓬,而且使用正確的命令來重建這個(gè)目標(biāo)文件。
例如, 現(xiàn)在有三個(gè)文件 test.cpp, my.cpp, my.h

image.png

  • test.cpp
#include <iostream>
#include "my.h"

int main(int argc, char * argv[]) {
    int a = 100, b = 101;
    std::cout << "this code is for test makefile" << std::endl;
    std::cout << xadd(a, b) << std::endl;
}
  • my.h
#ifndef _MY_H_
#define _MY_H_
int xadd(const int x, const int y);
#endif
  • my.cpp
#include "my.h"
int xadd(const int x, const int y)
{
    return x + y;
}

對于上邊的例子国旷,此默認(rèn)規(guī)則就使用命令“gcc -c test.cpp -o test.o”來創(chuàng)建文件“main.o”矛物。對一個(gè)目標(biāo)文件是“N.o”,倚賴文件是“N.c”的規(guī)則跪但,完全可以省略其規(guī)則的命令行履羞,而由 make 自身決定使用默認(rèn)命令。此默認(rèn)規(guī)則稱為 make 的隱含規(guī)則屡久。

test: test.cpp my.o
    gcc  -c -o test test.cpp
my.o: my.cpp my.h
    gcc -c  -o my.o  my.cpp

clean :
    rm test  my.o

也可以用隱式規(guī)則

test: test.cpp my.o

my.o: my.cpp my.h

clean :
    rm test  my.o

效果是一樣的

這里要說明一點(diǎn)的是忆首, clean 不是一個(gè)文件,它只不過是一個(gè)動作名字被环,有點(diǎn)像c語言中的label一 樣糙及,其冒號后什么也沒有,那么筛欢,make就不會自動去找它的依賴性浸锨,也就不會自動執(zhí)行其后所定義的命令。 要執(zhí)行其后的命令版姑,就要在make命令后明顯得指出這個(gè)label的名字柱搜。這樣的方法非常有用,我們可以在一 個(gè)makefile中定義不用的編譯或是和編譯無關(guān)的命令剥险,比如程序的打包聪蘸,程序的備份,等等表制。

0x06 規(guī)則書寫建議

書寫規(guī)則建議的方式是:單目標(biāo)健爬,多依賴。就是說盡量要做到一個(gè)規(guī)則中只存在一個(gè)目標(biāo)文件么介,可有多個(gè)依賴文件娜遵。盡量避免使用多目標(biāo),單依賴的方式壤短。

0x07 makefile 工作原理文和件搜索順序

在默認(rèn)的方式下魔熏,也就是我們只輸入 make 命令衷咽。那么,

  1. 首先會搜索目錄下的GNUmakefile,makefile,Makefile文件蒜绽,或者make -f從指定文件讀取
    2.找到makefile后首先從第一個(gè)target開始镶骗,如果生成target依賴別的目標(biāo)就遞歸從依賴開始
    例如:上面的例子中,首先準(zhǔn)備編譯生成目標(biāo)test躲雅,發(fā)現(xiàn)依賴my.o沒有生成,就向下找my.o的生成鼎姊,發(fā)現(xiàn)my.o的資源my.cpp,my.h已經(jīng)就緒了,就先編譯出my.o,回到test,發(fā)現(xiàn) test.cppmy.o全部就緒相赁,使用規(guī)則Command生成目標(biāo)test

這就是整個(gè)make的依賴性相寇,make會一層又一層地去找文件的依賴關(guān)系,直到最終編譯出第一個(gè)目標(biāo)文件钮科。在 找尋的過程中唤衫,如果出現(xiàn)錯(cuò)誤,比如最后被依賴的文件找不到绵脯,那么make就會直接退出佳励,并報(bào)錯(cuò),而對于所 定義的命令的錯(cuò)誤蛆挫,或是編譯不成功赃承,make根本不理。make只管文件的依賴性悴侵,即瞧剖,如果在我找了依賴關(guān)系 之后,冒號后面的文件還是不在可免,那么對不起抓于,我就不工作啦。

通過上述分析浇借,我們知道捉撮,像clean這種,沒有被第一個(gè)目標(biāo)文件直接或間接關(guān)聯(lián)逮刨,那么它后面所定義的命 令將不會被自動執(zhí)行,不過堵泽,我們可以顯示要make執(zhí)行修己。即命令—— make clean ,以此來清除所有 的目標(biāo)文件迎罗,以便重編譯睬愤。

0x08 makefile中使用變量

我們可以看到 .o 文件的字符串被重復(fù)了兩次,如果我們的工程需要加入一個(gè)新的 .o 文件纹安, 那么我們需要在兩個(gè)地方加(應(yīng)該是三個(gè)地方尤辱,還有一個(gè)地方在clean中)砂豌。
當(dāng)然,我們的makefile并不復(fù) 雜光督,所以在兩個(gè)地方加也不累阳距,但如果makefile變得復(fù)雜,那么我們就有可能會忘掉一個(gè)需要加入的地方结借, 而導(dǎo)致編譯失敗筐摘。所以,為了makefile的易維護(hù)船老,在makefile中我們可以使用變量咖熟。makefile的變量也 就是一個(gè)字符串,理解成C語言中的宏可能會更好柳畔。

比如馍管,我們聲明一個(gè)變量 obj,表示所有obj文件薪韩,在makefile的一開始就定義

obj = my.o
maincpp = test.cpp

于是确沸,我們就可以很方便地在我們的makefile中以 $(obj) 的方式來使用這個(gè)變量了,于是 我們的改良版makefile就變成下面這個(gè)樣子:

obj = my.o
maincpp = test.cpp

test :$(maincpp) $(obj)

my.o: my.cpp my.h

clean:
    rm $(obj)

于是如果有新的 .o 文件加入躬存,我們只需簡單地修改一下 obj 變量就可以了张惹。

關(guān)于變量更多的話題,我會在后續(xù)給你一一道來岭洲。

0x09 另類風(fēng)格的makefiles

既然我們的make可以自動推導(dǎo)命令宛逗,那么我看到那堆.o.h 的依賴就有點(diǎn)不爽,那么多的重復(fù)的 .h 盾剩,能不能把其收攏起來雷激,好吧,沒有問題告私,這個(gè)對于make來說很容易屎暇,誰叫它提供了自動 推導(dǎo)命令和文件的功能呢?來看看最新風(fēng)格的makefile吧驻粟。

obj = my.o
maincpp = test.cpp

test :$(maincpp) $(obj)

$(obj): my.h

clean:
    rm $(obj) test

這種風(fēng)格根悼,讓我們的makefile變得很簡單,但我們的文件依賴關(guān)系就顯得有點(diǎn)凌亂了蜀撑。魚和熊掌不可兼得挤巡。 還看你的喜好了。我是不喜歡這種風(fēng)格的酷麦,一是文件的依賴關(guān)系看不清楚矿卑,二是如果文件一多,要加入幾個(gè) 新的.o 文件沃饶,那就理不清楚了母廷。

0x10 清空目標(biāo)文件的規(guī)則

每個(gè)Makefile中都應(yīng)該寫一個(gè)清空目標(biāo)文件( .o 和執(zhí)行文件)的規(guī)則轻黑,這不僅便于重編譯,也很 利于保持文件的清潔琴昆。這是一個(gè)“修養(yǎng)”(呵呵氓鄙,還記得我的《編程修養(yǎng)》嗎)。一般的風(fēng)格都是:

clean:
  rm test $(obj)

更為穩(wěn)健的做法是:

.PHONY: clean
clean:
  rm  test $(obj)

0x11 Makefile的文件名

默認(rèn)的情況下椎咧,make命令會在當(dāng)前目錄下按順序找尋文件名為“GNUmakefile”玖详、 “makefile”、“Makefile”的文件勤讽,找到了解釋這個(gè)文件蟋座。在這三個(gè)文件名中,最好使用“Makefile” 這個(gè)文件名脚牍,因?yàn)橄蛲危@個(gè)文件名第一個(gè)字符為大寫,這樣有一種顯目的感覺诸狭。最好不要用“GNUmakefile”券膀, 這個(gè)文件是GNU的make識別的。有另外一些make只對全小寫的“makefile”文件名敏感驯遇,但是基本上來說芹彬, 大多數(shù)的make都支持“makefile”和“Makefile”這兩種默認(rèn)文件名。

當(dāng)然叉庐,你可以使用別的文件名來書寫Makefile舒帮,比如:“Make.Linux”,“Make.Solaris” 陡叠,“Make.AIX”等玩郊,如果要指定特定的Makefile,你可以使用make的-f--file參數(shù)

make -f  Makefile.Linux
make -f Makefile.mac

0x12 引用其他的Makefile

在Makefile使用include 關(guān)鍵字可以把別的Makefile包含進(jìn)來枉阵,這很像C語言的 #include 译红,被包含的文件會原模原樣的放在當(dāng)前文件的包含位置。 include 的語法是:

include <filename>

filename 可以是當(dāng)前操作系統(tǒng)Shell的文件模式(可以包含路徑和通配符)兴溜。

include 前面可以有一些空字符侦厚,但是絕不能是 Tab 鍵開始。 include<filename> 可以用一個(gè)或多個(gè)空格隔開拙徽。舉個(gè)例子刨沦,你有這樣幾個(gè)Makefilea.mkb.mk 斋攀、 c.mk 已卷,還有一個(gè)文件叫 foo.make 梧田,以及一個(gè)變量 $(bar) 淳蔼,其包含 了 e.mkf.mk 侧蘸,那么,下面的語句:

include foo.make *.mk $(bar)

等價(jià)于

include foo.make a.mk b.mk c.mk e.mk f.mk
  1. 如果make執(zhí)行時(shí)鹉梨,有 -I--include-dir 參數(shù)讳癌,那么make就會在這個(gè)參數(shù)所指定的目 錄下去尋找。

2.如果目錄 <prefix>/include (一般是: /usr/local/bin/usr/include )存在的話存皂,make也會去找晌坤。

如果有文件沒有找到的話,make會生成一條警告信息旦袋,但不會馬上出現(xiàn)致命錯(cuò)誤骤菠。它會繼續(xù)載入其它的 文件,一旦完成makefile的讀取疤孕,make會再重試這些沒有找到商乎,或是不能讀取的文件,如果還是 不行祭阀,make才會出現(xiàn)一條致命信息鹉戚。如果你想讓make不理那些無法讀取的文件,而繼續(xù)執(zhí)行专控,你可以 在include前加一個(gè)減號“-”抹凳。如:

-include <filename>

0x13 環(huán)境變量MAKEFILES

如果你的當(dāng)前環(huán)境中定義了環(huán)境變量 MAKEFILES ,那么伦腐,make會把這個(gè)變量中的值做一個(gè)類似于 include 的動作赢底。這個(gè)變量中的值是其它的Makefile,用空格分隔蔗牡。只是颖系,它和 include 不 同的是,從這個(gè)環(huán)境變量中引入的Makefile的“目標(biāo)”不會起作用辩越,如果環(huán)境變量中定義的文件發(fā)現(xiàn) 錯(cuò)誤嘁扼,make也會不理。

但是在這里我還是建議不要使用這個(gè)環(huán)境變量黔攒,因?yàn)橹灰@個(gè)變量一被定義趁啸,那么當(dāng)你使用make時(shí), 所有的Makefile都會受到它的影響督惰,這絕不是你想看到的不傅。在這里提這個(gè)事,只是為了告訴大家赏胚,也許 有時(shí)候你的Makefile出現(xiàn)了怪事访娶,那么你可以看看當(dāng)前環(huán)境中有沒有定義這個(gè)變量。

0x14 變量定義及賦值:

變量直接采用賦值的方法即可完成定義觉阅,如:

INCLUDE = ./include/

變量取值:

      用括號括起來再加個(gè)美元符崖疤,如:

      `FOO = $(OBJ)`

系統(tǒng)自帶變量:

通常都是大寫秘车,比如 CCPWD劫哼、CFLAG叮趴,等等。

有些有默認(rèn)值权烧,有些沒有眯亦。比如常見的幾個(gè):

CPPFLAGS : 預(yù)處理器需要的選項(xiàng) 如:-I

CFLAGS:編譯的時(shí)候使用的參數(shù) –Wall –g -c

LDFLAGS :鏈接庫使用的選項(xiàng) –L -l

變量的默認(rèn)值可以修改,比如 CC 默認(rèn)值是 cc般码,但可以修改為 gcc:CC=gcc

0x15 函數(shù)

Makefile 也為我們提供了大量的函數(shù)妻率,同樣經(jīng)常使用到的函數(shù)為以下兩個(gè)。需要注意的是板祝,Makefile 中所有的函數(shù)必須都有返回值舌涨。在以下的例子中,假如目錄下有 main.c扔字、func1.c囊嘉、func2.c 三個(gè)文件。

通配符:

用于查找指定目錄下指定類型的文件革为,跟的參數(shù)就是目錄+文件類型扭粱,比如:

src = $(wildcard ./src/*.c)

這句話表示:找到 ./src 目錄下所有后綴為 .c 的文件,并賦給變量 src震檩。

命令執(zhí)行完成后琢蛤,src 的值為:main.c func1.c fun2.c。

patsubst:

匹配替換抛虏,例如以下例子博其,用于從 src 目錄中找到所有 .c 結(jié)尾的文件,并將其替換為 .o 文件迂猴,并賦值給 obj慕淡。

obj = $(patsubst %.c ,%.o ,$(src))

命令執(zhí)行完成后,obj 的值為 main.o func1.o func2.o沸毁。
特別地峰髓,如果要把所有 .o 文件放在 obj 目錄下,可用以下方法:

obj = $(patsubst ./src/%.c, ./obj/%.o, $(src))

更多可以參考https://seisman.github.io/how-to-write-makefile/overview.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末息尺,一起剝皮案震驚了整個(gè)濱河市携兵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搂誉,老刑警劉巖徐紧,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡并级,警方通過查閱死者的電腦和手機(jī)巴柿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來死遭,“玉大人,你說我怎么就攤上這事凯旋⊙教叮” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵玉转,是天一觀的道長琼讽。 經(jīng)常有香客問我魂毁,道長,這世上最難降的妖魔是什么谐鼎? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮趣惠,結(jié)果婚禮上狸棍,老公的妹妹穿的比我還像新娘。我一直安慰自己味悄,他們只是感情好草戈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著侍瑟,像睡著了一般唐片。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涨颜,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天费韭,我揣著相機(jī)與錄音,去河邊找鬼庭瑰。 笑死星持,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弹灭。 我是一名探鬼主播钉汗,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鲤屡!你這毒婦竟也來了损痰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤酒来,失蹤者是張志新(化名)和其女友劉穎卢未,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辽社,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年伟墙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滴铅。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡戳葵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出汉匙,到底是詐尸還是另有隱情拱烁,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布噩翠,位于F島的核電站戏自,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏伤锚。R本人自食惡果不足惜擅笔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望屯援。 院中可真熱鬧猛们,春花似錦、人聲如沸狞洋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽徘铝。三九已至耳胎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惕它,已是汗流浹背怕午。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淹魄,地道東北人郁惜。 一個(gè)月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像甲锡,于是被迫代替她去往敵國和親兆蕉。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 來自陳浩的一片老文缤沦,但絕對營養(yǎng)虎韵。 示例工程:3 個(gè)頭文件*.h,和 8 個(gè) C 文件*.c缸废。 初 編譯過程包蓝,源文件...
    周筱魯閱讀 4,696評論 0 17
  • makefile 介紹 make命令執(zhí)行時(shí)驶社,需要一個(gè) makefile 文件,以告訴make命令如何去編譯和鏈接程...
    Stan_Z閱讀 1,624評論 2 15
  • makefile關(guān)系到整個(gè)工程的編譯規(guī)則测萎,一個(gè)工程中的源文件不計(jì)其數(shù)亡电,按其類型、功能硅瞧、模塊分別放在若干的目錄當(dāng)中份乒,...
    Joe_HUST閱讀 1,880評論 0 3
  • 1.Makefile規(guī)范 target 這 一 個(gè) 或 多 個(gè) 的 目 標(biāo) 文 件 依 賴 于prerequisi...
    G風(fēng)閱讀 1,891評論 0 3
  • 早上我洗完臉?biāo)⑼暄牢胰プx了一會書寫了一會字之后我休息了一會就到了吃午飯的的候了。 吃完午飯我又讀了一會書讀呀讀腕唧,讀...
    趙研博閱讀 115評論 0 1