有關makefile

先複習下​​​​​​c語言編譯程序的過程:

轉自https://www.mianfeiwendang.com/doc/15144a2ebbfbff782e76fc8d/2

1. C源程序
2. 預編譯處理(.c)主要包括四個過程

  • 宏定義指令,如#define N 6,#undef等。對於前一個僞指令,預編譯所要做的是將程序中的所有N用6替換,請大家注意這裏是替換,並不是像作爲函數參數那樣將6複製進N這個變量。對於後者,則將取消對某個宏的定義,使以後出現的N不再被替換。
  • 條件編譯指令,如#ifdef,#ifndef,#endif等。 這些僞指令的引入使得程序員可以通過定義不同的宏來決定編譯程序對哪些代碼進行處理。預編譯程序將根據有關的文件,將那些不必要的代碼過濾掉。這樣就能在編譯階段減少編譯時間,提高效率。
  •  頭文件包含指令,如#include "file.h"或#include 等。

    在頭文件中一般用僞指令#define定義了大量的宏(最常見的是字符常量);

    同時採用這樣的做法可以讓我們直接調用一些複雜庫函數;

    還可以免去我們在寫包含有各種外部符號的聲明。 程序時重複做一些定義聲明工作的麻煩。試想一下,一旦我們寫好頭文件,那麼以後要用到相關模塊就再也不用寫這些函數了,直接#include 就OK了。

    #include<>與#include"  "的區別。

    #include<>:這條指令就是告訴編譯器去系統默認的路徑尋找相關文件。

    #include"  " :這條是告訴編譯器先去源程序所在目錄下尋找,如果沒有就去系統默認路徑尋找。

  • 特殊符號,預編譯程序可以識別一些特殊的符號。 例如在源程序中出現的LINE標識將被解釋爲當前行號(十進制數),FILE則被解釋爲當前被編譯的C源程序的名稱。預編譯程序就是對在源程序中出現的這些特殊符號將用合適的值進行替換。

        預編譯階段基本上是完成對源程序的相關代碼進行替換,這樣之後程序的原意沒有改變,就是代碼的內容有所不同,這樣爲以後的編譯做好準備!

3. 編譯、優化程序(.s、.asm)
        經過預編譯處理,現在我們的程序已經沒有宏定義,包含頭文件等指令了,只剩下一些變量,常量,關鍵字等,而編譯的主要作用是檢查這些代碼的語法錯誤及將這些代碼編譯成爲彙編文件。

        我們有.c文件和.h文件,編譯的時候編譯的是.c文件(每一個.c文件對應一個.s文件),是和.h文件沒有關係的,.h文件是預處理的時候展開加進.c文件裏的。

4. 彙編程序(.obj、.o、.a、.ko)
      在這個階段是將彙編代碼翻譯成目標文件,這時的文件已經是二進制代碼了。在windows環境下文件的後綴名是.obj;而在unix下則有是o、.a、.ko等文件。目標文件由段組成。通常一個目標文件中至少有兩個段:前面文章中說的代碼段(text)和數據段(data);

       代碼段:該段中所包含的主要是程序的指令。該段一般是可讀和可執行的,但一般卻不可寫。
       數據段:主要存放程序中要用到的各種全局變量或靜態的數據。一般數據段都是可讀,可寫,可執行的

5. 鏈接程序(.exe、.elf、.axf)
       也許有人會有疑問,上面的目標代碼已經是機器碼了,也就是說CPU可以識別這些文件了,那爲什麼我們還要鏈接程序呢?大家想想我們是不是忘了點什麼。。。對!那些被包含的頭文件,以及當我們的程序分佈於很多源文件時,那麼這些源文件該怎麼處理呢,這就是連接器的作用,它們被翻譯成目標代碼後需要被鏈接到一起才能被執行。
        談到函數庫的鏈接,我們還需要了解點函數庫的知識,函數庫分靜態鏈接庫(又稱靜態庫*.lib)和鏈接動態庫(又稱動態庫*.dll)。靜態庫的鏈接在編譯時會被編譯進彙編文件,這樣的操作會改變文件大小;而動態庫則是在執行時(雙擊運行),當需要動態庫中的文件時才被鏈接到可執行文件的。                                                                                                                                        編譯是一次只編譯一個文件,鏈接是一次性將所有的.o,.a將交叉引用都解決掉,除了用ld鏈接,也可以用gcc將兩個。o鏈接成可執行文件;

makefile

        小程序的話單個c文件就可以編成可執行文件;                                                                                                                                  代碼越來越多的時候,可能需要多個人去修改維護,所以把一個工程按照不同的功能分成許多個.c文件,放在若干目錄裏,那麼編譯的時間就會越來越長,因爲就算只改了一個文件也會都從頭編譯一遍,這種情況下產生了makefile文件,makefile定義了一系列的規則,來指定哪些文件需要先編譯,哪些文件後編譯,哪些文件重新編譯。makefile最大的好處就是自動化編譯。一旦寫好,只需要一個make命令,整個過程都自動編譯。極大提高開發的效率。

makefile就像一個工程管理腳本,一個makefile文件包括兩塊內容:工程結構(文件、依賴關係)、指令(如何編譯、創建)

也就是說makefile的內容包括:要對什麼文件進行操作,如何操作;我們的規則是:
    1)如果這個工程沒有編譯過,那麼我們的所有C文件都要編譯並被鏈接。
    2)如果這個工程的某幾個C文件被修改,那麼我們只編譯被修改的C文件,並鏈接目標程序。
    3)如果這個工程的頭文件被改變了,那麼我們需要編譯引用了這幾個頭文件的C文件,並鏈接目標程序。

make命令用於解析makefile,並不侷限於.c文件編譯,

makefile並像程序一樣從上到下一行行的執行的

 Makefile 主要的 5個部分 (顯示規則, 隱晦規則, 變量定義, 文件指示, 註釋)

  1. 顯式規則 :: 說明如何生成一個或多個目標文件(包括 生成的文件, 文件的依賴文件, 生成的命令),由makefile的書寫者明顯指出
  2. 隱晦規則 :: make的自動推導功能所執行的規則
  3. 變量定義 :: Makefile中定義的變量
  4. 文件指示 :: Makefile中引用其他Makefile; 指定Makefile中有效部分; 定義一個多行命令
  5. 註釋     :: Makefile只有行註釋 "#", 如果要使用或者輸出"#"字符, 需要進行轉義, "\#"

1.顯式規則 :

target ... : prerequisites ...
    command                   //rule,規則前面必須是Tab鍵,
    ...
  • target              - 目標文件, 可以是 Object File, 也可以是可執行文件目標,prerequisites - 生成 target 所需要的文件或者目標這一行是依賴關係
  • command       - make需要執行的命令 (任意的shell命令), Makefile中的命令必須以 [tab] 開頭,這樣make纔會把這一行看成命令,這一行說的是規則

make命令從上往下讀取makefile的時候,它碰見的第一個目標就是最終目標,先檢查看all有沒有,沒有的話就看它的依賴,也就是top,這個時候再查看top的依賴,這樣依次進行,生成了一棵樹,

  • 當修改了某一個.c或者.h之後運行make指令,
  • make就去檢查依賴,一檢查依賴都在,
  • make就去檢查各個依賴的時間戳,正常情況下target的生成時間戳應該比它的依賴文件的生成時間戳要晚,但是改了.c/.h之後,這些.c/.h的時間戳就比它們對應的.target .o文件晚了,這個時候,make就會只編譯不符合這個規則的依賴;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章