最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

轻松玩转Makefile | 企业项目级Makefile实例

来源:博客园

前言

本文展示了一个比较完整的企业项目级别的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 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");#endifprintf("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_GCCelseCOMPILEMACRO += COMPILER_IS_LINUX_GCC MACRO_DEFendif#循环获取源文件和中间件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 := gccCFLAGS := -Wall -cRM := rmRMFLAGS := -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 cleanarm:$(TARGETNAME)clean:@-$(RM) $(RMFLAGS) $(TARGETNAME) $(OBJFILE) $(OUTPUTDIR)

3、效果演示

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

start  ../code/src/func/fun.c ......compilingcreated  ../code/src/func/fun.oend    ../code/src/func/fun.c ......compiled okstart  ../code/src/com/main.c ......compilingcreated  ../code/src/com/main.oend    ../code/src/com/main.c ......compiled okall files have been compiled , now begin to link every obj for excutable filelinking............../code/src/func/fun.o ../code/src/com/main.olinked ok, app has been createdFri_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_GCCelseCOMPILEMACRO += COMPILER_IS_LINUX_GCC MACRO_DEFendifCURCMPLMACRO   := $(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/libsLIBFILE := -lmylib$(TARGETNAME):$(OBJFILE)@$(CC)  -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)

将库文件的名字和存放路径单独用变量表示

4.8 编译选项

CC := gccCFLAGS := -Wall -cRM := rmRMFLAGS := -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 $@

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

码字不易,点个赞再走吧!

欢迎关注我的同名公众号,这里有更多好料等着你哦!

关键词: 编译时间 编译选项 目标文件