0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

什么是Makefile?

汽車電子技術(shù) ? 來源:嵌入式悅翔園 ? 作者: JamesBin ? 2023-02-17 10:41 ? 次閱讀

前言

如果您有多個 c、c++ 和其他語言的文件,并且想通過終端命令編譯它們,我們該如何編譯他們呢?為了解決這類問題,Makefile就出現(xiàn)了。Makefile在編譯大型項目的過程中,可以一次性編寫大量的源文件以及需要鏈接器標(biāo)志。廢話少說咱們直接開始今天的正文!圖片

什么是Makefile

Makefile是一種用于簡化或組織編譯代碼的工具,是一組具有變量名稱和目標(biāo)的命令(類似于終端命令),用于創(chuàng)建和刪除目標(biāo)文件的工具。在單個 make 文件中,我們可以創(chuàng)建多個目標(biāo)來編譯和刪除對象、二進(jìn)制文件。您可以使用Makefile多次編譯您的項目(程序)。

讓我們通過一個例子來理解:

假設(shè)我們有 3 個文件main.c(主源文件)、 misc.c(包含函數(shù)定義的源文件)、misc.h(包含函數(shù)聲明)。在這里,我們將聲明和定義一個名為myFunc()的函數(shù)來打印一些東西——這個函數(shù)將分別在misc.cmisc.h中定義和聲明。

misc.c

#include 
#include "misc.h"
 
/*function definition*/
void myFunc(void)
{
    printf("Body of myFunc function.\\n");
}

misc.h

#ifndef MISC_H
    #define MISC_H
     
    /*function declaration.*/
    void myFunc(void);
     
#endif

main.c

#include 
#include "misc.h"
 
int main()
{
    printf("Hello, World.\\n");
    myFunc();
    fflush(stdout);
 
    return 0;
}

上面這個場景是非常常見也是最簡單的一個多文件系統(tǒng)了,我們想要編譯他,并將他們鏈接在一起該如何做呢?顯然僅僅使用gcc等這些簡單的編譯器是不夠的,此時我們就需要用到Makefile了。

下面將內(nèi)容放在一個名為Makefile的文件中,注意Makefile文件的名字只能是這幾個字,而且區(qū)分大小寫。

Makefile

#make file - this is a comment section
 
all:    #target name
    gcc main.c misc.c -o main
  • 保存名為Makefile。
  • 插入注釋,后跟#字符。
  • all是一個目標(biāo)名稱,在目標(biāo)名稱之后插入:
  • gcc是編譯器名稱,main.c,misc.c源文件名,-o是鏈接器標(biāo)志,main是二進(jìn)制文件名。

“注意: Makefile必須使用 TAB 而不是空格縮進(jìn),否則make會失敗。

我們寫好Makefile后怎么進(jìn)行編譯呢?下面是代碼的編譯過程:

沒有目標(biāo)名稱:

make

帶有目標(biāo)名稱:

make all

輸出:

sh-4.3$ make
    gcc     main.c misc.c -o main

    sh-4.3$ ./main
    Hello, World.

    Body of myFunc function.
    sh-4.3$

此時我們就可以看到對應(yīng)文件夾里已經(jīng)生成了對應(yīng)的可執(zhí)行文件了!這就是Makefile的作用!

為什么會存在 Makefile?

Makefile 用于幫助決定大型程序的哪些部分需要重新編譯。在絕大多數(shù)情況下,編譯 C 或 C++ 文件。其他語言通常有自己的工具,其用途與 Make 相似。當(dāng)您需要一系列指令來運行取決于哪些文件已更改時,Make 也可以在編譯之外使用。本教程將重點介紹 C/C++ 編譯用例。

這是您可以使用 Make 構(gòu)建的示例依賴關(guān)系圖。如果任何文件的依賴項發(fā)生更改,則該文件將被重新編譯:圖片

Makefile的語法

一個 Makefile 由一組規(guī)則組成。規(guī)則通常如下所示:

targets: prerequisites
 command
 command
 command
  • targets:是文件名,以空格分隔。通常,每條規(guī)則只有一個。
  • command:是通常用于制作目標(biāo)的一系列步驟。這些需要以制表符開頭,而不是空格。
  • prerequisites:先決條件也是文件名,以空格分隔。這些文件需要在運行目標(biāo)命令之前存在。這些也稱為依賴項

Makefile的精髓

讓我們從一個 hello world 示例開始:

hello:
 echo "Hello, World"
 echo "This line will always print, because the file hello does not exist."

然后我們將運行make hello,只要hello文件不存在,命令就會運行。如果hello存在,則不會運行任何命令。

