輕松玩轉(zhuǎn)Makefile | 企業(yè)項(xiàng)目級(jí)Makefile實(shí)例

前言

本文展示了一個(gè)比較完整的企業(yè)項(xiàng)目級(jí)別的Makefile文件,包括了:文件調(diào)用,源文件、頭文件、庫文件指定,軟件版本號(hào)、宏定義,編譯時(shí)間,自動(dòng)目錄等內(nèi)容。

1、目錄架構(gòu)

本文中所采用的目錄架構(gòu),在企業(yè)項(xiàng)目開發(fā)中十分常見:源文件都放在src目錄中,頭文件都放在inc目錄中,并且這兩個(gè)目錄都可以有對(duì)應(yīng)的子目錄。庫文件放在lib目錄中,makefile相關(guān)文件放在build目錄中,編程生成的程序放在自動(dòng)生成的output目錄中。目錄結(jié)構(gòu)展示如下:

.
├── build
│   ├── Makefile
│   └── srcpathconfig.mk
├── code
│   ├── inc
│   │   ├── com
│   │   └── func
│   │       └── fun.h
│   └── src
│       ├── com
│       │   └── main.c
│       └── func
│           └── fun.c
└── lib
    ├── inc
    │   └── mylib.h
    └── libs
        └── libmylib.so

2、源文件及Makefile內(nèi)容

本文所用到的所有文件,也可以私信我獲取。

源文件

/* fun.h */
#ifndef __FUN_H__
#define __FUN_H__

void fun();

#endif


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

void fun()
{

#ifdef MACRO_DEF
    printf("macro definition enable!\n");
#endif


#ifdef COMPILER_IS_ARM_LINUX_GCC
    printf("The compilation target is arm!\n");
#endif

#ifdef COMPILER_IS_LINUX_GCC
    printf("The compilation target is linux!\n");
#endif

    printf("This is fun()!\n");
}

/* mylib.h */
void mylib();


/* libmylib.so */
// mylib()函數(shù),打印This is mylib()!

/* main.c */
#include "fun.h"
#include "mylib.h"

int main()
{ 
    fun();
    mylib();
    return 0; 
}

srcpathconfig.mk

這個(gè)文件的內(nèi)容,其實(shí)也可以放在Makefile中,本案例單獨(dú)用一個(gè)文件來配置路徑,是為了后期好管理

#源文件目錄
SRCCODEDIRS   :=../code/src/func \
                ../code/src/com \
    

#頭文件目錄
SRCHEADDIRS   :=../code/inc/func \
                ../code/inc/com \

#lib文件目錄
LIBFILEDIRS := ../lib/libs

#lib頭文件目錄
LIBHEADDIRS := ../lib/inc/

#lib文件
LIBFILE := -lmylib

Makefile

#引用其他文件
include srcpathconfig.mk

#時(shí)間信息
tmpbuildtm := `date |sed 's/ /_/g'`
TMPBUILDTM = $(tmpbuildtm)

#軟件版本
APPVERSION = 1.0.0.0


#不同的目標(biāo)采用不同的宏定義
ifeq ($(MAKECMDGOALS),arm)
COMPILEMACRO += COMPILER_IS_ARM_LINUX_GCC
else
COMPILEMACRO += COMPILER_IS_LINUX_GCC MACRO_DEF
endif


