C/C++通用Makefile
最近的项目又回到了Linux上运行,这就需要在Linux下编译项目,写Makefile针对习惯了Windows的程序员来说是一件痛苦的事,如果有一个通用的Makefile该多好啊,本着这样的目的,我再次研究了一下Makefile,写出了一个实用的通用Makefile,该Makefile在Windows以及Linux平台下作了一些简单测试,未发现问题,如果大家在使用过程中发现有问题可以联系我。话不多说,直接上代码:
###############################################################################
#
# C/C++通用Makefile
#
# 作者:Witton Bell E_Mail:witton@163.com
#
# 版本V1.0 (2017/02/23)
# 初始版本,可以生成App,Share以及Static库等等
# 版本V1.1 (2017/02/28)
# 添加预预编译头的支持,将生成的中间文件放在debug或者release目录下
# 版本V1.2 (2017/03/12)
# 优化以及fixed:修改头文件后,不会编译
#
# 一个项目只需要在顶层目录配置一个Makefile,不需要各个目录单独配置,简单方便实用
# 原文链接:http://blog.csdn.net/witton/article/details/56670748
# 最新文件下载地址:https://code.csdn.net/snippets/2259925
#
# 功能说明:
# 1.支持Windows以及Linux下的可执行文件(App)、动态库(Share)以及静态库(Static)的生成
# 2.支持生成Map文件,Bin文件以及十六进制文件
# 3.支持C/C++的预编译头,以加快编译速度,包括同时存在C与C++预编译头的情况
# 4.生成的中间以及目标代码会指定存放在debug或者release目录下
# 5.支持生成进度显示
#
############################################################################### # 可以修改的区域 # 目标文件的名字(必须指定)
TARGETNAME := test #目标类型可以是APP, STATIC或者SHARED
TARGETTYPE := APP #配置类型可以是Debug或者Release
CONFIG ?= Debug # C 预编译头文件,支持带路径
PCH_H =
# C++ 预编译头文件,支持带路径
PCH_X_H = #源文件目录列表,可以填多个目录,如:src1 src2 ../src
#如果没填写则默认为当前目录
SRC_ROOT_DIRS := #排除的目录列表,需要带上相对路径
EXCLUDE_DIRS := #排除的文件列表,需要带上相对路径
EXCLUDE_FILES := #头文件所在目录列表
INCLUDE_DIRS :=
#库文件所在目录列表
LIBRARY_DIRS :=
#公共库文件名列表
LIBRARY_NAMES :=
#公共宏定义
PREPROCESSOR_MACROS := #各种编译链接参数
#汇编编译参数
ASFLAGS := -f win64
#ld链接参数
LDFLAGS := -Wl,-gc-sections
#公共编译参数,根据情况作修改
COMMONFLAGS := -g -ffunction-sections
#额外的库文件
EXTERNAL_LIBS := #工具集,Windows下最好是填写绝对路径,并且路径中需要使用/分隔
CC := g:/TDM-GCC-64/bin/gcc
CXX := g:/TDM-GCC-64/bin/g++
LD := $(CXX)
ASM := "G:/Program Files (x86)/nasm/nasm.exe"
AR := ar
OBJCOPY := objcopy
CP := cp
MKDIR = mkdir
RM = rm -rf
ECHO = echo
SHELL = /bin/sh #根据配置类型填写不同的参数
ifeq ($(CONFIG), Debug)
PREPROCESSOR_MACROS += DEBUG
LIBRARY_NAMES +=
ADDITIONAL_LINKER_INPUTS :=
MACOS_FRAMEWORKS :=
LINUX_PACKAGES :=
CFLAGS := -O0
CXXFLAGS := -O0
else
PREPROCESSOR_MACROS +=
LIBRARY_NAMES +=
ADDITIONAL_LINKER_INPUTS :=
MACOS_FRAMEWORKS :=
LINUX_PACKAGES :=
CFLAGS := -O3
CXXFLAGS := -O3
endif #需要编译的源文件的扩展名列表
SRC_EXTS := .c .cpp .cc .cxx .c++ .s .S .asm #如果是在Linux下编译,需要打开此开关
#IS_LINUX_PROJECT := 1 #如果需要生成Map文件,需要打开此开关
#GENERATE_MAP_FILE := 1 #如果生成Bin文件,需要打开此开关
#GENERATE_BIN_FILE := 1 #如果生成十六进制文件,需要打开此开关
#GENERATE_IHEX_FILE := 1 #是否显示详细的命令行
#SHOW_CMD_DETAIL := 1 #可修改区域结束,以下区域不建议修改,除非特别了解其含义 #======================华丽的分割线============================= #小写函数
to_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)))))))))))))))))))))))))) BINARYDIR := $(call to_lowercase,$(CONFIG)) ifeq ($(BINARYDIR),)
error:
$(error Invalid configuration, please check your inputs)
endif #如果没配置源文件目录,则默认为当前目录
ifeq ($(SRC_ROOT_DIRS),)
SRC_ROOT_DIRS = .
endif define walk
$(wildcard $(addprefix $(1)/*, $(SRC_EXTS))) $(foreach e, $(wildcard $(1)/*), $(call walk, $(e)))
endef #匹配所有源文件
SOURCEFILES := $(foreach e, $(SRC_ROOT_DIRS), $(call walk, $(e)))
#匹配所有需要排除的源文件
EXCLUDE_SOURCE := $(foreach e, $(EXCLUDE_DIRS), $(call walk, $(e)))
EXCLUDE_FILES += $(EXCLUDE_SOURCE)
EXTERNAL_LIBS_COPIED := $(foreach lib, $(EXTERNAL_LIBS),$(BINARYDIR)/$(notdir $(lib))) #设置C预编译相关变量
ifneq ($(PCH_H),)
PCH = $(PCH_H).gch
PCH_FLAGS := -Winvalid-pch -include $(BINARYDIR)/$(PCH_H)
PCH_FILE := $(BINARYDIR)/$(PCH)
endif #设置C++预编译相关变量
ifneq ($(PCH_X_H),)
PCH_X = $(PCH_X_H).gch
PCH_X_FLAGS := -Winvalid-pch -include $(BINARYDIR)/$(PCH_X_H)
PCH_X_FILE := $(BINARYDIR)/$(PCH_X)
endif START_GROUP := -Wl,--start-group
END_GROUP := -Wl,--end-group
INCLUDE_DIRS += . #处理头文件目录
CFLAGS += $(addprefix -I,$(INCLUDE_DIRS))
CXXFLAGS += $(addprefix -I,$(INCLUDE_DIRS)) #处理宏定义
CFLAGS += $(addprefix -D,$(PREPROCESSOR_MACROS))
CXXFLAGS += $(addprefix -D,$(PREPROCESSOR_MACROS))
ASFLAGS += $(addprefix -D,$(PREPROCESSOR_MACROS)) #处理框架
CFLAGS += $(addprefix -framework ,$(MACOS_FRAMEWORKS))
CXXFLAGS += $(addprefix -framework ,$(MACOS_FRAMEWORKS))
LDFLAGS += $(addprefix -framework ,$(MACOS_FRAMEWORKS)) #处理库目录
LDFLAGS += $(addprefix -L,$(LIBRARY_DIRS)) CFLAGS += $(COMMONFLAGS)
CXXFLAGS += $(COMMONFLAGS)
LDFLAGS += $(COMMONFLAGS) ifeq ($(GENERATE_MAP_FILE),1)
LDFLAGS += -Wl,-Map=$(BINARYDIR)/$(basename $(TARGETNAME)).map
endif LIBRARY_LDFLAGS = $(addprefix -l,$(LIBRARY_NAMES)) ifeq ($(IS_LINUX_PROJECT),1) ifeq ($(TARGETTYPE),SHARED)
TempName = $(addsuffix .so,$(basename $(TARGETNAME)))
TARGETNAME := $(TempName)
endif RPATH_PREFIX := -Wl,--rpath='$$ORIGIN/../
LIBRARY_LDFLAGS += $(EXTERNAL_LIBS)
LIBRARY_LDFLAGS += -Wl,--rpath='$$ORIGIN'
LIBRARY_LDFLAGS += $(addsuffix ',$(addprefix $(RPATH_PREFIX),$(dir $(EXTERNAL_LIBS)))) #如果是Linux下的共享库(Share)项目,则需要添加-fPIC参数,以实现位置无关代码
ifeq ($(TARGETTYPE),SHARED)
CFLAGS += -fPIC
CXXFLAGS += -fPIC
ASFLAGS += -fPIC
LIBRARY_LDFLAGS += -Wl,-soname,$(TARGETNAME)
endif ifneq ($(LINUX_PACKAGES),)
PACKAGE_CFLAGS := $(foreach pkg,$(LINUX_PACKAGES),$(shell pkg-config --cflags $(pkg)))
PACKAGE_LDFLAGS := $(foreach pkg,$(LINUX_PACKAGES),$(shell pkg-config --libs $(pkg)))
CFLAGS += $(PACKAGE_CFLAGS)
CXXFLAGS += $(PACKAGE_CFLAGS)
LIBRARY_LDFLAGS += $(PACKAGE_LDFLAGS)
endif
else
LIBRARY_LDFLAGS += $(EXTERNAL_LIBS) ifeq ($(TARGETTYPE),APP)
TempName = $(addsuffix .exe,$(basename $(TARGETNAME)))
TARGETNAME := $(TempName)
endif ifeq ($(TARGETTYPE),SHARED)
TempName = $(addsuffix .dll,$(basename $(TARGETNAME)))
TARGETNAME := $(TempName)
endif endif LIBRARY_LDFLAGS += $(ADDITIONAL_LINKER_INPUTS) #静态库都是以.a为后缀
ifeq ($(TARGETTYPE),STATIC)
TempName = $(addsuffix .a,$(basename $(TARGETNAME)))
TARGETNAME := $(TempName)
endif ifeq ($(STARTUPFILES),)
all_source_files := $(SOURCEFILES)
else
all_source_files := $(STARTUPFILES) $(filter-out $(STARTUPFILES),$(SOURCEFILES))
endif define HandlePath
$(foreach x,$(foreach x,$(foreach x,$(subst //,/,$(1)),$(subst ../,*/,$(x))),$(subst ./,,$(x))),$(subst */,../,$(x)))
endef AllSource := $(call HandlePath, $(all_source_files))
AllExcludeSource := $(call HandlePath, $(EXCLUDE_FILES)) Source := $(filter-out $(AllExcludeSource),$(AllSource)) CompileObjs := $(foreach x,$(SRC_EXTS),$(patsubst %$(x),%.o,$(filter %$(x),$(Source))))
all_objs := $(foreach x,$(CompileObjs),$(BINARYDIR)/$(x)) ASM_EXT := .s .S .asm
CompileCObjs := $(foreach x,$(filter-out $(ASM_EXT),$(SRC_EXTS)),$(patsubst %$(x),%.d,$(filter %$(x),$(Source))))
all_Cobjs := $(foreach x,$(CompileCObjs),$(BINARYDIR)/$(x)) DEPS := $(all_Cobjs:.o=.d) revert0 = $(2) $(1)
define revert
$(foreach x,$(DEPS),$(eval TempStep = $(call revert0, $(TempStep),$(x)))) $(TempStep)
endef AllStep := $(call revert,$(DEPS))
AllStep += $(CompileObjs) WordNum := $(words $(AllStep)) ProgressInfo := $(foreach x,$(AllStep),$(eval Counter += A)$(addsuffix .$(words $(Counter)), $(basename $(x)))) define FindProgress
$(foreach x,$(ProgressInfo),$(if $(filter $(basename $(x)),$(1)),$(subst .,,$(suffix $(x))),))
endef define ShowProgress
$(strip $(call FindProgress,$(basename $(1))))/$(WordNum)
endef IS_GCC_ASM :=
ifneq ($(filter $(ASM),$(CXX)),)
IS_GCC_ASM = 1
else
ifneq ($(filter $(ASM),$(CC)),)
IS_GCC_ASM = 1
endif
endif ifeq ($(IS_GCC_ASM),1)
CompileA := $(ASM) $(CFLAGS) $(ASFLAGS) -c
else
CompileA := $(ASM) $(ASFLAGS)
endif CompileC := $(CC) $(PCH_FLAGS) $(CFLAGS) -c
CompileCXX := $(CXX) $(PCH_X_FLAGS) $(CXXFLAGS) -c ifneq ($(SHOW_CMD_DETAIL),)
define CompileSrc
@$(MKDIR) -p $(BINARYDIR)/$(subst ../,__/, $(dir $(1)))
@$(ECHO) -n [$(call ShowProgress,$(2))]
$(3) $(2) -o $(subst ../,__/, $(1))
endef
else
define CompileSrc
@$(MKDIR) -p $(BINARYDIR)/$(subst ../,__/, $(dir $(1)))
@$(ECHO) [$(call ShowProgress,$(2))] Compile $(2)
@$(3) $(2) -o $(subst ../,__/, $(1))
endef
endif define CompileCDep
@$(MKDIR) -p $(subst ../,__/, $(dir $(1)))
@$(ECHO) [$(call ShowProgress,$(BINARYDIR)/$(2))] Generate $(1)
@$(CC) -MM $(CFLAGS) $(2) > $(BINARYDIR)/temp
@$(ECHO) -n $(subst ./,, $(dir $(1))) > $(1)
@$(CC) -MM $(CFLAGS) $(2) >> $(1)
endef define CompileCXXDep
@$(MKDIR) -p $(subst ../,__/, $(dir $(1)))
@$(ECHO) [$(call ShowProgress,$(BINARYDIR)/$(2))] Generate $(1)
@$(CXX) -MM $(CXXFLAGS) $(2) > $(BINARYDIR)/temp
@$(ECHO) -n $(subst ./,, $(dir $(1))) > $(1)
@$(CXX) -MM $(CXXFLAGS) $(2) >> $(1)
endef PRIMARY_OUTPUTS := ifeq ($(GENERATE_BIN_FILE),1)
PRIMARY_OUTPUTS += $(BINARYDIR)/$(basename $(TARGETNAME)).bin
endif ifeq ($(GENERATE_IHEX_FILE),1)
PRIMARY_OUTPUTS += $(BINARYDIR)/$(basename $(TARGETNAME)).ihex
endif ifeq ($(PRIMARY_OUTPUTS),)
PRIMARY_OUTPUTS := $(BINARYDIR)/$(TARGETNAME)
endif .PHONY: all clean .SUFFIXES: all: $(PRIMARY_OUTPUTS)
@$(RM) $(BINARYDIR)/temp
@$(ECHO) Built OK. $(BINARYDIR)/$(basename $(TARGETNAME)).bin: $(BINARYDIR)/$(TARGETNAME)
@$(OBJCOPY) -O binary $< $(subst ../,__/, $@) $(BINARYDIR)/$(basename $(TARGETNAME)).ihex: $(BINARYDIR)/$(TARGETNAME)
@$(OBJCOPY) -O ihex $< $(subst ../,__/, $@) ifeq ($(TARGETTYPE),APP)
$(BINARYDIR)/$(TARGETNAME): $(all_objs) $(EXTERNAL_LIBS)
@$(ECHO) Link App $(subst ../,__/, $@)
@$(LD) -o $(subst ../,__/, $@) $(LDFLAGS) $(START_GROUP) $(subst ../,__/, $(all_objs)) $(LIBRARY_LDFLAGS) $(END_GROUP)
endif ifeq ($(TARGETTYPE),SHARED)
$(BINARYDIR)/$(TARGETNAME): $(all_objs) $(EXTERNAL_LIBS)
@$(ECHO) Link Share lib $(subst ../,__/, $@)
@$(LD) -shared -o $(subst ../,__/, $@) $(LDFLAGS) $(START_GROUP) $(subst ../,__/, $(all_objs)) $(LIBRARY_LDFLAGS) $(END_GROUP)
endif ifeq ($(TARGETTYPE),STATIC)
$(BINARYDIR)/$(TARGETNAME): $(all_objs)
@$(ECHO) Link Static lib $(subst ../,__/, $@)
@$(AR) -r $(subst ../,__/, $@) $(subst ../,__/, $^)
endif clean:
@$(RM) $(BINARYDIR) $(BINARYDIR):
@$(MKDIR) $(BINARYDIR) #Makefile的生成规则
#生成依赖文件
$(BINARYDIR)/%.d : %.c
$(call CompileCDep,$@,$<) $(BINARYDIR)/%.d : %.cpp
$(call CompileCXXDep,$@,$<) $(BINARYDIR)/%.d : %.cxx
$(call CompileCXXDep,$@,$<) $(BINARYDIR)/%.d : %.cc
$(call CompileCXXDep,$@,$<) $(BINARYDIR)/%.d : %.c++
$(call CompileCXXDep,$@,$<) #C文件的生成规则
$(BINARYDIR)/%.o : %.c $(PCH_FILE)
$(call CompileSrc,$@,$<,$(CompileC)) #C++的.cpp文件的生成规则
$(BINARYDIR)/%.o : %.cpp $(PCH_X_FILE)
$(call CompileSrc,$@,$<,$(CompileCXX)) #C++的.cc文件的生成规则
$(BINARYDIR)/%.o : %.cc $(PCH_X_FILE)
$(call CompileSrc,$@,$<,$(CompileCXX)) #C++的.cxx文件的生成规则
$(BINARYDIR)/%.o : %.cxx $(PCH_X_FILE)
$(call CompileSrc,$@,$<,$(CompileCXX)) #C++的.c++文件的生成规则
$(BINARYDIR)/%.o : %.c++ $(PCH_X_FILE)
$(call CompileSrc,$@,$<,$(CompileCXX)) #Asm的.S文件生成规则
$(BINARYDIR)/%.o : %.S
$(call CompileSrc,$@,$<,$(CompileA)) #Asm的.s文件生成规则
$(BINARYDIR)/%.o : %.s
$(call CompileSrc,$@,$<,$(CompileA)) #Asm的.asm文件生成规则
$(BINARYDIR)/%.o : %.asm
$(call CompileSrc,$@,$<,$(CompileA)) ifneq ($(PCH_H),)
#C预编译头文件的生成规则
$(BINARYDIR)/$(PCH) : $(PCH_H)
@$(MKDIR) -p $(subst ../,__/, $(dir $@))
@$(ECHO) Precompiled C header $<
@$(CC) $(CFLAGS) $> $^ -o $(subst ../,__/, $@)
@$(CP) $(PCH_H) $(BINARYDIR)/$(PCH_H)
endif ifneq ($(PCH_X_H),)
#C++预编译头文件的生成规则
$(BINARYDIR)/$(PCH_X) : $(PCH_X_H)
@$(MKDIR) -p $(subst ../,__/, $(dir $@))
@$(ECHO) Precompiled C++ header $<
@$(CXX) $(CXXFLAGS) $> $^ -o $(subst ../,__/, $@)
@$(CP) $(PCH_X_H) $(BINARYDIR)/$(PCH_X_H)
endif ifndef NODEP
ifneq ($(DEPS),)
sinclude $(DEPS)
endif
endif #生成规则结束
最终的运行结果如下图所示:
祝玩得开心!
C/C++通用Makefile的更多相关文章
- 一个简单的通用Makefile实现
一个简单的通用Makefile实现 Makefile是Linux下程序开发的自动化编译工具,一个好的Makefile应该准确的识别编译目标与源文件的依赖关系,并且有着高效的编译效率,即每次重新ma ...
- 通用Makefile
本文推荐了一个用于对 C/C++ 程序进行编译和连接以产生可执行程序的通用 Makefile. 在使用 Makefile 之前,只需对它进行一些简单的设置即可:而且一经设置,即使以后对源程序文件有所增 ...
- Makefile之写demo时的通用Makefile写法
Makefile之写demo时的通用Makefile写法[日期:2013-05-22] 来源:CSDN 作者:gqb666 [字体:大 中 小] 前面的一篇文章Makefile之大型工程项目子目录M ...
- linux下通用Makefile写法
linux编译多个源文件的程序比较麻烦,这下就需要通用的Makefile了,编译的时候执行一下make命令就OK,下面介绍通用makfile的写法. 假设现在有以下源文件:file1.h file1. ...
- 一个通用Makefile的编写
作者:杨老师,华清远见嵌入式学院讲师. 我们在Linux环境下开发程序,少不了要自己编写Makefile,一个稍微大一些的工程下面都会包含很多.c的源文件.如果我们用gcc去一个一个编译每一个源文件的 ...
- 【linux】-Makefile简要知识+一个通用Makefile
目录 Makefile Makefile规则与示例 为什么需要Makefile Makefile样式 先介绍Makefile的两个函数 完善Makefile 通用Makefile的使用 通用的Make ...
- 一个通用Makefile详解
我们在Linux环境下开发程序,少不了要自己编写Makefile,一个稍微大一些的工程下面都会包含很多.c的源文 件. 如果我们用gcc去一个一个编译每一个源文件的话,效率会低很多,但是如果我们可以写 ...
- 4通用Makefile编写
a.c #include<stdio.h> #include "a.h" int main() { printf("hello world\n"); ...
- Linux下C++的通用Makefile与解析
本文给出万能Makefile的具体实现,以及对其中的关键点进行解析.所谓C++万能Makefile,即可编译链接所有的C++程序,而只需作很少的修改. 号称万能Makefile,一统江湖.我对原版的M ...
随机推荐
- python os模块用法
import os #os主要做路径管理import glob #glob主要做搜索查询匹配import sys inputpath = r"C:\Users\Administrato ...
- Codeforces 1175E 倍增
题意:给你n个区间和m次询问,每次询问一个区间[l, r]至少需要几个区间覆盖? 思路:如果只有一个区间,并且区间是整个取值范围的话,这是一个经典的区间覆盖问题,我们都知道贪心可以解决.现在我们需要快 ...
- 8、Python MySQL - mysql-connector 驱动
一. 在线安装 mysql-connector : pip install mysql-connector-python 二.操作流程 1.连接数据库信息: conn = mysql.connec ...
- C之输入输出函数(1) -- fgets()
https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_71/rtref/fgets.htm #include <stdio.h> ...
- WebStorm编辑器
Webstorm的安装和免费使用 IntelliJ IDEA 注册码 (webstorm注册码):http://idea.lanyus.com/ (已经不能获取注册码了,之前的注册码可以重复使用) 安 ...
- 跨域解决方法之window.name
window对象有一个name属性,该属性有一个特征:即在一个窗口的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每一个页面对window.name都有读写的权限,window ...
- [NOIP模拟23]题解
中间鸽了好几篇啊QAQ……有时间再补吧…… A.mine sbdp,考场上写的巨麻烦不过还是能A的(虽然MLE了……每一维都少开1就A掉了555).设$dp[i][j][k]$为枚举到第i位,第i位是 ...
- C语言新手写扫雷源代码
今天发布源代码,由于写在一个文件里非常乱,所以分三个文件写 绘图和鼠标函数graph.h /*绘图与鼠标相关函数*/ #include<graphics.h> #include <e ...
- zip mysql安装启动方式
首先在官网(https://dev.mysql.com/downloads/mysql/)下载相应的zip包 然后进行解压找到配置文件 my-default.ini 文件打开进行配置 主要配置以下几项 ...
- 关于RF做自动化大致流程的梳理
RF只是一个框架,类似于单元测试框架,可以实现对用例的有效管理.结合其它第三方库,可以进行,接口,数据库,APP的自动化测试.结合JENKINS,还可以进行有效的持续集成. 本文不讲调用第三方库的哪些 ...