重要的是要意識到我說hello的是targetfile,那是因為兩者是直接聯(lián)系在一起的。通常,當(dāng)運行目標(biāo)時(也就是運行目標(biāo)的命令時),這些命令將創(chuàng)建一個與目標(biāo)同名的文件。在這種情況下,hello 目標(biāo)不會創(chuàng)建hello 文件。

那么我們怎么樣才能讓程序全部重新生成呢?這就要用到清理目標(biāo)文件的語句了,下面我們一起看一下如何清理已生成的目標(biāo)文件。

清理生成的目標(biāo)文件

我們還可以使用 Makefile 中的變量來概括Makefile。在此示例中,我們使用變量和干凈的目標(biāo)名稱編寫 Makefile 以刪除所有對象(.o 擴展文件)和二進(jìn)制文件(主文件)。

#make file - this is a comment section
 
CC=gcc  #compiler
TARGET=main #target file name
 
all:
    $(CC) main.c misc.c -o $(TARGET)
 
clean:
    rm $(TARGET)

編譯:

make

此時我們想要的目標(biāo)文件以及.o文件已經(jīng)出現(xiàn)在對應(yīng)的文件夾中,那我們?nèi)绾蝿h除編譯出來的文件呢?是不是要使用rm語句一個一個的刪除呢?其實大可不必,而且在大的工程中你也不可能一個一個的刪除,所以這時候make clean就出現(xiàn)了,他能通過一條語句就刪除剛才編譯出來的所有文件,下面我們來看一下應(yīng)該如何操作!

直接在Makefile對應(yīng)的文件夾先輸入一下命令,就會發(fā)現(xiàn)剛才生成的文件已經(jīng)消失了。

make clean

當(dāng)我們有多個文件時,我們可以在 Makefile 中編寫命令來為每個源文件創(chuàng)建目標(biāo)文件。如果你這樣做 只有那些被修改的文件將被編譯。

如果我們想要全部重新編譯只需要先執(zhí)行make clean 語句在執(zhí)行make即可。

Makefile中如何使用變量

在上面的示例中,大多數(shù)目標(biāo)值和先決條件值都是硬編碼的,但在實際項目中,這些值被替換為變量和模式。

在 Makefile 中定義變量的最簡單方法是使用=運算符。例如,要將命令分配給gcc變量CC

CC = gcc

這也稱為遞歸擴展變量,它用于如下所示的規(guī)則中:

hello: hello.c
    ${CC} hello.c -o hello

那么實際在終端中執(zhí)行的語句是下面的:

gcc hello.c -o hello

兩者${CC}$(CC)都是對 gcc的有效引用。但是如果想將一個變量重新分配給它自己,它將導(dǎo)致一個無限循環(huán)。讓我們驗證一下:

CC = gcc
CC = ${CC}

all:
    @echo ${CC}

運行make將導(dǎo)致下面的錯誤:

$ make
Makefile:8: *** Recursive variable 'CC' references itself (eventually).  Stop.

為了避免這種情況,我們可以使用:=運算符(這也稱為簡單擴展變量)。我們運行下面的makefile應(yīng)就不會出現(xiàn)上面的問題了:

CC := gcc
CC := ${CC}

all:
    @echo ${CC}

舉個例子

下面我們通過一個實際的例子來體會一下上面講的知識點。以下 makefile 使用了變量、模式和函數(shù)編譯所有 C 程序。

# Usage:
# make        # compile all binary
# make clean  # remove ALL binaries and objects

.PHONY = all clean

CC = gcc                        # compiler to use

LINKERFLAG = -lm

SRCS := $(wildcard *.c)
BINS := $(SRCS:%.c=%)

all: ${BINS}

%: %.o
        @echo "Checking.."
        ${CC} ${LINKERFLAG} $< -o $@

%.o: %.c
        @echo "Creating object.."
        ${CC} -c $<

clean:
        @echo "Cleaning up..."
        rm -rvf *.o ${BINS}
  • #注釋整行。
  • Line.PHONY = all clean定義虛假目標(biāo)allclean.
  • 變量LINKERFLAG定義要在gcc中使用的標(biāo)志。
  • SRCS := $(wildcard *.c):$(wildcard pattern)是文件名的功能之一。在這種情況下,所有帶有.c擴展名的文件都將存儲在一個變量SRCS中。
  • BINS := $(SRCS:%.c=%): 這稱為替代參考。在這種情況下,如果SRCS有值'foo.c bar.c'BINS就會有'foo bar'。
  • Line all: ${BINS}:虛假目標(biāo)all將值${BINS}作為單獨的目標(biāo)調(diào)用。

