Android with root Git for version control Lircd with Raspberry Pi for IR receiver and sender Tips for Windows Depolying your own password management tool -- KeeWeb Depoly your flask app into Heroku Fix shit IE code manually ISBN to Book Category by Scraping DangDang A Generic Makefile for C/C++ Program Configure Raspberry pi Remove watermark with PyPDF2 tips for docker Anaconda+TensorFlow+CUDA Snippets Configure Remote Mathematica Kernel Build your own ngrok server Access Array SSL VPN 使用Rstudio制作html5幻灯片 tips for Mac OS X system Tips for ipython notebook 配置Ubuntu server + Openbox (Obuntu) tips for Vimperator tips for Vim 安装CUDA My First Jekyll Blog rsync常见选项 在Linux中读取Ipod touch的文件 tip for texmacs 在VPS上建站的一些tip Gnuplot绘图札记 Samba系统和autofs自动挂载 Linux中alsamixer声卡无法录音 搭建自己的RSS订阅器——Tiny Tiny RSS Grub2引导安装Ubuntu awk tips 将Ubuntu系统装入U盘 The Great Rtorrent 编译GCC 再这样剁手!!!该死的libgd 使用ulimit进行资源限制 使用SSH代理上IPV6 使用RCurl抓取网页数据 修复Ubuntu Grub记 openbox中的文件关联 在Ubuntu 12.04下编译qtiplot 处理BCM4312网卡驱动纪实 配置我的Ubuntu Server记 Cygwin杂记 Linux 使普通用户具有以超级权限执行脚本 让firefox自定义地处理文件类型 WordPress优秀主题及插件 在phpcloud上搭建wordpress UBUNTU下用pptpd做VPN server ubuntu升级内核过后的一些问题 安装telnet服务 kubuntu札记 64位kubuntu札记 统计软件R Virtualbox stardict星际译王 Ubuntu重装windows系统后的grub引导修复 SSH服务及花生壳域名解析 采用cbp2make工具由code::blocks工程创建makefile文件 UBUNTU 札记

A Generic Makefile for C/C++ Program

2018年08月02日

I used to work a lot on scientific computing with C/C++, in which compiling the source code could be quite inconvenient. After some iteration, I came up with following Makefile. Just drop it into a source code directory and make.

Following is a simple adjustable Makefile which should locate inside your code directory.

##==========================================================================
## 需要调整的项
##==========================================================================
# 可执行文件名称
APP =

# 编译模式: Debug, Release 
MODE = Release
#MODE = Debug

# 使用GPU或OMP
#USEOMP=true
USEOMP=false

USEGPU=true
#USEGPU=false

#NORAND=true
NORAND=false

# 宏定义
MACRO_DEFINE= -D _ElemType_=double

# 源文件目录
SRC_DIRS = .

# 需要指明同步的文件后缀
FILETYPE = Makefile .R .r .sh
# 同步选项
upload: SYNC_OPTIONS_UPLOAD= --exclude=*/.hg/
download: SYNC_OPTIONS_DOWNLOAD= --exclude=*/.hg/ --include=*.dat 

-include $(shell echo $$HOME)/.local/include/Makefile.in

Put following generic Makefile in to a location like $HOME/.local/include/.

# Generic Makefile for C/C++ Program

###==========================================================================
### 需要调整的项
###==========================================================================
## 可执行文件名称
#APP =

## 需要指明同步的文件后缀
#FILETYPE = Makefile .R .r .sh

## 编译模式: Debug, Release 
#MODE = Release
##MODE = Debug

## 使用GPU或OMP
##USEOMP=true
#USEOMP=false

#USEGPU=true
##USEGPU=false

##NORAND=true
#NORAND=false

## 宏定义
#MACRO_DEFINE= -D _ElemType_=double

## 源文件目录
#SRC_DIRS = .

## 同步选项
#upload: SYNC_OPTIONS_UPLOAD= --exclude=*/.hg/
#download: SYNC_OPTIONS_DOWNLOAD= --exclude=*/.hg/ --include=*.dat 

