1. 程式人生 > >簡介make命令和makefile文件

簡介make命令和makefile文件

tab linux 後綴 依賴關系 函數調用 創建方式 href oca printf

一、為什麽要用到 make 命令和 makefile 文件

  在 Linux 下編寫一個程序,每次編譯都需要在命令行一行一行的敲命令。如果是一個很小的程序還好說,命令不怎的復雜,編譯速度也挺快,但是對於大型程序來說,這樣無疑很麻煩,且不說可能會敲錯命令,有時候僅僅改動了一個小地方,卻需要將整個程序全部重新編譯一遍,顯然很浪費時間。Linux 提供了 make 命令來解決上述問題,它會在必要時重新編譯所有受改動影響的源文件。同時,還提供了一個 makefile 文件,它告訴 make 命令如何構建應用程序。這裏用一個簡單的例子提前演示一下:

/* hello.c */
#include <unistd.h> #include
<stdio.h> #include <stdlib.h> int main() { printf("hello world!\n"); exit(0); }
/* Makefile */
hello: hello.c gcc
-o hello.s -S hello.c gcc -o hello.o -c hello.s gcc -o hello hello.o clean: -rm hello hello.s hello.o

  這裏提供了兩段代碼,第一段代碼是一個簡單的 HelloWorld 程序,第二段代碼是為這個程序編寫的一個 makefile 文件。此時,只需要在命令行輸入 make 命令,就可以對源文件 hello.c 進行編譯,如下:

技術分享圖片

  執行 make 命令時,make 命令會讀取 makefile 文件,並按照 makefile 文件中給出的命令來創建文件,同時會在執行時將命令打印到標準輸出。執行完 make 命令後,源文件所在目錄下多了三個文件:hello、hello.o 和 hello.s,其中 hello 是可執行文件,使用命令 ./hello 即可查看程序的輸出結果。這和直接在命令行使用 gcc 命令所得到的結果是一樣的,而且,當你修改了源文件時,也只需要再次使用 make 命令即可重新編譯,十分方便。

二、make 命令

  make 命令用於從一個名為 makefile 的文件中獲得構建一個程序的依賴關系。make 命令會根據 makefile 文件來確定目標文件的創建順序以及正確的規則調用順序。

make 命令的一些常用參數

1)-k 參數:

  使用 -k 參數可以讓 make 命令在發現錯誤時仍然繼續執行,而不是在檢測到第一個錯誤時就停下來。利用這個選項可以在一次操作中發現所有未編譯成功的源文件;

2)-n 參數:

   使用 -n 參數,讓 make 命令輸出將要執行的操作步驟,而不是真正執行這些操作;

3)-f 參數:

  使用 -f 參數,後面可以接一個文件名,用於指定一個文件作為 makefile 文件。如果沒有使用 -f 選項,則 make 命令會在當前目錄下查找名為 makefile 的文件,如果該文件不存在,則查找名為 Makefile 的文件。

三、makefile 文件

  makefile 文件由一組依賴關系規則構成。每個依賴關系都由一個目標(即將要創建的文件)和一個該目標所依賴的源文件組成;規則描述了如何通過這些依賴文件創建目標。簡單的來說,makefile 文件的寫法如下:

target: prerequisites
    command1
    command2
    ...

  其中,target 是即將要創建的目標(通常是一個可執行文件),target 後面緊跟一個冒號,prerequisite 是生成該目標所需要的源文件(依賴),一個目標所依賴的文件可以有多個,依賴文件與目標之間以及各依賴文件之間用空格或制表符 Tab 隔開,這些元素組成了一個依賴關系。隨後的命令 command 就是規則,也就是 make 需要執行的命令,它可以是任意的 shell 命令。另外,makefile 文件中,註釋以 # 號開頭,一直延續到該行的結束

3.1 依賴關系

  依賴關系定義了最終應用程序裏的每個文件與源文件之間的關系。一個依賴關系列表由目標和該目標的零個或多個依賴組成,語法是:先寫目標,然後接一個冒號,再用一個空格或制表符隔開,最後是用空格或制表符隔開的依賴文件列表,如下:

target: prerequisite1 prerequisite2 prerequisite3 ...

  依賴關系表明了這樣一件事:目標文件 target 依賴於文件 prerequisite1、prerequisite2、prerequisite3 ...,即,要生成 target,需要有這幾個依賴文件的存在,而且,若其中一個依賴文件發生了改變,則需要重新生成 target。目標所依賴的文件可以有一個或多個,也可以沒有依賴文件 —— 該目標總被認為是過時的,在執行 make 命令時,若指定了該目標,則該目標所對應的規則將總被執行(如目標 clean)。

  makefile 文件中可以有很多個目標,每個目標都有自己對應的規則。make 命令默認創建的是 makefile 文件中的第一個目標。也可以自己指定一個目標讓 make 命令去創建,只需要將該目標的名字作為參數放到 make 命令之後即可(如常用的 make clean)。實際上,更好的做法是,將 makefile 文件中的第一個目標定義為 all,然後再 all 後面列出其他從屬目標,這將告訴 make 命令,在未指定特定目標時,默認情況下將創建哪個目標。此外,使用目標 all ,還可以使 make 命令一次性創建多個文件,這取決於 all 後面所接的從屬目標的個數。

  舉個例子說明一下文件與文件之間的依賴關系:

/* sum.c */
#include <stdio.h>
#include <stdlib.h>

extern int add(int i,int j);

int main()
{
  printf("%d\n",add(1,2));
  exit(0);
}

/* add.c */
#include <stdio.h>

