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.