C編譯: makefile基礎
在編譯一個大型專案的時候,往往有很多目標檔案、庫檔案、標頭檔案以及最終的可執行檔案。不同的檔案之間存在 依賴關係(dependency)。比如當我們使用下面命令編譯時:
$gcc -c -o test.o test.c
$gcc -o helloworld test.o
可執行檔案helloworld依賴於test.o進行編譯的,而test.o依賴於test.c。
依賴關係
在我們編譯一個大型專案時,我們往往要很多次的呼叫編譯器,來根據依賴關係,逐步編譯整個專案。這樣的方式是自下而上的,即先編譯下游檔案,再編譯上游檔案。
UNIX系統下的make工具用於自動記錄和處理檔案之間的依賴關係。我們不用輸入大量的"gcc"命令,而只需呼叫 make就可以完成整個編譯過程。所有的依賴關係都記錄在makefile文字檔案中。我們只需要make helloworld,make會根據依賴關係,自上而下的找到編譯該檔案所需的所有依賴關係,最後再自下而上的編譯。
(make有多個版本,本文將基於GNU make。make會自動搜尋當前目錄下的makefile, Makefile或者GNUmakefile)
依賴
基本概念
我們使用一個示例C語言檔案:
#include <stdio.h>/* * By Vamei * test.c for makefile demo */int main() { printf("Hello world!\n"); return 0; }
下面是一個簡單的makefile
# helloworld is a binary filehelloworld: test.o
echo "good"
gcc -o helloworld test.o
test.o: test.c
gcc -c -o test.o test.c
觀察上面的makefile
- #號起始的行是 註釋行
- target: prerequisite為依賴關係,即 目標檔案(target)依賴於 前提檔案(prerequisite)。可以有多個前提檔案,用空格分開。
- 依賴關係後面的 <Tab>縮排行是實現依賴關係進行的 操作,即正常的UNIX命令。一個依賴關係可以附屬有多個操作。
用直白的話說,就是:
- 想要helloworld嗎?那你必須有test.o,並執行附屬的操作。
- 如果沒有test.o,那你必須搜尋其他依賴關係,並建立test.o。
我們執行
$make helloworld
來建立helloworld。
make是一個 遞迴建立的過程:
- Base Case 1: 如果當前依賴關係中沒有說明前提檔案,那麼直接執行操作。
- Base Case 2: 如果當前依賴關係說明了目標檔案,而目標檔案所需的前提檔案已經存在,而且前提檔案與上次make時沒有發生改變(根據最近寫入時間判斷),也直接執行該依賴關係的操作。
- 如果當前目標檔案依賴關係所需的前提檔案不存在,或者前提檔案發生改變,那麼以前提檔案為新的目標檔案,尋找依賴關係,建立目標檔案。
虛線: 依賴關係檢索
上面是make的核心功能。有了上面的功能,我們可以記錄專案中所有的依賴關係和相關操作,並使用make進行編譯。下面的內容都是在此核心內容上的擴充。
宏
make中可以使用宏(MACRO)。宏類似於文字型別的變數。比如下面的CC:
CC = gcc
# helloworld is a binary filehelloworld: test.o
echo "good"
$(CC) -o helloworld test.o
test.o: test.c
$(CC) -c -o test.o test.c
我們用CC來代表"gcc"。在makefile中,使用$(CC)的方式來呼叫宏的值。make會在執行時,使用宏的值(gcc)來替代$(CC)。
shell的環境變數可以直接作為宏呼叫。如果同一個自定義的宏同時也有同名環境環境變數,make將優先使用自定義宏。
(可以使用$make -e helloworld來優先使用環境變數)
類似於C語言的宏,makefile中的宏可以方便的管理一些固定出現的文字,並方便替換操作。比如我們未來使用ifort編譯器時,只需要更改宏定義為:
CC = ifort
就可以了
內部宏
make中有內部定義的宏,可以直接使用。 $@中包含有當前依賴關係的目標檔名,而 $^包含當前目標的前提檔案:
CC = gcc # helloworld is a binary file helloworld: test.o echo $@ $(CC) -o $@ $^ test.o: test.c $(CC) -c -o $@ $^
內部宏 功能
$* 當前依賴關係中的目標檔名,不包括字尾。
$* 當前依賴關係中,發生改變的前提檔案
$$ 字元"$"
如果目標或者前提檔案是一個完整路徑,我們可以附加 D和 F來提取資料夾部分和檔名部分,比如 $(@F)表示目標檔案的檔名部分。
字尾依賴
在makefile中使用
.SUFFIXES: .c .o
來說明.c和.o是字尾。
我們可以使用字尾依賴的方式,比如:
CC = gcc .SUFFIXES: .c .o .c.o: $(CC) -c -o $@ $^ #-------------------------- # helloworld is a binary file helloworld: test.o echo $@ $(CC) -o $@ $^ test.o: test.c
我們定義 .c和 .o為字尾。並有字尾依賴關係 .c.o:。前者為前提,後者為目標。(注意,與一般的依賴關係順序不同)
上面的test.o和test.c有依賴關係,但沒有操作。make會發現該依賴關係符合.c.o的字尾依賴,並執行該字尾依賴後面的操作。
如果專案很大型的時候,字尾依賴非常有用。符合字尾依賴的檔案往往有類似的操作,我們可以將這些操作用字尾依賴表示,而避免重複輸入。
其他
makefile的續行符為 \
makefile中經常會定義下面依賴關係:
all:
如果make後沒有跟隨檔名,那麼將執行該依賴關係。
clean:
常用於清理歷史檔案。
比如:
CC = gcc .SUFFIXES: .c .o .c.o: $(CC) -c -o $@ $^ #-------------------------- all: helloworld @echo "ALL" # helloworld is a binary file helloworld: test.o @echo $@ $(CC) -o $@ $^ test.o: test.c clean: -rm helloworld *.o
注意: echo前面的 @和rm前面的 -。@後的命令將不顯示命令本身。-後面的命令將忽略錯誤(比如刪除不存在的檔案)。
總結
make的核心功能是根據依賴關係來實現編譯管理。
make的其他功能是讓使用者可以更加便捷的寫出makefile。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31543790/viewspace-2663155/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- C 編譯: makefile 基礎編譯
- 使用makefile編譯c程式編譯C程式
- 使用makefile編譯編譯
- C語言的本質(36)——makefile基礎C語言
- C#反編譯工具:.NET Reflector基礎使用C#編譯
- 【Makefile】5-Makefile變數的基礎變數
- Android Makefile 編譯過程分析Android編譯
- makefile教程---nmake命令編譯器的使用編譯
- 編譯器有關的Makefile語法編譯
- 如何在Windows下使用make編譯MakefileWindows編譯
- Linux技術——makefile基礎Linux
- css預編譯--sass基礎篇CSS編譯
- 在Ubuntu環境下用gcc命令執行c程式以及Makefile編譯UbuntuGCC程式編譯
- makefile基礎和工作常用點補充
- 重走LFS之路:(九) 基礎系統編譯-2編譯
- 重走LFS之路:(八) 基礎系統編譯-1編譯
- C# 基礎知識:字元編碼、編碼轉換C#字元
- android編譯系統makefile(Android.mk)寫法Android編譯
- Linux驅動開發筆記(三):基於ubuntu的驅動、makefile編寫以及編譯載入流程Linux筆記Ubuntu編譯
- VS編譯器基礎 int main(int argc,char* argv[])編譯AIGC
- 重走LFS之路:(七) 基礎系統編譯準備編譯
- C/C++ 通用 Makefile(轉)C++
- PLSQL Language Reference-PL/SQL語言基礎-條件編譯-條件編譯指令限制SQL編譯
- PLSQL Language Reference-PL/SQL語言基礎-條件編譯-條件編譯舉例SQL編譯
- 彙編基礎
- iOS逆向之旅(基礎篇) — 彙編(一)— 彙編基礎iOS
- linux下靜態庫、動態庫編譯及makefile書寫Linux編譯
- vim編譯執行c編譯
- C語言編譯工具C語言編譯
- windows 下c++編譯WindowsC++編譯
- c#程式反編譯C#編譯
- C++ 編譯過程C++編譯
- 使用C編譯器編寫shellcode編譯
- 大話css預編譯處理(三):基礎語法篇CSS編譯
- Go編譯原理系列2(詞法分析&語法分析基礎)Go編譯原理詞法分析語法分析
- 一文了解如何原始碼編譯Rainbond基礎元件原始碼編譯AI元件
- JAVA基礎之八-方法變數作用域和編譯器Java變數編譯
- c 基礎三