#-include $(shell echo $$HOME)/.local/include/Makefile
##==========================================================================
## 不常修改的项
##==========================================================================
# Rsync 同步选项
SYNC_OPTIONS+= -amz -v -b -h --progress --backup-dir=backup.bak -Lpt -u --partial
SSH_HOST_ALL=$(shell awk -F: '/^SSH_HOST_ALL= /{$$1="";$$NF="";print $$0}' $$HOME/.myrc)
SSH_HOST=$(shell awk '/^SSH_HOST= /{print $$2}' $$HOME/.myrc)
SSH_HOST_DIR=$(shell echo $(SSH_HOST_ALL) | \
	     awk 'BEGIN{RS=" "} \
	         /$(SSH_HOST)/{\
		    match($$0,"{.*}");\
		    print substr($$0,RSTART+1,RLENGTH-2)}')

# 预处理选项
CPPFLAGS += 

# CUDA 选项 计算能力
GENCODE_SM10    = -gencode arch=compute_10,code=sm_10
GENCODE_SM13    = -gencode arch=compute_13,code=sm_13
GENCODE_SM20    = -gencode arch=compute_20,code=sm_20
GENCODE_SM30    = -gencode arch=compute_30,code=sm_30 -gencode arch=compute_35,code=sm_35
GENCODE_FLAGS   = $(GENCODE_SM20) 

# OS-specific build flags
OS_SIZE = $(shell uname -m | sed -e "s/i.86/32/" -e "s/x86_64/64/")

# CUDA位置
CUDA_PATH       ?= /usr/local/cuda-5.5
ifeq ($(OS_SIZE),32)
    CUDA_LIB_PATH   ?= $(CUDA_PATH)/lib
else
    CUDA_LIB_PATH   ?= $(CUDA_PATH)/lib64
endif

# 需要额外添加的库
LIBS +=
CUDA_LIBS   += -I$(CUDA_PATH)/include -I. -I$(CUDA_PATH)/samples/0_Simple -I$(CUDA_PATH)/samples/common/inc

# 需要指明的依赖
DEPS_MORE += $(firstword $(MAKEFILE_LIST))

# 链接选项
LDFLAGS += -lncurses -lrt
CUDA_LDFLAGS   += -L$(CUDA_LIB_PATH) -lcudart -lcurand

# 源文件后缀名
SRCEXTS += .c .C .cc .cpp .CPP .c++ .cxx .cp
# 头文件后缀名
HDREXTS += .h .H .hh .hpp .HPP .h++ .hxx .hp
ifeq ($(USEGPU),true) # CUDA program
SRCEXTS += .cu
HDREXTS += .cuh
endif

# 编译选项: -pg 生成性能测试报表; -coverage 生成代码覆盖报表; -openmp intel C 编译器使用的OPENMP编译选项
CFLAGS += -Wall -O2
NVCCFLAGS += -O2
ifeq ($(MODE),Debug)
CFLAGS := $(filter-out -O1 -O2 -O3, $(CFLAGS))
NVCCFLAGS := $(filter-out -O1 -O2 -O3, $(NVCCFLAGS))
endif
CXXFLAGS = $(CFLAGS)

# 调试选项 -g, -g2, -g3
DEBUG_FLAG ?= -g
DEBUG_NVCC_FLAG ?= -g -G

# std flag Options: -std=c99,-std=iso9899:199409, -std=c89
STD_FLAG ?= -std=c++0x

# The C program compiler.
CC := gcc

# The C++ program compiler.
CXX ?= g++

# The CUDA program compiler.
NVCC ?= $(CUDA_PATH)/bin/nvcc

# 打开此选项后以C++格式编译C程序
#CC = $(CXX)

# 删除文件的命令
RM ?= rm -f

# ctags命令及选项
CTAGS ?= ctags
CTAGSFLAGS +=-R --c++-kinds=+p --langmap=c++:+.hpp.cu.cuh --fields=+iaS --extra=+q  

