当前位置: 首页 > 工具软件 > makefile2dot > 使用案例 >

makefile

柯栋
2023-12-01
 = 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值
#项目类型 属于C 还是 C++
TYPE ?= C

#此项目源文件后缀类型
PROJECTTYPE = .cpp

#您想要生成可执行文件的名字 如果外部没有赋值,那么使用obj.out
target ?= obj.out

#是否生成DEBUG选项
DEBUG ?=

#用户额外追加的编译配置选项
ARGS ?=

#系统之外的宏定义
PREDEF ?=

#C语言编译器
CC = gcc

#C++编译器
CXX = g++

#简化rm -f
RM = rm -f

#C语言配置参数
CFLAGS = -pedantic -std=c11 -Wall -o

#C++配置参数
CXXFLAGS = -g -Wall -std=c++11 -o
COMPILER =
COMPILERFLAG =

#条件编译使得其满足C项目 以及 C++项目
ifeq ($(TYPE),C)
	COMPILER := $(CC)
	COMPILERFLAG := $(CFLAGS)
	PROJECTTYPE = .c
else
ifeq ($(TYPE),C++)
	COMPILER := $(CXX)
	COMPILERFLAG := $(CXXFLAGS)
	PROJECTTYPE = .cpp

else
ifeq ($(TYPE),ARMC)
	COMPILER := $(CC)
	COMPILERFLAG := $(CFLAGS)
	PROJECTTYPE = .c
else
	COMPILER :=
	COMPILERFLAG :=
	PROJECTTYPE :=
endif
endif
endif

#获取当前makefile绝对路径
pes_parent_dir:=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))////

