本文展示了一个比较完整的企业项目级别的Makefile文件,包括了:文件调用,源文件、头文件、库文件指定,软件版本号、宏定义,编译时间,自动目录等内容。
本文中所采用的目录架构,在企业项目开发中十分常见:源文件都放在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
本文所用到的所有文件,也可以直接到我的公众号,后台回复“ 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)
输入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()
include srcpathconfig.mk
相当于把srcpathconfig.mk的内容都拿过来,srcpathconfig.mk中的变量,在Makefile文件中都可以直接使用。
tmpbuildtm := `date |sed 's/ /_/g'` TMPBUILDTM = $(tmpbuildtm) @echo $(TMPBUILDTM)
这个是把当前的时间,保存到TMPBUILDTM变量中,可以运用到源码中,本案例只是打印一下此变量。
APPVERSION = 1.0.0.0 @$(CC) -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)
开发过程中,我们会有多个版本的程序,可以在程序加上版本号作为后缀。
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中有使用,对应的是打印不同的内容。在实际项目中,宏定义的作用很广,可以用来跨平台开发,也可以用来调试打印。
SRCFILE := $(foreach d,$(SRCCODEDIRS),$(wildcard $(addprefix $(d)/*,.c))) OBJFILE := $(patsubst %.c,%.o,$(SRCFILE))
由于我们的源文件是放在src目录下的不同子目录中,所以使用了foreach函数来循环获取。简单说明一下,foreach后面跟着的d,是中间变量,这一行的作用就是将SRCCODEDIRS的路径下的.c文件,逐个逐个拿出来,加上对应的路径前缀。
关于foreach的函数的具体使用方法,不做过多介绍。
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 $@
将普通头文件和库头文件的存放路径单独用变量表示
LIBFILEDIRS := ../lib/libs LIBFILE := -lmylib $(TARGETNAME):$(OBJFILE) @$(CC) -o $(OUTPUTDIR)/$(TARGETNAME).$(APPVERSION) $(OBJFILE) -L$(LIBFILEDIRS) $(LIBFILE)
将库文件的名字和存放路径单独用变量表示
CC := gcc CFLAGS := -Wall -c RM := rm RMFLAGS := -rf
CC := gcc,指定编译器为gcc;CFLAGS 和RMFLAGS中的内容可以根据需求调整,所以单独拿出来,-Wall是表示编译的时候可以产生告警,便于分析。
OUTPUTDIR := ../output @mkdir -p $(OUTPUTDIR) @-$(RM) $(RMFLAGS) $(TARGETNAME) $(OBJFILE) $(OUTPUTDIR)
make命令会自动创建output目录,用来存放生成的目标文件。
make clean会将此目录及目录中的所有内容都删除
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 $@
———————————————————————————————
码字不易,点个赞再走吧!
欢迎关注我的同名公众号,这里有更多好料等着你哦!