讓我們看一個例子來理解這個規(guī)則。假設(shè)foo是中的值之一${BINS}。然后%將匹配foo%可以匹配任何目標(biāo)名稱)。以下是擴展形式的規(guī)則:

foo: foo.o
  @ echo "Checking.."
  gcc -lm foo.o -o foo

如上所示,%替換為foo%.o替換為foo.o。%.o被模式化以匹配先決條件,并將%匹配為目標(biāo)。

下面是對上述makefile的重寫,并將它被放置在具有單個文件的foo.c中:

# Usage:
# make        # compile all binary
# make clean  # remove ALL binaries and objects

.PHONY = all clean

CC = gcc                        # compiler to use

LINKERFLAG = -lm

SRCS := foo.c
BINS := foo

all: foo

foo: foo.o
        @echo "Checking.."
        gcc -lm foo.o -o foo

foo.o: foo.c
        @echo "Creating object.."
        gcc -c foo.c

clean:
        @echo "Cleaning up..."
        rm -rvf foo.o foo

這樣我們就可以使用一條語句make 完成整個程序的編譯了,如果想刪除編譯中生成的文件,可以使用make clean

多目標(biāo)Makefile

制作多個目標(biāo)并且您希望所有目標(biāo)都運行?做一個all目標(biāo)。make由于這是列出的第一條規(guī)則,如果在沒有指定目標(biāo)的情況下調(diào)用它,它將默認(rèn)運行。

all: one two three

one:
 touch one
two:
 touch two
three:
 touch three

clean:
 rm -f one two three

自動變量和通配符

* 通配符*%Make 中都稱為通配符,但它們的含義完全不同。*在您的文件系統(tǒng)中搜索匹配的文件名。

# Print out file information about every .c file
print: $(wildcard *.c)
 ls -la  $?
  • *可以在目標(biāo)、先決條件或wildcard函數(shù)中使用。
  • *不能在變量定義中直接使用
  • 當(dāng)*沒有匹配到文件時,保持原樣(除非在wildcard函數(shù)中運行)

% 通配符%確實很有用,但是由于可以使用的情況多種多樣,因此有些混亂。

  • matching模式下使用時,它匹配字符串中的一個或多個字符。
  • replacing模式下使用時,它采用匹配的詞干并替換字符串中的詞干。
  • %最常用于規(guī)則定義和某些特定功能中。

結(jié)語

最后讓我們通過一個非常多汁的 Make 示例來結(jié)束本文,它適用于中型項目。

這個 makefile 的巧妙之處在于它會自動為您確定依賴關(guān)系。您所要做的就是將您的 C/C++ 文件放入該src/文件夾中。

TARGET_EXEC := final_program

BUILD_DIR := ./build
SRC_DIRS := ./src

# Find all the C and C++ files we want to compile
# Note the single quotes around the * expressions. Make will incorrectly expand these otherwise.
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')

# String substitution for every C/C++ file.
# As an example, hello.cpp turns into ./build/hello.cpp.o
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)

# String substitution (suffix version without %).
# As an example, ./build/hello.cpp.o turns into ./build/hello.cpp.d
DEPS := $(OBJS:.o=.d)

# Every folder in ./src will need to be passed to GCC so that it can find header files
INC_DIRS := $(shell find $(SRC_DIRS) -type d)
# Add a prefix to INC_DIRS. So moduleA would become -ImoduleA. GCC understands this -I flag
INC_FLAGS := $(addprefix -I,$(INC_DIRS))

# The -MMD and -MP flags together generate Makefiles for us!
# These files will have .d instead of .o as the output.
CPPFLAGS := $(INC_FLAGS) -MMD -MP

# The final build step.
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)
 $(CXX) $(OBJS) -o $@ $(LDFLAGS)

# Build step for C source
$(BUILD_DIR)/%.c.o: %.c
 mkdir -p $(dir $@)
 $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@

# Build step for C++ source
$(BUILD_DIR)/%.cpp.o: %.cpp
 mkdir -p $(dir $@)
 $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@


.PHONY: clean
clean:
 rm -r $(BUILD_DIR)

# Include the .d makefiles. The - at the front suppresses the errors of missing
# Makefiles. Initially, all the .d files will be missing, and we don't want those
# errors to show up.
-include $(DEPS)
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • C++
    C++
    +關(guān)注

    關(guān)注

    21

    文章

    2085

    瀏覽量

    73301
  • 編譯
    +關(guān)注

    關(guān)注

    0

    文章

    646

    瀏覽量

    32668
  • Makefile
    +關(guān)注

    關(guān)注

    1

    文章

    125

    瀏覽量

    19145