#删除路径下最后一个 /
pes_parent_dir:=$(subst /////,,$(pes_parent_dir))

#将所有宏定义前方加入-D指令以便给编译器识别
DEF := $(foreach n,$(PREDEF),-D$(n))

#获取目录下所有子目录
AllDirs := $(shell cd $(pes_parent_dir); ls -R | grep '^\./.*:$$' | awk '{gsub(":","");print}') .

#添加成为绝对路径
AllDirs := $(foreach n,$(AllDirs),$(subst .,$(pes_parent_dir),$(n)))

#获取所有 .c/.cpp文件路径
Sources := $(foreach n,$(AllDirs) , $(wildcard $(n)/*$(PROJECTTYPE)))

#设置*.o 和 *.d 文件的存放路径
buildPath :=$(foreach n,$(Sources),$(subst $(pes_parent_dir),$(pes_parent_dir)/build,$(n)))

#处理得到*.o 后缀文件名
OBJS := $(patsubst %$(PROJECTTYPE),%.o, $(buildPath))

#同理得到 *.d文件名
Deps := $(patsubst %$(PROJECTTYPE),%.d, $(buildPath))

#需要用到的第三方静态库
StaticLib :=

#需要用到的第三方动态链接库
DynamicLib :=

#真实二进制文件输出路径
Bin :=$(pes_parent_dir)/$(target)

#头文件搜索路径
INCLUDE_PATH = $(foreach n,$(AllDirs) , -I$(n))
LDFLAGS =

#指定AllLibs为终极目标 即:最新的Bin
AllLibs:$(Bin)

#声明这个标签 des 用于观察当前的路径是否正确
.PHONY:des
des:
	@echo OBJS =  $(OBJS)
	@echo cur_makefile_path = $(pes_parent_dir)
	@echo AllDirs = $(AllDirs)
	@echo Sources = $(Sources)
	@echo Deps = $(Deps)
	@echo makefilePath =$(mkPath)
	@echo bulidPath=$(buildPath)
	@echo PREDEF = $(DEF)
	@echo DEBUG = $(DEBUG)
	@echo TYPE = $(TYPE)

#对应关系 在本makefile中以空格隔开的后缀为.c 都会为其生成一个新的.d文件
$(pes_parent_dir)/build/%.d :$(pes_parent_dir)/%$(PROJECTTYPE)
	@echo 'finding $< depending head file'
	@if [ ! -d $(dir $@) ]; then mkdir -p $(dir $@); fi;
	@$(COMPILER) -MM $(INCLUDE_PATH) $(CPPFLAGS) $< > $@.$$$$;\
	sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@;\
	rm -f $@.$$$$
#	$(CXX) -MM $(INCLUDE_PATH) $(DEF) $(CPPFLAGS) $(CXXFLAGS) $< > $@;

#对于include中的*.d文件,只要里面任意有一个文件被修改,那么就会触发此规则生成一个新的*.o文件
%.o: %.d
	@echo compile $(patsubst %.d,%$(PROJECTTYPE),$(subst build/,,$<))
	@$(COMPILER) -c $(patsubst %.d,%$(PROJECTTYPE),$(subst build/,,$<)) $(DEBUG) $(DEF) $(INCLUDE_PATH) $(ARGS) $(COMPILERFLAG) $@

include $(buildPath:$(PROJECTTYPE)=.d)

#生成文件的最终表达式
$(Bin) : $(OBJS)
	@echo bulding....
	@$(COMPILER) $(OBJS) $(ARGS)  $(COMPILERFLAG) $(Bin)
	@echo created file: $(target)

#删除所有编译带来的文件
.PHONY : clean
clean:clean_out clean_build clean_bin

#删除 *.o文件
.PHONY : clean_out
clean_out:
	    @echo 'removeing all *.o file ...'
	    @$(RM) -r $(pes_parent_dir)/build/*.o
	    @echo 'done'

#删除 build 文件夹
.PHONY : clean_build
clean_build:
	    @echo 'removeing all build temp file ...'
	    @$(RM) -r $(pes_parent_dir)/build
	    @echo 'done'

#删除 可执行文件
.PHONY : clean_bin
clean_bin:
	    @echo 'removeing executable file ...'
	    @$(RM) $(Bin)
	    @echo 'done'


#重新编译
.PHONY : rebuild
rebuild: clean_out AllLibs

#显示帮助
.PHONY : help
help:
		@echo "本makefile 一共实现了以下几种命令模式"
		@echo "AllLibs \t\t默认的命令,含义为编译当前项目并输出可执行文件 $(target)\n"
		@echo "clean \t\t清理命令,删除由makefile带来的所有文件\n"
		@echo "clean_out \t\t清理中间文件命令,删除由编译带来的所有文件 *.o\n"
		@echo "clean_build \t\t清理中间文件命令,删除编译临时文件夹 /build\n"
		@echo "clean_bin \t\t删除可执行文件 $(target)\n"
		@echo "rebuild \t\t重新编译\n"
		@echo "|----------------------------------------------|"
		@echo "内建一些逻辑,以及传参即可改变\n"
		@echo "ARGS \t\t用户自我追加的一些编译参数,例如多线程中需要加入 -lpthread 参数\n"
		@echo "TYPE \t\t用于确认当前项目是C++ 项目还是 C语言项目 (默认为C) 如果是C++ 那么自动编译*.cpp文件 ,如果是 C 那么自动编译*.c文件,暂不支持混合添加\n"
		@echo "target \t\t用户自定义生成的可执行文件名,例如windows下 生成obj.exe Linux下生成 obj.out\n"
		@echo "DEBUG \t\t一般传入 -g 表示支持单步调试,\n"
		@echo "PREDEF \t\t预先定义一些宏,在程序中起到开关裁剪的作用\n"
		@echo "Please check the console encoding format in case of garbled codes. The initial encoding format of this makefile is UTF8\n"

此 Makefile模板 已经充分考虑到文件之间的依赖关系,即: 如果某个头文件发生改变,当运行make的时候,所有依赖于它的源文件都将被重新编译。

// 关键变量
CROSS_COMPILE:交叉编译器前缀
OPTIMIZE:     关于优化的编译参数
WARNINGS:     关于warning的编译参数
DEFS:         关于宏定义的编译参数
EXTRA_CFLAGS: 其它的编译参数
$(OPTIMIZE) $(WARNINGS) $(DEFS) $(EXTRA_CFLAGS) 共同构成了传给gcc的编译参数

INC_DIR:      头文件 include 目录
SRC_DIR:      源文件 .c .cpp .cc .cxx 目录(当前目录 .是被默认包含的)
OBJ_DIR:      目标文件.o 输出目录
EXTRA_SRC:    额外源文件
EXCLUDE_FILES:exclude文件列表

SUFFIX:源文件的后缀名
TARGET:最终的目标程序名
TARGET_TYPE:目标程序的类型: 
            ar (static library)或者
            so (shared library)或者
            app(application)


// 最终Makefile的行为是:
1. 查找 $(SRC_DIR) 定义的每个目录中后缀为 $(SUFFIX) 的文件,并加上 $(EXTRA_SRC) 中的文件,
2. 排除 $(EXCLUDE_FILES) 中的文件,获得本工程定义的源文件列表。
3. 对每个源文件,编译生成的一个.o文件和一个.d文件(依赖文件),放在 $(OBJ_DIR) 目录下。
4. 最终生成的目标文件为 $(TARGET)。

= 是最基本的赋值
:= 是覆盖之前的值
?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值

###############################################################################
#
# A smart Makefile template for GNU/LINUX programming
#
# Author: PRC (ijkxyz AT msn DOT com)
# Date:   2011/06/17
#
# Usage:
#   $ make           Compile and link (or archive)
#   $ make clean     Clean the objectives and target.
###############################################################################

CROSS_COMPILE =
OPTIMIZE := -O2
WARNINGS := -Wall -Wno-unused -Wno-format
DEFS     :=
EXTRA_CFLAGS :=

INC_DIR   =
SRC_DIR   =
OBJ_DIR   =
EXTRA_SRC =
EXCLUDE_FILES =

SUFFIX       = c cpp cc cxx
TARGET       :=
#TARGET_TYPE  := ar
TARGET_TYPE   := so
#TARGET_TYPE  := app


#####################################################################################
#  Do not change any part of them unless you have understood this script very well  #
#  This is a kind remind.                                                           #
#####################################################################################

#FUNC#  Add a new line to the input stream.
define add_newline
$1

endef

#FUNC# set the variable `src-x' according to the input $1
define set_src_x
src-$1 = $(filter-out $4,$(foreach d,$2,$(wildcard $d/*.$1)) $(filter %.$1,$3))

endef

#FUNC# set the variable `obj-x' according to the input $1
define set_obj_x
obj-$1 = $(patsubst %.$1,$3%.o,$(notdir $2))

endef

#VAR# Get the uniform representation of the object directory path name
ifneq ($(OBJ_DIR),)
prefix_objdir  = $(shell echo $(OBJ_DIR)|sed 's:\(\./*\)*::')
prefix_objdir := $(filter-out /,$(prefix_objdir)/)
endif

GCC      := $(CROSS_COMPILE)gcc
G++      := $(CROSS_COMPILE)g++
SRC_DIR := $(sort . $(SRC_DIR))
inc_dir = $(foreach d,$(sort $(INC_DIR) $(SRC_DIR)),-I$d)

#--# Do smart deduction automatically
$(eval $(foreach i,$(SUFFIX),$(call set_src_x,$i,$(SRC_DIR),$(EXTRA_SRC),$(EXCLUDE_FILES))))
$(eval $(foreach i,$(SUFFIX),$(call set_obj_x,$i,$(src-$i),$(prefix_objdir))))
$(eval $(foreach f,$(EXTRA_SRC),$(call add_newline,vpath $(notdir $f) $(dir $f))))
$(eval $(foreach d,$(SRC_DIR),$(foreach i,$(SUFFIX),$(call add_newline,vpath %.$i $d))))

all_objs = $(foreach i,$(SUFFIX),$(obj-$i))
all_srcs = $(foreach i,$(SUFFIX),$(src-$i))

CFLAGS       = $(EXTRA_CFLAGS) $(WARNINGS) $(OPTIMIZE) $(DEFS)
TARGET_TYPE := $(strip $(TARGET_TYPE))

ifeq ($(filter $(TARGET_TYPE),so ar app),)
$(error Unexpected TARGET_TYPE `$(TARGET_TYPE)')
endif

ifeq ($(TARGET_TYPE),so)
CFLAGS  += -fpic -shared
LDFLAGS += -shared
endif

PHONY = all .mkdir clean

all: .mkdir $(TARGET)

define cmd_o
$(obj-$1): $2%.o: %.$1  $(MAKEFILE_LIST)
        $(GCC) $(inc_dir) -Wp,-MT,$@ -Wp,-MMD,$@.d $(CFLAGS) -c -o $@ $<

endef
$(eval $(foreach i,$(SUFFIX),$(call cmd_o,$i,$(prefix_objdir))))

ifeq ($(TARGET_TYPE),ar)
$(TARGET): AR := $(CROSS_COMPILE)ar
$(TARGET): $(all_objs)
        rm -f $@
        $(AR) rcvs $@ $(all_objs)
else
$(TARGET): LD = $(if $(strip $(src-cpp) $(src-cc) $(src-cxx)),$(G++),$(GCC))
$(TARGET): $(all_objs)
        $(LD) $(LDFLAGS) $(all_objs) -o $@
endif

.mkdir:
        @if [ ! -d $(OBJ_DIR) ]; then mkdir -p $(OBJ_DIR); fi

clean:
        rm -f $(prefix_objdir)*.o $(TARGET)

-include $(patsubst %.o,%.o.d,$(all_objs))

.PHONY: $(PHONY)

参考: http://bbs.chinaunix.net/thread-3553668-1-1.html

 类似资料: