make 和 makefile 的關系


程序的 編譯 和 鏈接

要先總結 make 和 makefile,就需要先了解下面這個過程:

  1. 預編譯:也叫預處理,進行一些文本替換工作,比如將 #define 定義的內容,在代碼中進行替換;
  2. 編譯:將預處理得到的代碼,進行詞法分析、語法分析、中間代碼……;如果是在Windows下,中間代碼就是 .obj 文件;在Linux系統下,中間代碼就是 .o 文件;
  3. 匯編:將編譯得到的匯編代碼,通過匯編程序得到 0 和 1 機器語言;
  4. 鏈接:鏈接各種靜態鏈接庫和動態鏈接庫得到可執行文件。

 

make 和 makefile 能干啥?

一個工程,那么多源文件,一堆的 cpp 和 h 文件,怎么編譯啊?編譯一個大型工程,如果Rebuild可能就需要好幾個小時,甚至十幾個小時,那我們就可能要問了。

  1. 如何像VS那樣,一鍵就能編譯整個項目?
  2. 如何修改了哪個文件,就編譯修改的那個文件,而不是重新編譯整個工程?

好吧,make 和 makefile 就能搞定這些。makefile 定義了一系列的規則來指定,哪些文件需要先編譯,哪些文件需要后編譯,哪些文件需要重新編譯,甚至於進行更復雜的功能操作,因為 makefile 就像一個Shell腳本一樣,其中也可以執行操作系統的命令。makefile 帶來的好處就是——“自動化編譯”,一旦寫好,只需要一個 make 命令,整個工程完全自動編譯,極大的提高了軟件開發的效率。

make 是一個命令工具,是一個解釋 makefile 中指令的命令工具,一般來說,大多數的IDE都有這個命令,比如:Delphi的make,Visual C++的nmake,Linux 下 GNU 的 make。可見,makefile 都成為了一種在工程方面的編譯方法。make 命令執行時,需要一個 makefile 文件,以告訴 make 命令需要怎么樣的去編譯和鏈接程序。

現在,應該明白了吧。make 是一個命令,用來解析 makefile 文件;makefile 是一個文件,用來告訴 make 命令,如何編譯整個工程,生成可執行文件。再打個比方:

導演 == make

劇本 == makefile

演員 == MAKE調用的外部命令,如編譯器、鏈接器等

電影 == 生成的程序

 

解決問題舉例

怎么就出現了make這個東西了呢?還記得你入門C語言時,寫下的Hello World程序么?

#include <stdio.h>
int main()
{
    printf("Hello World\n");
    return 0;
}

當你在終端中輸入 gcc HelloWorld.c 命令時,就會生成一個 a.out 文件(如果不用 -o 參數指定輸出文件名的話,默認為 a.out),然后就可以神奇的使用 ./a.out 執行該文件,打印出了 Hello World。這是一件讓初學者興奮的事情。問題來了,現在就僅僅是一個 HelloWorld.c 文件,如果有多個代碼文件,而多個代碼文件之間又存在引用關系,這個時候,該如何去編譯生成一個可執行文件呢?比如現在有一下源文件:

add.h
add.c
sub.h
sub.c
mul.h
mul.c
divi.h
divi.c
main.c

這些代碼文件的定義分別如下:

add.h 文件

#ifndef _ADD_H_
#define _ADD_H_
int add(int a, int b);
#endif

add.c 文件

#include "add.h"
int add(int a, int b)
{
    return a + b;
}

sub.h 文件

#ifndef _SUB_H_
#define _SUB_H_
int sub(int a, int b);
#endif

sub.c 文件