# nvprof命令及选项
NVPROF ?= nvprof --profile-from-start-off

##==========================================================================
# 对未指定选项做一些处理
##==========================================================================
ifeq ($(USEGPU),true) #GPU不能与OMP混用
USEOMP=false
endif

ifeq ($(USEOMP),true) 
ifeq ($(CC),gcc)
CFLAGS += -fopenmp
else
CFLAGS += -openmp
endif
endif

#-设置可执行文件名-#
EMPTY =
SPACE = $(EMPTY) $(EMPTY)
CUR_PATH_NAMES = $(subst /,$(SPACE),$(subst $(SPACE),_,$(CURDIR)))
DIR_NAME = $(word $(words $(CUR_PATH_NAMES)),$(CUR_PATH_NAMES))
ifeq ($(DIR_NAME),)
    DIR_NAME = a
endif
APP := $(strip $(APP))
ifeq ($(APP),)
    APP = $(DIR_NAME)
endif
ifeq ($(OS),Windows_NT)
    APP := $(APP).exe
else
    APP := $(APP).out
endif

#设置系统字长
ifeq ($(OS_SIZE),32)
    NVCCFLAGS   += -m32
else
    NVCCFLAGS   += -m64
endif

#-DEBUG模式下添加选项-#
ifeq ($(MODE),Debug)
    CFLAGS += $(DEBUG_FLAG)
    NVCCFLAGS += $(DEBUG_NVCC_FLAG)
    MACRO_DEFINE+= -D THRUST_DEBUG
else
    MACRO_DEFINE+= -D NDEBUG
endif

#-源文件目录-#
ifeq ($(SRC_DIRS),)
    SRC_DIRS = .
endif

#-添加std选项-#
ifneq ($(STD_FLAG),)
    CFLAGS += $(STD_FLAG)
endif

