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.