前言
本文展示了一個(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è)贊再走吧!也歡迎私信我,一起交流!