# Phil's multiplatform makefile template # With auto-incrementing build number and automatic version.h generation # Version 1.4, 2009-01-27 # # The latest version of this Makefile can be found at http://www.philpem.me.uk/ # # # Copyright (c) 2009 Philip Pemberton # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # # Instructions for use: # Run 'make init' to create the required directories # Add your source files to the 'SOURCES' list, and change the TARGET filename # Set the desired build type and platform in the BUILD_TYPE and PLATFORM # variables respectively # Set your project type (C only, or C++) in the SRC_TYPE variable # Add any libraries you need to link against to the 'LIB' list # Run 'make' # # Object files are created in the 'obj' subdirectory, from source code in the # 'src' directory. Dependency files are created in the 'dep' directory from # the same source code the object files are created from. # # Supported targets are: # all Build everything. # update-revision Increment the build number without building anything. # clean-versioninfo Delete src/version.h (will be rebuilt on the next # 'make all'). # init Initialise the build system for a new project. # WARNING: overwrites .buildnum and src/version.h.in! # cleandep Delete all dependency files. # clean Delete all dependency, intermediate and target files. # tidy Delete all dependency and intermediate files, leaving # the target file intact. # # If you want to reset the build number to zero, delete '.buildnum'. This # should be done whenever the major or minor version changes. Excluding # .buildnum from version control may also be a good idea, depending on how # you want your build numbers to work. # # The BUILD_TYPE variable contains the current build type. There are two # supported build types: # debug Debug mode - object files are compiled with debug information # and the target is left unstripped. # release Release mode - object files are not compiled with debug info, # and the target is fed through strip to remove redundant # data. # # The PLATFORM variable contains the current target platform. There are two # supported platforms: # linux GNU/Linux with GNU Compiler Collection # win32 Windows 32-bit with MinGW # # The EXTSRC variable is used to specify other files to build. It is typically # used to specify platform or build-type specific source files, e.g. # # ifeq ($(BUILD_TYPE),debug-memwatch) # CFLAGS += -g -ggdb # CPPFLAGS += -DMEMWATCH # INCPATH += ./memwatch # EXTSRC += memwatch/memwatch.c # endif # # (example taken from one of my projects that allowed the use of Memwatch to # track down memory allocation/deallocation bugs) # #### # Build configuration #### # version information -- major.minor.extra # note that VER_EXTRA can be overridden on the command line, e.g.: # make VER_EXTRA=12345 all VER_MAJOR = 0 VER_MINOR = 0 VER_EXTRA ?= # build platform: win32 or linux PLATFORM ?= linux # build type: release or debug BUILD_TYPE ?= debug # target executable TARGET = test # source files that produce object files SRC = main.cpp # source type - either "c" or "cpp" (C or C++) SRC_TYPE = cpp # additional object files that don't necessarily include source EXT_OBJ = # libraries to link in -- these will be specified as "-l" parameters, the -l # is prepended automatically LIB = # library paths -- where to search for the above libraries LIBPATH = # include paths -- where to search for #include files (in addition to the # standard paths INCPATH = # garbage files that should be deleted on a 'make clean' or 'make tidy' GARBAGE = # extra dependencies - files that we don't necessarily know how to build, but # that are required for building the application; e.g. object files or # libraries in sub or parent directories EXTDEP = #### # Win32 target-specific settings #### ifeq ($(strip $(PLATFORM)),win32) # windows executables have a .exe suffix TARGET := $(addsuffix .exe,$(TARGET)) # console mode application EXT_CFLAGS = -mconsole endif #### # Tool setup #### MAKE = make CC = gcc CXX = g++ CFLAGS = -Wall -pedantic -std=gnu99 $(EXT_CFLAGS) CXXFLAGS= -Wall -pedantic $(EXT_CXXFLAGS) LDFLAGS = $(EXT_LDFLAGS) RM = rm STRIP = strip ############################################################################### # You should not need to touch anything below here, unless you're adding a new # platform or build type (or changing the version string format) ############################################################################### #### # A quick sanity check on the platform type #### ifneq ($(PLATFORM),linux) ifneq ($(PLATFORM),win32) $(error Platform '$(PLATFORM)' not supported. Supported platforms are: linux, win32) endif endif #### # Version info generation #### # get the current build number VER_BUILDNUM = $(shell cat .buildnum) # there are two ways to get the SVN rev - use svnversion, or use svn info # then pipe through awk. which one you use is up to you. VER_SVNREV = $(shell LANG=C svn info 2>/dev/null || echo 'Revision: 0' | awk '/^Revision:/ { print$$2 }' ) #VER_SVNREV = $(shell svnversion .) # if the version string is "exported", then the CSD was not checked out of SVN # note that if the CSD is not an SVN checkout, then @@svnrev@@ will be set to # zero. ifeq ($(VER_SVNREV),exported) VER_SVNREV = 0 endif # start creating the revision string VER_FULLSTR = $(VER_MAJOR).$(VER_MINOR).$(VER_BUILDNUM)$(VER_EXTRA) # if this is an SVN release, include the SVN revision in the version string ifneq ($(VER_SVNREV),0) VER_FULLSTR += (svn $(VER_SVNREV)) endif #### # Build-type specific configuration #### ifeq ($(BUILD_TYPE),debug) CFLAGS += -g -ggdb CXXFLAGS += -g -ggdb else ifeq ($(BUILD_TYPE),release) CFLAGS += -O2 CXXFLAGS += -O2 else $(error Unsupported build type: '$(BUILD_TYPE)') endif endif #### # rules #### # object files OBJ = $(addprefix obj/, $(addsuffix .o, $(basename $(SRC))) $(EXT_OBJ)) $(addsuffix .o, $(basename $(EXTSRC))) # dependency files DEPFILES = $(addprefix dep/, $(addsuffix .d, $(basename $(SRC))) $(EXT_OBJ)) $(addsuffix .d, $(basename $(EXTSRC))) # path commands LIBLNK = $(addprefix -l, $(LIB)) LIBPTH = $(addprefix -L, $(LIBPATH)) INCPTH = $(addprefix -I, $(INCPATH)) CPPFLAGS += $(INCPTH) #### # Make sure there is at least one object file to be linked in #### ifeq ($(strip $(OBJ)),) $(error Unable to build: no object or source files specified in Makefile) endif #### # targets #### .PHONY: default all update-revision versionheader clean-versioninfo init cleandep clean tidy all: update-revision @$(MAKE) versionheader $(MAKE) $(TARGET) # increment the current build number NEWBUILD=$(shell expr $(VER_BUILDNUM) + 1) update-revision: @echo $(NEWBUILD) > .buildnum versionheader: @sed -e 's/@@date@@/$(shell LC_ALL=C date)/g' \ -e 's/@@time@@/$(shell LC_ALL=C date +%T)/g' \ -e 's/@@whoami@@/$(shell whoami)/g' \ -e 's/@@hostname@@/$(shell hostname)/g' \ -e 's|@@compiler@@|$(shell $(CC) $(CFLAGS) -v 2>&1 | tail -n 1 | sed -e "s;|;/;")|g' \ -e 's/@@majorver@@/$(VER_MAJOR)/g' \ -e 's/@@minorver@@/$(VER_MINOR)/g' \ -e 's/@@extraver@@/$(subst \",,$(VER_EXTRA))/g' \ -e 's/@@buildnum@@/$(VER_BUILDNUM)/g' \ -e 's/@@buildtype@@/$(BUILD_TYPE)/g' \ -e 's/@@svnrev@@/$(VER_SVNREV)/g' \ -e 's/@@fullverstr@@/$(VER_FULLSTR)/g' \ -e 's/@@cflags@@/$(CFLAGS)/g' \ < src/version.h.in > src/version.h # version.h creation stuff based on code from the Xen makefile clean-versioninfo: @if [ ! -r src/version.h -o -O src/version.h ]; then \ rm -f src/version.h; \ fi @echo 0 > .buildnum # initialise the build system for a new project init: @mkdir -p src dep obj @echo 0 > .buildnum @echo '#define VER_COMPILE_DATE "@@date@@"' > src/version.h.in @echo '#define VER_COMPILE_TIME "@@time@@"' >> src/version.h.in @echo '#define VER_COMPILE_BY "@@whoami@@"' >> src/version.h.in @echo '#define VER_COMPILE_HOST "@@hostname@@"' >> src/version.h.in @echo '#define VER_COMPILER "@@compiler@@"' >> src/version.h.in @echo '#define VER_BUILD_TYPE "@@buildtype@@"' >> src/version.h.in @echo '#define VER_CFLAGS "@@cflags@@"' >> src/version.h.in @echo '' >> src/version.h.in @echo '#define VER_MAJOR @@majorver@@' >> src/version.h.in @echo '#define VER_MINOR @@minorver@@' >> src/version.h.in @echo '#define VER_BUILDNUM @@buildnum@@' >> src/version.h.in @echo '#define VER_EXTRA "@@extraver@@"' >> src/version.h.in @echo '#define VER_SVNREV @@svnrev@@' >> src/version.h.in @echo '' >> src/version.h.in @echo '#define VER_FULLSTR "@@fullverstr@@"' >> src/version.h.in @echo '' >> src/version.h.in @echo Build system initialised # remove the dependency files cleandep: -rm $(DEPFILES) # remove the dependency files and any target or intermediate build files clean: cleandep clean-versioninfo -rm $(OBJ) $(TARGET) $(GARBAGE) # remove any dependency or intermediate build files tidy: cleandep clean-versioninfo -rm $(OBJ) $(GARBAGE) ################################# $(TARGET): $(OBJ) $(EXTDEP) ifeq ($(SRC_TYPE),c) $(CC) $(CXXFLAGS) $(LDFLAGS) $(OBJ) $(LIBPTH) $(LIBLNK) -o $@ else $(CXX) $(CXXFLAGS) $(LDFLAGS) $(OBJ) $(LIBPTH) $(LIBLNK) -o $@ endif ifeq ($(BUILD_TYPE),release) $(STRIP) $(TARGET) endif ### # extra rules # example: #src/parser.c: src/parser.h #### # make object files from C source files obj/%.o: src/%.c $(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ ## # make object files from C++ source files obj/%.o: src/%.cc $(CXX) -c $(CXXFLAGS) $(CPPFLAGS) $< -o $@ obj/%.o: src/%.cpp $(CXX) -c $(CXXFLAGS) $(CPPFLAGS) $< -o $@ ### # make C files from yacc/bison source src/%.h src/%.c: src/%.y $(YACC) $(YFLAGS) -d $< mv -f y.tab.c $*.c mv -f y.tab.h $*.h ### # make C files from lex/flex source src/%.c: src/%.l $(LEX) $(LFLAGS) -o$@ $< ### # make dependencies for our source files dep/%.d: src/%.c $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,obj/\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ dep/%.d: src/%.cpp $(CXX) -MM $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,obj/\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ dep/%.d: src/%.cc $(CXX) -MM $(CPPFLAGS) $< > $@.$$$$; \ sed 's,\($*\)\.o[ :]*,obj/\1.o $@ : ,g' < $@.$$$$ > $@; \ rm -f $@.$$$$ #### # pull in the dependency files, but only for 'make $(TARGET)' #### ifeq ($(MAKECMDGOALS),$(TARGET)) -include $(DEPFILES) endif