最近的项目又回到了Linux上运行,这就需要在Linux下编译项目,写Makefile针对习惯了Windows的程序员来说是一件痛苦的事,如果有一个通用的Makefile该多好啊,本着这样的目的,我再次研究了一下Makefile,写出了一个实用的通用Makefile,该Makefile在Windows以及Linux平台下作了一些简单测试,未发现问题,如果大家在使用过程中发现有问题可以联系我。话不多说,直接上代码:

  1###############################################################################
  2#
  3#  C/C++通用Makefile
  4#
  5#  作者:Witton Bell         E_Mail:witton@163.com
  6#
  7#  版本V1.0 (2017/02/23)
  8#		初始版本,可以生成App,Share以及Static库等等
  9#  版本V1.1 (2017/02/28)
 10#		添加预预编译头的支持,将生成的中间文件放在debug或者release目录下
 11#  版本V1.2 (2017/03/12)
 12#		优化以及fixed:修改头文件后,不会编译
 13#  版本V1.3 (2017/03/13)
 14#		优化:将依赖文件放在.deps目录下,make clean时不会重复生成
 15#
 16#  一个项目只需要在顶层目录配置一个Makefile,不需要各个目录单独配置,简单方便实用
 17#  原文链接:http://blog.csdn.net/witton/article/details/56670748
 18#
 19#  功能说明:
 20#  1.支持Windows以及Linux下的可执行文件(App)、动态库(Share)以及静态库(Static)的生成
 21#  2.支持生成Map文件,Bin文件以及十六进制文件
 22#  3.支持C/C++的预编译头,以加快编译速度,包括同时存在C与C++预编译头的情况
 23#  4.生成的中间以及目标代码会指定存放在debug或者release目录下
 24#  5.支持生成进度显示
 25#
 26###############################################################################
 27
 28# 可以修改的区域
 29
 30# 目标文件的名字(必须指定)
 31TARGETNAME :=
 32
 33#目标类型可以是APP, STATIC或者SHARED
 34TARGETTYPE := APP
 35
 36#配置类型可以是Debug或者Release
 37CONFIG ?= Debug
 38
 39# C 预编译头文件,支持带路径
 40PCH_H =
 41# C++ 预编译头文件,支持带路径
 42PCH_X_H =
 43
 44#源文件目录列表,可以填多个目录,如:src1 src2 ../src
 45#如果没填写则默认为当前目录
 46SRC_ROOT_DIRS := .
 47
 48#排除的目录列表,需要带上相对路径
 49EXCLUDE_DIRS :=
 50
 51#排除的文件列表,需要带上相对路径
 52EXCLUDE_FILES :=
 53
 54#头文件所在目录列表
 55INCLUDE_DIRS :=
 56#库文件所在目录列表
 57LIBRARY_DIRS :=
 58#公共库文件名列表
 59LIBRARY_NAMES := 
 60#公共宏定义
 61PREPROCESSOR_MACROS :=
 62
 63#各种编译链接参数
 64#汇编编译参数
 65ASFLAGS := -f win64
 66#ld链接参数
 67LDFLAGS := -Wl,-gc-sections
 68#公共编译参数,根据情况作修改
 69COMMONFLAGS := -g -ffunction-sections -finput-charset=UTF-8 -fexec-charset=UTF-8 -fwide-exec-charset=UTF-16LE
 70#额外的库文件
 71EXTERNAL_LIBS := 
 72
 73#工具集,Windows下最好是填写MinGW命令行工具的绝对路径,并且路径中需要使用/分隔
 74#因为Windows下的一些命令无相应的参数,会报错,
 75#比如mkdir, cp, rm, echo等等,如果安装了Git及命令行工具,建议直接使用git安装目录下usr/bin路径
 76CC := gcc
 77CXX := g++
 78LD := $(CXX)
 79ASM := nasm
 80AR := ar
 81OBJCOPY := objcopy
 82CP := cp
 83MKDIR = mkdir
 84RM = rm -rf
 85ECHO = echo
 86SHELL = /bin/sh
 87
 88#根据配置类型填写不同的参数
 89ifeq ($(CONFIG), Debug)
 90	PREPROCESSOR_MACROS += DEBUG
 91	LIBRARY_NAMES += 
 92	ADDITIONAL_LINKER_INPUTS := 
 93	MACOS_FRAMEWORKS := 
 94	LINUX_PACKAGES := 
 95	CFLAGS := -O0 -Wall
 96	CXXFLAGS := -O0
 97else
 98	PREPROCESSOR_MACROS +=
 99	LIBRARY_NAMES += 