int add(int i,int j)
{
  int k;
  k = i + j;
  return k;
}

  這是一個簡單的加法程序,包含兩個文件:sum.c 和 add.c,其中,sum.c 中的 main 函數調用了 add.c 中的 add 函數。這個程序的依賴關系表如下:

sum: sum.o add.o
sum.o: sum.c stdio.h stdlib.h
add.o: add.c stdio.h

  其中,最終所需要的目標文件是 sum,sum.o 和 add.o 是依賴 —— 要生成目標文件 sum ,需要先生成 sum.o 和 add.o。同樣的,作為目標的 sum.o 依賴於 sum.c、stdio.h 和 stdlib.h;add.o 依賴於 add.c 和 stdio.h。這組依賴關系形成了一個層次結構,它顯示了源文件之間的關系。

  可以看出來,如果 add.c 發生了改變,那麽就需要重新編譯 add.o,而由於 add.o 發生了改變,目標文件 sum 也需要被重新創建,同時,由於 add.c 的改變並沒有影響到 sum.o(sum.o 不依賴於 add.c),因此,sum.o 並不需要被重新編譯。也就是說,通過使用 makefile 文件和 make 命令,我們可以實現,只重新編譯所有受到改動影響的源文件,沒有受到影響的源文件不必重新編譯。這比把整個程序全部重新編譯一遍顯然要快上很多,尤其是對於大型程序。

3.2 規則

  makefile 文件裏另一部分內容是規則,它們定義了目標的創建方式。 規則的內容可以是任意的 shell 命令。關於規則,有以下兩點需要註意:

1)規則所在行必須以制表符 tab 開頭,不能用空格

2)規則所在行最好不要以空格結尾,可能會導致 make 命令執行失敗;

3)如果一行不足以寫下所有內容,需要在每行代碼的結尾加上一個反斜杠符 “\”,以讓所有的命令在邏輯上處於同一行。

兩個特殊字符 - 和 @:

  1)在規則中,若命令之前加上了符號 “-”,則表明 make 命令將忽略該命令產生的所有錯誤;

  2)若在命令之前加上了符號“@”,則表明 make 在執行該命令前,不會將該命令顯示在標準輸出上。

/* Makefile */
all: sum sum: sum.o add.o gcc
-o sum add.o sum.o sum.o: sum.c gcc -c sum.c add.o: add.c gcc -c add.c
clean:
-rm sum sum.o add.o

  這是 3.1 中 sum.c 程序的 makefile 文件。其中 gcc 、rm 命令等行就是規則,它們告訴了 make 命令將如何去創建目標。

兩個特殊的目標:clean 和 install

  目標 clean 和 install 是兩個特殊的目標,它們並不用於創建文件,而是有其他用途。

  目標 clean 在前面已經提到過,它使用 rm 命令來刪除目標文件。rm 命令通常以減號 - 開頭,表示讓 make 命令忽略該命令的執行結果,這意味著,即使由於文件不存在而導致 rm 命令返回錯誤,命令 make clean 也能成功執行。

  目標 install 用於按照命令的執行順序將應用程序安裝到指定的目錄,還是用上面的 sum.c 程序來演示一下目標 install 的用法:

all: sum

# 安裝目錄
INSTDIR = /tmp

sum: sum.o add.o
    gcc -o sum add.o sum.o
sum.o: sum.c
    gcc -c sum.c
add.o: add.c
    gcc -c add.c
clean:
    rm sum sum.o add.o

install: sum
    @if [ -d $(INSTDIR) ];    then            cp sum $(INSTDIR);            chmod a+x $(INSTDIR)/sum;            chmod og-w $(INSTDIR)/sum;            echo "Installed in $(INSTDIR)";    else            echo "The directory $(INSTDIR) dose not exist!";    fi

  使用這個 makefile 文件,make 命令將會把 sum 安裝到目錄 /tmp 下(實際上,應用程序一般是安裝在 /usr/local/bin 下的,這裏為了方便就放到 /tmp 下了) 。執行 make install 命令,將得到如下結果:

技術分享圖片

  輸出結果顯示 sum 已被成功安裝到了 /tmp 目錄下(實際上就是把可執行文件 sum 復制到 /tmp 目錄下)。再進入 /tmp 目錄查看,可以看到可執行文件 sum,其文件權限是 rwxr-xr-x,與 makefile 文件中所設置的一致。

技術分享圖片

3.3 makefile 文件中的宏

  在 makefile 文件中定義一個宏很簡單,如下:

MACRONAME=value

  這裏定義了一個宏 MACRONAME,引用宏的方法是使用 $(MACRONAME) 或 ${MACRONAME} 。使用宏定義,可以讓 makefile 文件的可移植性更強。除了自己定義一些宏以外,make 命令還內置了一些特殊的宏定義,使得 makefile 文件變得更加簡潔:

說明
$? 當前目標所依賴的文件列表中比當前目標文件還要新的文件
$@ 當前目標的名字
$< 當前依賴文件的名字
$* 不包括後綴名的當前依賴文件的名字

  除了在 makefile 文件裏面定義宏以外,還可以調用 make 命令時,在命令行上給出宏定義。命令行上的宏定義將 覆蓋在 makefile 文件中的宏定義。需要註意的是,在 make 命令後接宏定義時,宏定義必須以單個參數的形式傳遞,因此,需要避免在宏定義中使用空格或加引號。

參考資料:

《Linux 程序設計 第四版》

https://www.ibm.com/support/knowledgecenter/zh/ssw_aix_71/com.ibm.aix.cmds3/make.htm

簡介make命令和makefile文件