make
計算機程序
make,常指一條計算機指令,是在安裝有GNU Make的計算機上的可執行指令。該指令是讀入一個名為makefile的文件,然後執行這個文件中指定的指令。
有時make又指GNU Make,GNU Make是一個用來控制可執行文件和其他一些從源文件來的非源代碼文件版本的軟體。
GNU Make是一個用來控制可執行文件和其他一些從源文件來的非源代碼文件版本的軟體。
Make可以從一個名為makefile的文件中獲得如何構建你所寫程序的依賴關係,Makefile中列出了每個目標文件以及如何由其他文件來生成它。當你編寫一個程序時,你可以為它編寫一個makefile文件,這樣你就可以使用Make來編譯和安裝這個程序。
Make使最終用戶可以在不知道構建細節的情況下構建和安裝你的軟體,因為這些細節都記錄在了你提供的Makefile中。
基於源文件的改動,Make可以自動知道那些文件需要更新;它也會自動決定文件更新的適當順序,以避免要更新的文件依賴於另一個同樣需要更新的文件。因此,在你修改了程序的源代碼並且執行Make后,你不必重新完全編譯你的所有文件,只需要重新編譯那些直接或間接受到影響的文件就好了。
Make不限於任何一種特定的語言。對於程序中任何一種非源文件,makefile文件中可以指定shell指令去生成它。shell指令可以執行編譯器生成目標文件,執行鏈接器生成可執行文件,執行ar更新庫文件,執行TeX(一個文本排版軟體)或Makeinfo去格式化文檔。
Make不限於用來生成軟體包。你可以用make來控制安裝和卸載軟體包,或者用來生成標籤表,以及其他的任何你想要做的,當然前提是你寫好怎麼做(╬▔皿▔)凸。
Makefile告訴Make怎樣執行一系列的指令去依靠源文件生成一個目標文件。Makefile中聲明了一個依賴關係的列表,這個列表應當包含所有文件(無論是源文件或者目標)作為輸入。
以下是個簡單的規則示例:
當你執行make的時候你可以指定特定的目標去更新,否則Make會更新Makefile目標列表中第一個目標。當然作為這個目標輸入的其他目標會先更新。make用makefile確定那些目標文件應該被更新,然後那些目標文件實際上需要更新。如果一個目標文件比他的所有依賴都新,這樣他就是最新的了,不需要再去重新生成了。其他文件確實需要更新,要按照正確的順序,在用來生成其他文件之前先更新自己。
GNU制訂了一些如何編寫Makefiles的約定,所用的GNU軟體包必須遵守。即使你不打算成為GNU軟體,在你的程序中遵循這些約定也是一個好主意,這樣用戶就可以像其他許多軟體包一樣構建你的軟體包,而且不需要學習任何特殊的東西。
這些約定可以在章節``Makefile conventions''(147k characters)和章節GNU Coding Standards(147k characters)中看到。
Android.mk和Application.mk是基於makefile裝了一層封裝,編寫Android.mk和Application.mk之後可以通過ndk-build執行so的編譯,Android.mk中新增了一些對固定文件固定的環境變數的的讀取
如APP_ABI:=armeabi-v7a定義在Application.mk中然後執行ndk-build將只編譯armeabi-v7a的so在當前目錄的上級目錄。
具體參考
當你寫一個簡單的程序,只有一到兩個源文件的時候,輸入
%cc file1.c file2.c
就沒什麼問題,但如果有很多源文件就會很煩人──編譯的時間也會很長。
一個方法就是使用目標文件,只在源文件有改變的情況下才重新編譯源文件。因此你可以這樣做:
%cc file1.o file2.o ... file37.c...
上次編譯后,file37.c發生了改變,但其他文件沒有。這樣做可以讓編譯過程快很多,但是也不能解決累人的輸入問題。
或者我們可以使用一個shell script來解決輸入問題,但是也需要重新編譯所有文件,在大型項目上很沒有效率。
如果有成百上千的源文件的話怎麼辦?如果我們在與很多人合作寫程序,別人對源文件進行了修改,又沒有告訴你,該怎麼辦?
也許我們可以把以上兩種方法結合,寫一種像shellscript一樣的東西。這種文件包含某種技巧可以決定什麼時候該對源文件進行編譯。如今所有我們要的就是一個程序可以懂得這種技巧,因為要懂得這種技巧,shell還沒那麼大的能耐。
這個程序就叫make。它讀入一個文件,叫makefile,這個文件決定了源文件之間的依賴關係。而且決定了源文件什麼時候該編譯什麼時候不應該編譯。例如,某個規則可以說“如果fromboz.o比fromboz.c要舊,意思就是有人修改了fromboz.c,因此我們需要重新編譯這個文件。”這個makefile還有規則通知make該怎麼重新編譯源文件,因此make是一個強大得多的工具。
makefile通常和相關的源文件保存在同一個目錄下,可以叫做makefile,Makefile或者MAKEFILE。大多數程序員會使用 Makefile這個名字,因為這樣可以讓這個文件被放在目錄列表的頂端,可以很容易得看見。
這是一個非常簡單的make文件:
foo:foo.c
cc -o foo foo.c
包含兩行,一行是依賴關係,一行是執行動作。
依賴關係的那一行包含了程序的名字(叫做target),緊跟著一個冒號,然後是空格,最後是源文件的名字。當make讀入這一行的時候,會檢查foo是否存在;如果存在,就比較foo和foo.c最後的修改時間有什麼不同。如果foo不存在,或者比foo.c要舊,就檢查執行動作那一行看看該怎麼做。換句話說,就是foo.c需要重新編譯的時候該怎麼辦。
執行動作那一行以一個tab(按下tab)開始,然後是你在命令行下產生foo所執行的命令。如果foo過期了,或者不存在,make就會執行這個命令來產生foo。換句話說,這就是重新編譯foo.c的規則。
因此,當你輸入make時,它會確定foo和foo.c在修改時間上是否同步。這個原則可以在Makefile里擴展到成百上千的目標文件上──實際上,在FreeBSD里,你只要在合適的目錄下輸入make world就可以編譯整個操作系統!
makefile另一個有用的特點就是目標文件不一定就是程序。例如,我們可以有這樣的make文件。
foo:foo.c
cc -o foo foo.c
install:
cp foo /home/me
我們可以輸入如下的命令告訴 make 該執行哪個目標:
%make target
make會只執行這個目標而忽略其他的目標。例如,如果我們輸入 make foo,就只有 foo 被執行,必要的情況下重新編譯foo而不會繼續執行 install這個目標。
如果我們只是輸入make這個命令,make總會尋找第一個目標,並且在執行完以後就不管其他的目標了。例如,如果我們輸入make foo,make就會轉到foo這個目標,在必要的情況下重新編譯foo,而不會執行 install 目標,然後就停止了。
一定要注意,install這個目標不依賴任何其他的東西!這意味著我們一旦輸入make install,這個目標下的所有命令都將被執行。這種情況下,foo將被安裝到用戶的家目錄下。應用程序的makefile正是這樣寫的,以便程序在正確編譯后可以被安裝到正確的目錄。
要嘗試解釋的話會比較容易讓人混淆。如果你不太懂make是如何工作的,最好的辦法就是先寫一個簡單的程序例如“hello world”以及和上面的例子相同的make文件再去實驗。然後再進一步,使用多個源文件,或者讓你的源文件包含一個頭文件。touch命令在這裡就非常有用了──它能讓在不改變文件內容的情況下改變文件的日期。
想進一步學習如何編寫makefile請參考引用4.