收藏 人收藏

    評論

    相關(guān)推薦

    Makefile中文教程 -下載

    Makefile中文教程,免費下載哦。
    發(fā)表于 03-25 08:57 ?67次下載

    教你寫Makefile

    教你寫Makefile 什么是makefile?或許很多Winodws的程序員都不知道這個東西,因為那些Windows的IDE都為你做了這個工作,但我覺得要作一個好的和professional的程序員,
    發(fā)表于 02-10 14:24 ?37次下載

    駕馭Makefile

    駕馭Makefile在網(wǎng)上你能找到很多關(guān)于Makefile的學(xué)習(xí)資料,但絕大部分給你的只是一個知識點,與將Makefile運用到項目中(尤其是大型項目)的差距非常的大。因為,將Makefile
    發(fā)表于 01-05 17:05 ?9次下載

    Makefile教程

    Linux開發(fā)必備的Makefile文件編譯教本,講解的很到位,新手基本上看上幾天就可以上手了
    發(fā)表于 01-05 17:05 ?0次下載

    跟我一起學(xué)makefile

    關(guān)于makefile學(xué)習(xí),深入了解嵌入式下makefile的編寫
    發(fā)表于 05-23 18:21 ?0次下載

    駕馭makefile

    一本Makefile的經(jīng)典教程,深入淺出,易學(xué)易懂
    發(fā)表于 06-17 16:16 ?9次下載

    makefile教程(中文版)

    算法學(xué)習(xí)的資料makefile教程(中文版)
    發(fā)表于 09-01 14:55 ?0次下載

    Linux內(nèi)核Makefile文件

    Makefile文件的詳細(xì)編寫過程
    發(fā)表于 08-28 09:25 ?14次下載

    linux makefile教程

     什么是makefile?或許很多Winodws的程序員都不知道這個東西,因為那些Windows的IDE都為你做了這個工作,但我覺得要作一個好的和professional的程序員,makefile
    發(fā)表于 11-12 09:11 ?5210次閱讀

    makefile的基本語法

     在Makefile中,最重要的三個概念是:目標(biāo)(target)、依賴關(guān)系(dependency)和命令(command)。目標(biāo)是指要干什么,即運行make后生成什么;依賴是指明目標(biāo)所依賴的其他目標(biāo);命令則告訴make如何生成目標(biāo),這三個概念是通過Makefile中的規(guī)則
    發(fā)表于 11-12 10:15 ?1.1w次閱讀

    Makefile是什么?Makefile工作原理是怎樣的?Makefile經(jīng)典教程免費下載

    Makefile的重要性 會不會寫makefile,從一個側(cè)面說明了一個人是否具備完成大型工程的能力 makefile帶來的好處就是——“自動化編譯”,一旦寫好,只需要一個make命令,整個工程完全自動編譯,極大的提高了軟件
    發(fā)表于 09-12 17:19 ?0次下載
    <b class='flag-5'>Makefile</b>是什么?<b class='flag-5'>Makefile</b>工作原理是怎樣的?<b class='flag-5'>Makefile</b>經(jīng)典教程免費下載

    嵌入式中的Makefile應(yīng)用

    文章目錄一.Makefile 引入二. Makefile語法1.通配符2.假象目標(biāo)3.變量三.Makefile函數(shù)四.實例本文主要總結(jié)一下嵌入式開發(fā)中的Makefile,一般項目中都需
    發(fā)表于 11-03 17:06 ?11次下載
    嵌入式中的<b class='flag-5'>Makefile</b>應(yīng)用

    MakefileMakefile與shell命令的聯(lián)系

    博主最近在項目實踐過程中,需要深度定制項目的Makefile,其中有些復(fù)雜的流程必須得借助shell腳本才能高效地完成,為此博主特意深入學(xué)習(xí)了在Makefile種調(diào)用shell命令的方法。
    的頭像 發(fā)表于 07-11 09:06 ?3145次閱讀
    【<b class='flag-5'>Makefile</b>】<b class='flag-5'>Makefile</b>與shell命令的聯(lián)系

    Makefile】簡單實用的Makefile模板來了

    【Linux + Makefile】簡單實用的Makefile模板來了
    的頭像 發(fā)表于 08-31 12:46 ?1808次閱讀
    【<b class='flag-5'>Makefile</b>】簡單實用的<b class='flag-5'>Makefile</b>模板來了

    Makefile可以做什么?Makefile的基本格式

    Makefile可以根據(jù)指定的依賴規(guī)則和文件是否有修改來執(zhí)行命令。常用來編譯軟件源代碼,只需要重新編譯修改過的文件,使得編譯速度大大加快。
    的頭像 發(fā)表于 01-25 11:18 ?531次閱讀