100	ADDITIONAL_LINKER_INPUTS := 
101	MACOS_FRAMEWORKS := 
102	LINUX_PACKAGES := 
103	CFLAGS := -O3
104	CXXFLAGS := -O3
105endif
106
107#需要编译的源文件的扩展名列表
108SRC_EXTS := .c .cpp .cc .cxx .c++ .s .S .asm
109
110#如果是在Linux下编译,需要打开此开关
111#IS_LINUX_PROJECT := 1
112
113#如果需要生成Map文件,需要打开此开关
114#GENERATE_MAP_FILE := 1
115
116#如果生成Bin文件,需要打开此开关
117#GENERATE_BIN_FILE := 1
118
119#如果生成十六进制文件,需要打开此开关
120#GENERATE_IHEX_FILE := 1
121
122#是否显示详细的命令行
123#SHOW_CMD_DETAIL := 1
124
125#可修改区域结束,以下区域不建议修改,除非特别了解其含义
126
127#======================华丽的分割线=============================
128
129#小写函数
130to_lowercase = $(subst A,a,$(subst B,b,$(subst C,c,$(subst D,d,$(subst E,e,$(subst F,f,$(subst G,g,$(subst H,h,$(subst I,i,$(subst J,j,$(subst K,k,$(subst L,l,$(subst M,m,$(subst N,n,$(subst O,o,$(subst P,p,$(subst Q,q,$(subst R,r,$(subst S,s,$(subst T,t,$(subst U,u,$(subst V,v,$(subst W,w,$(subst X,x,$(subst Y,y,$(subst Z,z,$1))))))))))))))))))))))))))
131
132BINARYDIR := $(call to_lowercase,$(CONFIG))
133
134ifeq ($(BINARYDIR),)
135error:
136	$(error Invalid configuration, please check your inputs)
137endif
138
139DEPS_DIR := .deps
140
141#如果没配置源文件目录,则默认为当前目录
142ifeq ($(SRC_ROOT_DIRS),)
143	SRC_ROOT_DIRS = .
144endif
145
146define walk
147$(wildcard $(addprefix $(1)/*, $(SRC_EXTS))) $(foreach e, $(wildcard $(1)/*), $(call walk, $(e)))
148endef
149
150#匹配所有源文件
151SOURCEFILES := $(foreach e, $(SRC_ROOT_DIRS), $(call walk, $(e)))
152#匹配所有需要排除的源文件
153EXCLUDE_SOURCE := $(foreach e, $(EXCLUDE_DIRS), $(call walk, $(e)))
154EXCLUDE_FILES += $(EXCLUDE_SOURCE)
155EXTERNAL_LIBS_COPIED := $(foreach lib, $(EXTERNAL_LIBS),$(BINARYDIR)/$(notdir $(lib)))
156
157#设置C预编译相关变量
158ifneq ($(PCH_H),)
159PCH = $(PCH_H).gch
160PCH_FLAGS := -Winvalid-pch -include $(BINARYDIR)/$(PCH_H)
161PCH_FILE := $(BINARYDIR)/$(PCH)
162endif
163
164#设置C++预编译相关变量
165ifneq ($(PCH_X_H),)
166PCH_X = $(PCH_X_H).gch
167PCH_X_FLAGS := -Winvalid-pch -include $(BINARYDIR)/$(PCH_X_H)
168PCH_X_FILE := $(BINARYDIR)/$(PCH_X)
169endif
170
171START_GROUP := -Wl,--start-group
172END_GROUP := -Wl,--end-group
173INCLUDE_DIRS += .
174
175#处理头文件目录
176CFLAGS += $(addprefix -I,$(INCLUDE_DIRS))
177CXXFLAGS += $(addprefix -I,$(INCLUDE_DIRS))
178
179#处理宏定义
180CFLAGS += $(addprefix -D,$(PREPROCESSOR_MACROS))
181CXXFLAGS += $(addprefix -D,$(PREPROCESSOR_MACROS))
182ASFLAGS += $(addprefix -D,$(PREPROCESSOR_MACROS))
183
184#处理框架
185CFLAGS += $(addprefix -framework ,$(MACOS_FRAMEWORKS))
186CXXFLAGS += $(addprefix -framework ,$(MACOS_FRAMEWORKS))
187LDFLAGS += $(addprefix -framework ,$(MACOS_FRAMEWORKS))
188
189#处理库目录
190LDFLAGS += $(addprefix -L,$(LIBRARY_DIRS))
191
192CFLAGS += $(COMMONFLAGS)
193CXXFLAGS += $(COMMONFLAGS)
194LDFLAGS += $(COMMONFLAGS)
195
196ifeq ($(GENERATE_MAP_FILE),1)
197LDFLAGS += -Wl,-Map=$(BINARYDIR)/$(basename $(TARGETNAME)).map
198endif
199
200LIBRARY_LDFLAGS = $(addprefix -l,$(LIBRARY_NAMES))
201
202ifeq ($(IS_LINUX_PROJECT),1)
203
204ifeq ($(TARGETTYPE),SHARED)
205TempName = $(addsuffix .so,$(basename $(TARGETNAME)))
206TARGETNAME := $(TempName)
207endif
208
209	RPATH_PREFIX := -Wl,--rpath='$$ORIGIN/../
210	LIBRARY_LDFLAGS += $(EXTERNAL_LIBS)
211	LIBRARY_LDFLAGS += -Wl,--rpath='$$ORIGIN'
212	LIBRARY_LDFLAGS += $(addsuffix ',$(addprefix $(RPATH_PREFIX),$(dir $(EXTERNAL_LIBS))))
213	
214	#如果是Linux下的共享库(Share)项目,则需要添加-fPIC参数,以实现位置无关代码
215	ifeq ($(TARGETTYPE),SHARED)
216		CFLAGS += -fPIC
217		CXXFLAGS += -fPIC
218		ASFLAGS += -fPIC
219		LIBRARY_LDFLAGS += -Wl,-soname,$(TARGETNAME)
220	endif
221	
222	ifneq ($(LINUX_PACKAGES),)
223		PACKAGE_CFLAGS := $(foreach pkg,$(LINUX_PACKAGES),$(shell pkg-config --cflags $(pkg)))
224		PACKAGE_LDFLAGS := $(foreach pkg,$(LINUX_PACKAGES),$(shell pkg-config --libs $(pkg)))
225		CFLAGS += $(PACKAGE_CFLAGS)
226		CXXFLAGS += $(PACKAGE_CFLAGS)
227		LIBRARY_LDFLAGS += $(PACKAGE_LDFLAGS)
228	endif
229else
230	LIBRARY_LDFLAGS += $(EXTERNAL_LIBS)
231
232ifeq ($(TARGETTYPE),APP)
233TempName = $(addsuffix .exe,$(basename $(TARGETNAME)))
234TARGETNAME := $(TempName)
235endif
236
237ifeq ($(TARGETTYPE),SHARED)
238TempName = $(addsuffix .dll,$(basename $(TARGETNAME)))
239TARGETNAME := $(TempName)
240endif
241
242endif
243
244LIBRARY_LDFLAGS += $(ADDITIONAL_LINKER_INPUTS)
245
246#静态库都是以.a为后缀
247ifeq ($(TARGETTYPE),STATIC)
248TempName = $(addsuffix .a,$(basename $(TARGETNAME)))
249TARGETNAME := $(TempName)
250endif
251
252ifeq ($(STARTUPFILES),)
253	all_source_files := $(SOURCEFILES)
254else
255	all_source_files := $(STARTUPFILES) $(filter-out $(STARTUPFILES),$(SOURCEFILES))
256endif
257
258define HandlePath
259$(foreach x,$(foreach x,$(foreach x,$(subst //,/,$(1)),$(subst ../,*/,$(x))),$(subst ./,,$(x))),$(subst */,../,$(x)))
260endef
261
262AllSource := $(call HandlePath, $(all_source_files))
263AllExcludeSource := $(call HandlePath, $(EXCLUDE_FILES))
264
265Source := $(filter-out $(AllExcludeSource),$(AllSource))
266
267CompileObjs := $(foreach x,$(SRC_EXTS),$(patsubst %$(x),%.o,$(filter %$(x),$(Source))))
268all_objs := $(foreach x,$(CompileObjs),$(BINARYDIR)/$(x))
269
270ASM_EXT := .s .S .asm
271CompileCObjs := $(foreach x,$(filter-out $(ASM_EXT),$(SRC_EXTS)),$(patsubst %$(x),%.d,$(filter %$(x),$(Source))))
272all_Cobjs := $(foreach x,$(CompileCObjs),$(BINARYDIR)/$(x))
273all_Deps := $(subst ../,__/, $(foreach x,$(CompileCObjs),$(DEPS_DIR)/$(x)))
274
275DEPS := $(all_Deps:.o=.d)
276
277revert0 = $(2) $(1)
278define revert
279$(foreach x,$(DEPS),$(eval TempStep = $(call revert0, $(TempStep),$(x)))) $(TempStep)
280endef
281
282AllStep := $(call revert,$(DEPS))
283AllStep += $(CompileObjs)
284
285WordNum := $(words $(AllStep))
286
287ProgressInfo := $(foreach x,$(AllStep),$(eval Counter += A)$(addsuffix .$(words $(Counter)), $(basename $(x))))
288
289define FindProgress
290$(foreach x,$(ProgressInfo),$(if $(filter $(basename $(x)),$(1)),$(subst .,,$(suffix $(x))),))
291endef
292
293define ShowProgress
294$(strip $(call FindProgress,$(basename $(1))))/$(WordNum)
295endef
296
297IS_GCC_ASM :=
298ifneq ($(filter $(ASM),$(CXX)),)
299IS_GCC_ASM = 1
300else
301ifneq ($(filter $(ASM),$(CC)),)
302IS_GCC_ASM = 1
303endif
304endif
305
306ifeq ($(IS_GCC_ASM),1)
307CompileA 	:= $(ASM) $(CFLAGS) $(ASFLAGS) -c
308else
309CompileA 	:= $(ASM) $(ASFLAGS)
310endif
311
312CompileC 	:= $(CC) $(PCH_FLAGS) $(CFLAGS) -c
313CompileCXX	:= $(CXX) $(PCH_X_FLAGS) $(CXXFLAGS) -c
314
315ifneq ($(SHOW_CMD_DETAIL),)
316define CompileSrc
317	@$(MKDIR) -p $(BINARYDIR)/$(subst ../,__/, $(dir $(1)))
318	@$(ECHO) -n [$(call ShowProgress,$(2))]
319	$(3) $(2) -o $(subst ../,__/, $(1))
320endef
321else
322define CompileSrc
323	@$(MKDIR) -p $(BINARYDIR)/$(subst ../,__/, $(dir $(1)))
324	@$(ECHO) [$(call ShowProgress,$(2))] Compile $(2)
325	@$(3) $(2) -o $(subst ../,__/, $(1))
326endef
327endif
328
329define CompileCDep
330	@$(MKDIR) -p $(subst ../,__/, $(dir $(1)))
331	@$(ECHO) [$(call ShowProgress,$(DEPS_DIR)/$(2))] Generate $(subst ../,__/,$(1))
332	@$(CC) -MM $(CFLAGS) $(2) > $(DEPS_DIR)/temp
333	@$(ECHO) -n $(subst ./,, $(dir $(1))) > $(subst ../,__/,$(1))
334	@$(CC) -MM $(CFLAGS) $(2) >> $(subst ../,__/,$(1))
335endef
336
337define CompileCXXDep
338	@$(MKDIR) -p $(subst ../,__/, $(dir $(1)))
339	@$(ECHO) [$(call ShowProgress,$(DEPS_DIR)/$(2))] Generate $(subst ../,__/,$(1))
340	@$(CXX) -MM $(CXXFLAGS) $(2) > $(DEPS_DIR)/temp
341	@$(ECHO) -n $(subst ./,, $(dir $(1))) > $(subst ../,__/,$(1))
342	@$(CXX) -MM $(CXXFLAGS) $(2) >> $(subst ../,__/,$(1))
343endef
344
345PRIMARY_OUTPUTS :=
346
347ifeq ($(GENERATE_BIN_FILE),1)
348PRIMARY_OUTPUTS += $(BINARYDIR)/$(basename $(TARGETNAME)).bin
349endif
350
351ifeq ($(GENERATE_IHEX_FILE),1)
352PRIMARY_OUTPUTS += $(BINARYDIR)/$(basename $(TARGETNAME)).ihex
353endif
354
355ifeq ($(PRIMARY_OUTPUTS),)
356PRIMARY_OUTPUTS := $(BINARYDIR)/$(TARGETNAME)
357endif
358
359.PHONY: all clean rebuild distclean cleandeps help
360
361.SUFFIXES:
362
363all: $(PRIMARY_OUTPUTS)
364	@$(RM) $(DEPS_DIR)/temp
365	@$(ECHO) Built OK.
366
367clean:
368	@$(RM) $(BINARYDIR)
369
370cleandeps:
371	@$(RM) $(DEPS_DIR)
372
373distclean: clean cleandeps
374	
375rebuild: clean all
376
377$(BINARYDIR)/$(basename $(TARGETNAME)).bin: $(BINARYDIR)/$(TARGETNAME)
378	@$(OBJCOPY) -O binary $< $(subst ../,__/, $@)
379
380$(BINARYDIR)/$(basename $(TARGETNAME)).ihex: $(BINARYDIR)/$(TARGETNAME)
381	@$(OBJCOPY) -O ihex $< $(subst ../,__/, $@) 
382
383ifeq ($(TARGETTYPE),APP)
384ifneq ($(SHOW_CMD_DETAIL),)
385$(BINARYDIR)/$(TARGETNAME): $(all_objs) $(EXTERNAL_LIBS)
386	$(LD) -o $(subst ../,__/, $@) $(LDFLAGS) $(START_GROUP) $(subst ../,__/, $(all_objs)) $(LIBRARY_LDFLAGS) $(END_GROUP)
387else
388$(BINARYDIR)/$(TARGETNAME): $(all_objs) $(EXTERNAL_LIBS)
389	@$(ECHO) Link App $(subst ../,__/, $@)
390	@$(LD) -o $(subst ../,__/, $@) $(LDFLAGS) $(START_GROUP) $(subst ../,__/, $(all_objs)) $(LIBRARY_LDFLAGS) $(END_GROUP)
391endif
392endif
393
394ifeq ($(TARGETTYPE),SHARED)
395ifneq ($(SHOW_CMD_DETAIL),)
396$(BINARYDIR)/$(TARGETNAME): $(all_objs) $(EXTERNAL_LIBS)
397	$(LD) -shared -o $(subst ../,__/, $@) $(LDFLAGS) $(START_GROUP) $(subst ../,__/, $(all_objs)) $(LIBRARY_LDFLAGS) $(END_GROUP)
398else
399$(BINARYDIR)/$(TARGETNAME): $(all_objs) $(EXTERNAL_LIBS)
400	@$(ECHO) Link Share lib $(subst ../,__/, $@)
401	@$(LD) -shared -o $(subst ../,__/, $@) $(LDFLAGS) $(START_GROUP) $(subst ../,__/, $(all_objs)) $(LIBRARY_LDFLAGS) $(END_GROUP)
402endif
403endif
404
405ifeq ($(TARGETTYPE),STATIC)
406ifneq ($(SHOW_CMD_DETAIL),)
407$(BINARYDIR)/$(TARGETNAME): $(all_objs)
408	$(AR) -r $(subst ../,__/, $@) $(subst ../,__/, $^)
409else
410$(BINARYDIR)/$(TARGETNAME): $(all_objs)
411	@$(ECHO) Link Static lib $(subst ../,__/, $@)
412	@$(AR) -r $(subst ../,__/, $@) $(subst ../,__/, $^)
413endif
414endif
415
416$(BINARYDIR):
417	@$(MKDIR) $(BINARYDIR)
418
419#Makefile的生成规则
420#生成依赖文件
421$(DEPS_DIR)/%.d : %.c
422	$(call CompileCDep,$@,$<)
423
424$(DEPS_DIR)/%.d : %.cpp
425	$(call CompileCXXDep,$@,$<)
426
427$(DEPS_DIR)/%.d : %.cxx
428	$(call CompileCXXDep,$@,$<)
429
430$(DEPS_DIR)/%.d : %.cc
431	$(call CompileCXXDep,$@,$<)
432
433$(DEPS_DIR)/%.d : %.c++
434	$(call CompileCXXDep,$@,$<)
435
436#C文件的生成规则
437$(BINARYDIR)/%.o : %.c $(PCH_FILE)
438	$(call CompileSrc,$@,$<,$(CompileC))
439
440#C++的.cpp文件的生成规则
441$(BINARYDIR)/%.o : %.cpp $(PCH_X_FILE)
442	$(call CompileSrc,$@,$<,$(CompileCXX))
443
444#C++的.cc文件的生成规则
445$(BINARYDIR)/%.o : %.cc $(PCH_X_FILE)
446	$(call CompileSrc,$@,$<,$(CompileCXX))
447
448#C++的.cxx文件的生成规则
449$(BINARYDIR)/%.o : %.cxx $(PCH_X_FILE)
450	$(call CompileSrc,$@,$<,$(CompileCXX))
451
452#C++的.c++文件的生成规则
453$(BINARYDIR)/%.o : %.c++ $(PCH_X_FILE)
454	$(call CompileSrc,$@,$<,$(CompileCXX))
455
456#Asm的.S文件生成规则
457$(BINARYDIR)/%.o : %.S
458	$(call CompileSrc,$@,$<,$(CompileA))
459
460#Asm的.s文件生成规则
461$(BINARYDIR)/%.o : %.s
462	$(call CompileSrc,$@,$<,$(CompileA))
463
464#Asm的.asm文件生成规则
465$(BINARYDIR)/%.o : %.asm
466	$(call CompileSrc,$@,$<,$(CompileA))
467
468ifneq ($(PCH_H),)
469#C预编译头文件的生成规则 
470$(BINARYDIR)/$(PCH) : $(PCH_H)
471	@$(MKDIR) -p $(subst ../,__/, $(dir $@))
472	@$(ECHO) Precompiled C header $<
473	@$(CC) $(CFLAGS) $> $^ -o $(subst ../,__/, $@)
474	@$(CP) $(PCH_H) $(BINARYDIR)/$(PCH_H)
475endif
476
477ifneq ($(PCH_X_H),)
478#C++预编译头文件的生成规则 
479$(BINARYDIR)/$(PCH_X) : $(PCH_X_H)
480	@$(MKDIR) -p $(subst ../,__/, $(dir $@))
481	@$(ECHO) Precompiled C++ header $<
482	@$(CXX) $(CXXFLAGS) $> $^ -o $(subst ../,__/, $@)
483	@$(CP) $(PCH_X_H) $(BINARYDIR)/$(PCH_X_H)
484endif
485
486ifndef NODEP  
487ifneq ($(DEPS),)  
488sinclude $(DEPS)  
489endif  
490endif
491
492#生成规则结束
493
494help:
495	@$(ECHO) "C/C++ general Makefile V1.3"
496	@$(ECHO) "Copyright(C) 2017     E_Mail:witton@163.com by Witton Bell"
497	@$(ECHO) "Usage:make [Target]"
498	@$(ECHO) "Target:"
499	@$(ECHO) "     all          compile and link to target directory(debug or release)"
500	@$(ECHO) "     clean        remove target directory(debug or release)"
501	@$(ECHO) "     cleandeps    remove depends directory(.deps)"
502	@$(ECHO) "     distclean    run target clean and cleandeps"
503	@$(ECHO) "     rebuild      run target clean and all"
504	@$(ECHO) "     help         show this help"```
505
506
507
508
509
510 最终的运行结果如下图所示:
511
512
513![](../assets/2017-02-23-C_C++通用Makefile/20170303195422399)
514
515
516
517
518祝玩得开心!