#循環(huán)獲取源文件和中間件
SRCFILE := $(foreach d,$(SRCCODEDIRS),$(wildcard $(addprefix $(d)/*,.c)))
OBJFILE := $(patsubst %.c,%.o,$(SRCFILE))

#宏定義,源文件路徑,頭文件路徑
CURCMPLMACRO   := $(addprefix -D ,$(COMPILEMACRO))
CURSRCHEADDIRS := $(addprefix -I ,$(SRCHEADDIRS))
CURLIBHEADDIRS := $(addprefix -I ,$(LIBHEADDIRS))

#程序輸出路徑
OUTPUTDIR := ../output

#編譯器及選項(xiàng)
CC := gcc
CFLAGS := -Wall -c

RM := rm
RMFLAGS := -rf

#目標(biāo)文件   
TARGETNAME = app

$(TARGETNAME):$(OBJFILE)
    @mkdir -p $(OUTPUTDIR)
    @echo ""
    @echo "all files have been compiled , now begin to link every obj for excutable file"
    @echo ""
    @echo "linking............"
    @echo $(OBJFILE)
    @$(CC)  -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)
    @echo ""
    @echo "linked ok," $(TARGETNAME) "has been created"
    @echo ""
    @echo $(TMPBUILDTM)
    
%.o: %.c
    @echo ""
    @echo "start " $< "......compiling"
    @$(CC) $(CURCMPLMACRO) $(CFLAGS) $(CURSRCHEADDIRS) $(CURLIBHEADDIRS) $< -o $@
    @echo "created " $@
    @echo "end   " $< "......compiled ok"
    @echo ""

.PHONY: arm clean

arm:$(TARGETNAME)

clean:
    @-$(RM) $(RMFLAGS) $(TARGETNAME) $(OBJFILE) $(OUTPUTDIR)

3、效果演示

輸入make 或者 make arm ,打印如下

start  ../code/src/func/fun.c ......compiling
created  ../code/src/func/fun.o
end    ../code/src/func/fun.c ......compiled ok


start  ../code/src/com/main.c ......compiling
created  ../code/src/com/main.o
end    ../code/src/com/main.c ......compiled ok


all files have been compiled , now begin to link every obj for excutable file

linking............
../code/src/func/fun.o ../code/src/com/main.o

linked ok, app has been created

Fri_Mar__3_22:14:09_PST_2023

生成的文件架構(gòu)如下

.
├── build
│   ├── Makefile
│   └── srcpathconfig.mk
├── code
│   ├── inc
│   │   ├── com
│   │   └── func
│   │       └── fun.h
│   └── src
│       ├── com
│       │   ├── main.c
│       │   └── main.o
│       └── func
│           ├── fun.c
│           └── fun.o
├── lib
│   ├── inc
│   │   └── mylib.h
│   └── libs
│       └── libmylib.so
└── output
    └── app.1.0.0.0

運(yùn)行output中生成的app.1.0.0.0程序

/* 由make命令編譯生成的app.1.0.0.0 */

macro definition enable!
The compilation target is linux!
This is fun()!
This is mylib()
/* 由make arm命令編譯生成的app.1.0.0.0 */

The compilation target is arm!
This is fun()!
This is mylib()

4、Makefile內(nèi)容解析

4.1 文件調(diào)用

include srcpathconfig.mk

相當(dāng)于把srcpathconfig.mk的內(nèi)容都拿過來,srcpathconfig.mk中的變量,在Makefile文件中都可以直接使用。

4.2 編譯時(shí)間

tmpbuildtm := `date |sed 's/ /_/g'`
TMPBUILDTM = $(tmpbuildtm)

@echo $(TMPBUILDTM)

這個(gè)是把當(dāng)前的時(shí)間,保存到TMPBUILDTM變量中,可以運(yùn)用到源碼中,本案例只是打印一下此變量。

4.3 軟件版本

APPVERSION = 1.0.0.0
@$(CC)  -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)

開發(fā)過程中,我們會(huì)有多個(gè)版本的程序,可以在程序加上版本號(hào)作為后綴。

4.4 宏定義

ifeq ($(MAKECMDGOALS),arm)
COMPILEMACRO += COMPILER_IS_ARM_LINUX_GCC
else
COMPILEMACRO += COMPILER_IS_LINUX_GCC MACRO_DEF
endif

CURCMPLMACRO   := $(addprefix -D ,$(COMPILEMACRO))

%.o: %.c
    @$(CC) $(CURCMPLMACRO) $(CFLAGS) $(CURSRCHEADDIRS) $(CURLIBHEADDIRS) $< -o $@

makefile中也可以使用條件判斷,具體用法這里不多做介紹。

MAKECMDGOALS,是make命令后面跟的目標(biāo),比如make arm,那么MAKECMDGOALS的值就為arm。

這里利用MAKECMDGOALS的值來選擇使用哪些宏定義,假如make 后面跟的是arm,宏定義則是COMPILER_IS_ARM_LINUX_GCC,假如make后面跟的不是arm,宏定義則是COMPILER_IS_LINUX_GCC和MACRO_DEF。

這些宏定義在fun.c中有使用,對(duì)應(yīng)的是打印不同的內(nèi)容。在實(shí)際項(xiàng)目中,宏定義的作用很廣,可以用來跨平臺(tái)開發(fā),也可以用來調(diào)試打印。

4.5 源文件及中間件

SRCFILE := $(foreach d,$(SRCCODEDIRS),$(wildcard $(addprefix $(d)/*,.c)))
OBJFILE := $(patsubst %.c,%.o,$(SRCFILE))

由于我們的源文件是放在src目錄下的不同子目錄中,所以使用了foreach函數(shù)來循環(huán)獲取。簡(jiǎn)單說明一下,foreach后面跟著的d,是中間變量,這一行的作用就是將SRCCODEDIRS的路徑下的.c文件,逐個(gè)逐個(gè)拿出來,加上對(duì)應(yīng)的路徑前綴。

關(guān)于foreach的函數(shù)的具體使用方法,不做過多介紹。

4.6 頭文件

SRCHEADDIRS   :=../code/inc/func \
                ../code/inc/com \
                
LIBHEADDIRS := ../lib/inc/              

CURSRCHEADDIRS := $(addprefix -I ,$(SRCHEADDIRS))
CURLIBHEADDIRS := $(addprefix -I ,$(LIBHEADDIRS))

%.o: %.c
    @$(CC) $(CURCMPLMACRO) $(CFLAGS) $(CURSRCHEADDIRS) $(CURLIBHEADDIRS) $< -o $@

將普通頭文件和庫頭文件的存放路徑單獨(dú)用變量表示

4.7 庫文件

LIBFILEDIRS := ../lib/libs
LIBFILE := -lmylib

$(TARGETNAME):$(OBJFILE)
    @$(CC)  -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)

將庫文件的名字和存放路徑單獨(dú)用變量表示

4.8 編譯選項(xiàng)

CC := gcc
CFLAGS := -Wall -c

RM := rm
RMFLAGS := -rf

CC := gcc,指定編譯器為gcc;CFLAGS 和RMFLAGS中的內(nèi)容可以根據(jù)需求調(diào)整,所以單獨(dú)拿出來,-Wall是表示編譯的時(shí)候可以產(chǎn)生告警,便于分析。

4.9 自動(dòng)目錄

OUTPUTDIR := ../output

@mkdir -p $(OUTPUTDIR)

@-$(RM) $(RMFLAGS) $(TARGETNAME) $(OBJFILE) $(OUTPUTDIR)

make命令會(huì)自動(dòng)創(chuàng)建output目錄,用來存放生成的目標(biāo)文件。

make clean會(huì)將此目錄及目錄中的所有內(nèi)容都刪除

4.10 打印信息

TARGETNAME = app

$(TARGETNAME):$(OBJFILE)
    @mkdir -p $(OUTPUTDIR)
    @echo ""
    @echo "all files have been compiled , now begin to link every obj for excutable file"
    @echo ""
    @echo "linking............"
    @echo $(OBJFILE)
    @$(CC)  -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)
    @echo ""
    @echo "linked ok," $(TARGETNAME) "has been created"
    @echo ""
    @echo $(TMPBUILDTM)
    
%.o: %.c
    @echo ""
    @echo "start " $< "......compiling"
    @$(CC) $(CURCMPLMACRO) $(CFLAGS) $(CURSRCHEADDIRS) $(CURLIBHEADDIRS) $< -o $@
    @echo "created " $@
    @echo "end   " $< "......compiled ok"
    @echo ""

所有@echo的內(nèi)容,都是為了編譯的時(shí)候,打印一些信息,方便查看才加上去的,實(shí)際上有真正有用的是下面這些

TARGETNAME = app

$(TARGETNAME):$(OBJFILE)
    @mkdir -p $(OUTPUTDIR)
    @$(CC)  -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)

%.o: %.c
    @$(CC) $(CURCMPLMACRO) $(CFLAGS) $(CURSRCHEADDIRS) $(CURLIBHEADDIRS) $< -o $@


———————————————————————————————

碼字不易,點(diǎn)個(gè)贊再走吧!也歡迎私信我,一起交流!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容