簡介make命令和makefile文件

来源:https://www.cnblogs.com/tongye/archive/2018/11/11/9921654.html
-Advertisement-
Play Games

一、為什麼要用到 make 命令和 makefile 文件 在 Linux 下編寫一個程式,每次編譯都需要在命令行一行一行的敲命令。如果是一個很小的程式還好說,命令不怎的複雜,編譯速度也挺快,但是對於大型程式來說,這樣無疑很麻煩,且不說可能會敲錯命令,有時候僅僅改動了一個小地方,卻需要將整個程式全部 ...


一、為什麼要用到 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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第一章 第一個C#程式 ******************C#程式*************** ①:建立項目:文件-->新建-->項目-->c#-->控制台程式(項目名/文件存儲位置)-->確定 ①:c#程式 namespace:命名空間; 相當於java中的package(聲明包) using ...
  • 三大框架房很好看絕代風華搜狗科技華東師範撒的發空間撒地方是否 ...
  • 註意:本文背景為 Linq to sql 。文中ie指代IEnumerable,iq指代IQueryable。 IQueryable 和 IEnumerable 的區別 IQueryable延時執行;擴展方法接受的是Expression(必須要能轉成sql,否則報錯) IEnumerable延時執行 ...
  • 在.NET Core框架中使用NLog組件記錄日誌,寫資料庫,寫文件!!! ...
  • " 【.NET Core項目實戰 統一認證平臺】開篇及目錄索引 " 這篇文章,我們將從Ocelot的中間件源碼分析,目前Ocelot已經實現那些功能,還有那些功能在我們實際項目中暫時還未實現,如果我們要使用這些功能,應該如何改造等方面來說明。 一、Ocelot源碼解讀 在使用一個組件前,最好我們要了 ...
  • [TOC] 算術運算符     在任何一門形式的語言中均會存在算術運算的情況,Shell常見的運算符如下所示: | 運算符 | 含義 | | | | | + \ / % | 加 減 乘 除 求餘 | | \ \ | 冪運算 | | ++ | 自增 自減 | | && & 124; ...
  • Linux 學習-電腦基礎 一、描述電腦的組成及其功能。 電腦系統是由硬體(Hardware)和軟體(Software )兩部分組成。 硬體: 從硬體基本結構上來講,電腦是由運算器、控制器、存儲器、輸入設備、輸出設備五大部分組成的,每一部分分別按要求執行特定的基本功能。 1、運算器(算數邏輯 ...
  • 關閉觸摸 關閉觸摸和按鍵 打開 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...