前言 本文展示了一個比較完整的企業項目級別的Makefile文件,包括了:文件調用,源文件、頭文件、庫文件指定,軟體版本號、巨集定義,編譯時間,自動目錄等內容。 1、目錄架構 本文中所採用的目錄架構,在企業項目開發中十分常見:源文件都放在src目錄中,頭文件都放在inc目錄中,並且這兩個目錄都可以有對 ...
前言
本文展示了一個比較完整的企業項目級別的Makefile文件,包括了:文件調用,源文件、頭文件、庫文件指定,軟體版本號、巨集定義,編譯時間,自動目錄等內容。
1、目錄架構
本文中所採用的目錄架構,在企業項目開發中十分常見:源文件都放在src目錄中,頭文件都放在inc目錄中,並且這兩個目錄都可以有對應的子目錄。庫文件放在lib目錄中,makefile相關文件放在build目錄中,編程生成的程式放在自動生成的output目錄中。目錄結構展示如下:
.
├── 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內容
本文所用到的所有文件,也可以直接到我的公眾號,後臺回覆“ mk ”獲取。
源文件
/* 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()函數,列印This is mylib()!
/* main.c */
#include "fun.h"
#include "mylib.h"
int main()
{
fun();
mylib();
return 0;
}
srcpathconfig.mk
這個文件的內容,其實也可以放在Makefile中,本案例單獨用一個文件來配置路徑,是為了後期好管理
#源文件目錄
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
#時間信息
tmpbuildtm := `date |sed 's/ /_/g'`
TMPBUILDTM = $(tmpbuildtm)
#軟體版本
APPVERSION = 1.0.0.0
#不同的目標採用不同的巨集定義
ifeq ($(MAKECMDGOALS),arm)
COMPILEMACRO += COMPILER_IS_ARM_LINUX_GCC
else
COMPILEMACRO += COMPILER_IS_LINUX_GCC MACRO_DEF
endif
#迴圈獲取源文件和中間件
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
#編譯器及選項
CC := gcc
CFLAGS := -Wall -c
RM := rm
RMFLAGS := -rf
#目標文件
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
生成的文件架構如下
.
├── 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
運行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內容解析
4.1 文件調用
include srcpathconfig.mk
相當於把srcpathconfig.mk的內容都拿過來,srcpathconfig.mk中的變數,在Makefile文件中都可以直接使用。
4.2 編譯時間
tmpbuildtm := `date |sed 's/ /_/g'`
TMPBUILDTM = $(tmpbuildtm)
@echo $(TMPBUILDTM)
這個是把當前的時間,保存到TMPBUILDTM變數中,可以運用到源碼中,本案例只是列印一下此變數。
4.3 軟體版本
APPVERSION = 1.0.0.0
@$(CC) -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)
開發過程中,我們會有多個版本的程式,可以在程式加上版本號作為尾碼。
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命令後面跟的目標,比如make arm,那麼MAKECMDGOALS的值就為arm。
這裡利用MAKECMDGOALS的值來選擇使用哪些巨集定義,假如make 後面跟的是arm,巨集定義則是COMPILER_IS_ARM_LINUX_GCC,假如make後面跟的不是arm,巨集定義則是COMPILER_IS_LINUX_GCC和MACRO_DEF。
這些巨集定義在fun.c中有使用,對應的是列印不同的內容。在實際項目中,巨集定義的作用很廣,可以用來跨平臺開發,也可以用來調試列印。
4.5 源文件及中間件
SRCFILE := $(foreach d,$(SRCCODEDIRS),$(wildcard $(addprefix $(d)/*,.c)))
OBJFILE := $(patsubst %.c,%.o,$(SRCFILE))
由於我們的源文件是放在src目錄下的不同子目錄中,所以使用了foreach函數來迴圈獲取。簡單說明一下,foreach後面跟著的d,是中間變數,這一行的作用就是將SRCCODEDIRS的路徑下的.c文件,逐個逐個拿出來,加上對應的路徑首碼。
關於foreach的函數的具體使用方法,不做過多介紹。
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 $@
將普通頭文件和庫頭文件的存放路徑單獨用變數表示
4.7 庫文件
LIBFILEDIRS := ../lib/libs
LIBFILE := -lmylib
$(TARGETNAME):$(OBJFILE)
@$(CC) -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)
將庫文件的名字和存放路徑單獨用變數表示
4.8 編譯選項
CC := gcc
CFLAGS := -Wall -c
RM := rm
RMFLAGS := -rf
CC := gcc,指定編譯器為gcc;CFLAGS 和RMFLAGS中的內容可以根據需求調整,所以單獨拿出來,-Wall是表示編譯的時候可以產生告警,便於分析。
4.9 自動目錄
OUTPUTDIR := ../output
@mkdir -p $(OUTPUTDIR)
@-$(RM) $(RMFLAGS) $(TARGETNAME) $(OBJFILE) $(OUTPUTDIR)
make命令會自動創建output目錄,用來存放生成的目標文件。
make clean會將此目錄及目錄中的所有內容都刪除
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的內容,都是為了編譯的時候,列印一些信息,方便查看才加上去的,實際上有真正有用的是下麵這些
TARGETNAME = app
$(TARGETNAME):$(OBJFILE)
@mkdir -p $(OUTPUTDIR)
@$(CC) -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)
%.o: %.c
@$(CC) $(CURCMPLMACRO) $(CFLAGS) $(CURSRCHEADDIRS) $(CURLIBHEADDIRS) $< -o $@
————————————————————————————————
碼字不易,點個贊再走吧!
歡迎關註我的同名公眾號,這裡有更多好料等著你哦!