#宏定义
MACRO_DEFINE += -D GPU=$(USEGPU) -D OPENMP=$(USEOMP) -D NORAND=$(NORAND)
##==========================================================================
# 下面的基本不要修改,自动化执行
##==========================================================================
# foreach 对每一个后缀名做wildcard(展开通配符得到实际的源文件)处理 
FILETYPE += $(SRCEXTS) $(HDREXTS)
SOURCES = $(foreach d,$(SRC_DIRS),$(wildcard $(addprefix $(d)/*,$(SRCEXTS))))
HEADERS = $(foreach d,$(SRC_DIRS),$(wildcard $(addprefix $(d)/*,$(HDREXTS))))
SYNC_INCLUDE = $(foreach d,$(FILETYPE),--include=*$(d))
SRC_CXX = $(filter-out %.c %.cu,$(SOURCES))
SRC_CUDA = $(filter %.cu,$(SOURCES))
OBJS = $(join $(dir $(SOURCES)), $(addprefix ., $(addsuffix .o, $(basename $(notdir $(SOURCES)) ))))
DEPS = $(join $(dir $(SOURCES)), $(addprefix ., $(addsuffix .d, $(basename $(notdir $(SOURCES)) ))))

COMPILE_c = $(CC) $(CPPFLAGS) $(CFLAGS) $(LIBS) $(MACRO_DEFINE) -c
COMPILE_cxx = $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LIBS) $(MACRO_DEFINE) -c
COMPILE_cuda = $(NVCC) $(NVCCFLAGS) $(GENCODE_FLAGS) $(CUDA_LIBS) $(MACRO_DEFINE) -c
LINK_c = $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS)
LINK_cxx = $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS)
PRODUCTS = $(APP) $(OBJS) $(DEPS) 

#==================================================================
.PHONY: all objs clean distclean garbageclean rebuild

# Delete the default suffixes
.SUFFIXES:

all: $(APP)

# 生成依赖文件(.d).
.%.d:%.c
	@$(CC) -MM -MT $(basename $@).o -MF $@ $(CFLAGS) $<
                                 
.%.d:%.C                         
	@$(CC) -MM -MT $(basename $@).o -MF $@ $(CXXFLAGS) $<
                                 
.%.d:%.cc                        
	@$(CC) -MM -MT $(basename $@).o -MF $@ $(CXXFLAGS) $<
                                 
.%.d:%.cpp                       
	@$(CC) -MM -MT $(basename $@).o -MF $@ $(CXXFLAGS) $<
                                 
.%.d:%.CPP                       
	@$(CC) -MM -MT $(basename $@).o -MF $@ $(CXXFLAGS) $<
                                 
.%.d:%.c++                       
	@$(CC) -MM -MT $(basename $@).o -MF $@ $(CXXFLAGS) $<
                  
.%.d:%.cp         
	@$(CC) -MM -MT $(basename $@).o -MF $@ $(CXXFLAGS) $<
                                           
.%.d:%.cxx                                 
	@$(CC) -MM -MT $(basename $@).o -MF $@ $(CXXFLAGS) $<
.%.d:%.cu
	@$(CC) -MM -MT $(basename $@).o -MF $@ -x c++ $(CXXFLAGS) $<


# 生成目标文件(.o).
objs:$(OBJS)

.%.o:%.c $(DEPS_MORE)
	$(COMPILE_c) $< -o $@
.%.o:%.C $(DEPS_MORE)
	$(COMPILE_cxx) $< -o $@
.%.o:%.cc $(DEPS_MORE)
	$(COMPILE_cxx) $< -o $@
.%.o:%.cpp $(DEPS_MORE)
	$(COMPILE_cxx) $< -o $@
.%.o:%.CPP $(DEPS_MORE)
	$(COMPILE_cxx) $< -o $@
.%.o:%.c++ $(DEPS_MORE)
	$(COMPILE_cxx) $< -o $@
.%.o:%.cp $(DEPS_MORE)
	$(COMPILE_cxx) $< -o $@
.%.o:%.cxx $(DEPS_MORE)
	$(COMPILE_cxx) $< -o $@
.%.o:%.cu $(DEPS_MORE)
	$(COMPILE_cuda) $< -o $@

# 链接成可执行文件
$(APP):$(OBJS)
ifeq ($(SRC_CUDA),) # C/C++ program
ifeq ($(SRC_CXX),) # C program
	$(CC) $(CPPFLAGS) $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS) 
else # C++ program
	$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(OBJS)  -o $@ $(LDFLAGS)
endif
else # CUDA program
	$(NVCC) $(NVCCFLAGS) $(OBJS) -o $@ $(LDFLAGS) $(CUDA_LDFLAGS)
endif
	@echo
	@echo Type ./$@ to execute the program.
ifeq ($(NORAND),true)
	@echo "========================================"
	@echo "       Waring: no rand mode !!!"
	@echo "========================================"
endif

# 包含进文件依赖
-include $(DEPS)

clean:
	$(RM) $(OBJS) $(APP)

distclean:
	$(RM) $(PRODUCTS)

garbageclean:
	$(RM) *.o *.save *.gcov *.gcda *.gcno *.orig *.d tags gmon.out   

rebuild: clean all 


#######################################
#          常用函数
######################################
.PHONY: ctags profiler excute vim tar download upload help show binary-name
# 生成ctags
ctags: $(HEADERS) $(SOURCES)
	$(CTAGS) $(CTAGSFLAGS) $(HEADERS) $(SOURCES)

# 性能分析
profiler: $(APP)
	$(NVPROF) ./$(APP)

excute:$(APP)
	@mkdir -p data
	@time ./$(APP) | tee data/output.dat

# 用vim打开所有文件
vim: $(HEADERS) $(SOURCES)
	vim $(HEADERS) $(SOURCES)

# 打包所有代码及数据文件
tar: $(SOURCES) $(HEADERS)
	tar -zcvf $(DIR_NAME)$$(date +%F).tar.gz $(SOURCES) $(HEADERS) $(firstword $(MAKEFILE_LIST)) data

download:
	@echo -n $(SSH_HOST_ALL) |\
	    awk '{for(i = 1; i<NF+1; i++) {\
		    sub(/{.*}/,"",$$i);\
		    print i"."$$i;\
		 } \
		 ORS=""; \
		 print "download from...?";}'
	@REMOTE_HOST=$$(echo -n $(SSH_HOST_ALL) |\
	    awk '{\
		getline var < "/dev/tty";\
		host=substr($$var,1,length($$var)-1);\
		sub(/{/,":/",host);\
		print host}');\
	    REMOTE_HOST_DIR=$$(echo $$(pwd) | sed "s#$(SSH_HOST_DIR)#$$REMOTE_HOST#");\
	    rsync $(SYNC_OPTIONS) $(SYNC_OPTIONS_DOWNLOAD) --exclude=*backup.bak/ --include=*/ $(SYNC_INCLUDE) --exclude=* \
	    $$REMOTE_HOST_DIR/ $$(pwd)/

upload:                                                        
	@echo -n $(SSH_HOST_ALL) |\
	    awk '{for(i = 1; i<NF+1; i++) {\
		    sub(/{.*}/,"",$$i);\
		    print i"."$$i;\
		 } \
		 ORS=""; \
		 print "upload to...?";}'
	@REMOTE_HOST=$$(echo -n $(SSH_HOST_ALL) |\
	    awk '{\
		getline var < "/dev/tty";\
		host=substr($$var,1,length($$var)-1);\
		sub(/{/,":/",host);\
		print host}');\
	    REMOTE_HOST_DIR=$$(echo $$(pwd) | sed "s#$(SSH_HOST_DIR)#$$REMOTE_HOST#");\
	    rsync $(SYNC_OPTIONS) $(SYNC_OPTIONS_UPLOAD) --exclude=*backup.bak/ --include=*/ $(SYNC_INCLUDE) --exclude=* \
	    $$(pwd)/ $$REMOTE_HOST_DIR/

help:
	@echo
	@echo "Usage: make [TARGET]"
	@echo "TARGETS:"
	@echo " all   \t  (=make) compile and link."
	@echo " objs   \t  compile only (no linking)."
	@echo " ctags   \t  create ctags for VI editor."
	@echo " clean   \t  clean objects and the executable file."
	@echo " distclean   \t  clean objects and executable ."
	@echo " garbageclean   \t  clean some garbage."
	@echo " excute   \t  excute the program."
	@echo " rebuild   \t  clean and make all again."
	@echo " upload/download   \t  upload or download the files via rsync."
	@echo " show   \t  show variables (for debug use only)."
	@echo " help   \t  print this message."
	@echo " use command 'tar -zcvf ../code.tar.gz ../hg' to tar the source code and untar it with 'tar -xf code.tar.gz'"
	@echo

show:
	@echo
	@echo "OS   \t  :" $(OS)
	@echo "APP   \t  :" $(APP)
	@echo "MODE   \t  :" $(MODE)
	@echo "SRC_DIRS   \t  :" $(SRC_DIRS)
	@echo "HEADERS   \t  :" $(HEADERS)
	@echo "SOURCES   \t  :" $(SOURCES)
	@echo "SRC_CXX   \t  :" $(SRC_CXX)
	@echo "OBJS   \t  :" $(OBJS)
	@echo "COMPILE_c   \t  :" $(COMPILE_c)
	@echo "COMPILE_cxx   \t  :" $(COMPILE_cxx)
	@echo "LINK_c   \t  :" $(LINK_c)
	@echo "LINK_cxx   \t  :" $(LINK_cxx)
	@echo "PRODUCTS   \t  :" $(PRODUCTS)
	@echo "rsync   \t  :" rsync $(SYNC_OPTIONS) --exclude=*/backup.bak/ --include=*/ $(SYNC_INCLUDE) --exclude=* remote://path host://path
	@echo

binary-name:
	@echo $(APP)

There are some drawbacks for this Makefile like only one main function can exist in the code tree.