#include "sub.h"
int sub(int a, int b
{
    return a - b;
}

mul.h 文件

#ifndef _MUL_H_
#define _MUL_H_
int mul(int a, int b);
#endif

mul.c 文件

#include "mul.h"
int mul(int a, int b)
{
    return a * b;
}

divi.h 文件

#ifndef _DIVI_H_
#define _DIVI_H_
int divi(int a, int b);
#endif

divi.c 文件

#include "divi.h"
int divi(int a, int b)
{
    if (b == 0)
    {
        return 0;
    }
    return a / b;
}

main.c 文件

#include <stdio.h>
#include "add.h"
#include "sub.h"
#include "mul.h"
#include "divi.h"

int main()
{
    int a = 10;
    int b = 2;

    printf("%d + %d = %d\n", a, b, add(a, b));
    printf("%d - %d = %d\n", a, b, sub(a, b));
    printf("%d * %d = %d\n", a, b, mul(a, b));
    printf("%d / %d = %d\n", a, b, divi(a, b));
    return 0;
}

你也看到了,在 main.c 中要引用這些文件,那現在如何編譯,生成一個可執行文件呢?

 

最笨的解決方法

最笨的解決方法就是依次編譯所有文件,生成對應的 .o 目標文件。參考如下:

$ gcc -c sub.c -o sub.o
$ gcc -c add.c -o add.o
$ gcc -c sub.c -o sub.o
$ gcc -c mul.c -o mul.o
$ gcc -c divi.c -o divi.o
$ gcc -c main.c -o main.o

然后再使用如下命令對所生成的單個目標文件進行鏈接,生成可執行文件。

$ gcc -o main add.o sub.o mul.o divi.o main.o

然后就可以得到一個可執行程序 main,可以直接使用 ./main 進行運行。還不錯,雖然過程艱辛,至少也可以得到可執行程序。那么有沒有比這更簡單的方法呢?如果一個項目,幾千個文件,這么寫下去,還不得累死人啊。辦法是有的,我接着總結。

 

使用makefile文件

使用上面那種最笨的辦法,效率是非常低得,當添加新的文件,或者修改現有文件時,維護起來也是非常難得。基於此,現在就來說說使用 makefile 文件來搞定這一切。

關於什么是 makefile,在文章的開頭我就已經總結了,至於它和 make 的關系,在文章的開頭也說的非常清楚了,現在就來看看如何使用 makefile 來完成上面同樣的任務,生成一個 main 的可執行文件。

#target:dependency-file
main:main.o add.o sub.o mul.o divi.o
    gcc -o main main.o add.o sub.o mul.o divi.o
main.o:main.c add.h sub.h mul.h divi.h
    gcc -c main.c -o main.o
add.o:add.c add.h
    gcc -c add.c -o add.o
sub.o:sub.c sub.h
    gcc -c sub.c -o sub.o
mul.o:mul.c mul.h
    gcc -c mul.c -o mul.o
divi.o:divi.c divi.h
    gcc -c divi.c -o divi.o
clean:
    rm -f *.o

上面就是 makefile 文件的內容,對於 makefile 的內容的編寫規則,這里先不說。

現在你可以在 makefile 的同目錄下執行 make 命令,然后就可以看到生成了一堆 .o 目標文件,還有那個可執行的 main 文件;接着運行 make clean,那些 .o 文件就全部被刪除了。為什么是這樣?好了,你先照着做一遍吧。

 

makefile文件編寫規則

上面只是給出了一個簡單的 makefile 文件,你肯定好奇這個 makefile 的書寫規則是什么樣子的?

makefile 的規則大體上就是以下格式:

target 是一個目標文件,可以是 Object File(.o文件),也可以使最終的執行文件,而 dependency-file 是生成對應 target 所需要依賴的文件或者其它的 target,command 就是最終由 make 執行的命令。

上面說了一段話,簡短而言就是:生成一個 target,需要依賴的文件,而使用命令來將依賴文件生成對應的 target 的規則,是在 command 中定義的。如果 dependency-file 中有一個或者多個文件比 target 文件要新的話,command 所定義的命令就會被執行,這就是 makefile 的規則,也就是 makefile 最核心的內容。

makefile 文件中可以定義變量,可以使用函數,還有各種判斷,內容繁多,這里就不一一總結了,更詳細的介紹,可以看看大牛陳皓的系列博客《跟我一起寫makefile》。

 

 

參考:

http://www.jellythink.com/archives/810?utm_source=tuicool&utm_medium=referral

http://bbs.csdn.net/topics/390143962

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM