SRC_DIR = @SRC_DIR@ CONFIG_HASH = @CONFIG_HASH@ PACKAGE = @PACKAGE@ PACKAGE_NAME = @PACKAGE_NAME@ PREFIX = @PREFIX@ EPREFIX = @EPREFIX@ BINDIR = @BINDIR@ SBINDIR = @SBINDIR@ LIBEXECDIR = @LIBEXECDIR@ SYSCONFDIR = @SYSCONFDIR@ SHAREDSTATEDIR = @SHAREDSTATEDIR@ LOCALSTATEDIR = @LOCALSTATEDIR@ LIBDIR = @LIBDIR@ INCLUDEDIR = @INCLUDEDIR@ OLDINCLUDEDIR = @OLDINCLUDEDIR@ DATAROOTDIR = @DATAROOTDIR@ DATADIR = @DATADIR@ PACKAGEDATADIR = @PACKAGEDATADIR@ INFODIR = @INFODIR@ LOCALEDIR = @LOCALEDIR@ MANDIR = @MANDIR@ DOCDIR = @DOCDIR@ HTMLDIR = @HTMLDIR@ DVIDIR = @DVIDIR@ PDFDIR = @PDFDIR@ PSDIR = @PSDIR@ DEBUGDIR = @DEBUGDIR@ BISON = @BISON@ FLEX = @FLEX@ CPPFLAGS = @CPPFLAGS@ LDFLAGS = @LDFLAGS@ LDLIBS = @LDLIBS@ CXX = @CXX@ CXXFLAGS = @CXXFLAGS@ GTEST_DIR = @GTEST_DIR@ ENABLE_BACKUPS_DURING_INSTALL = @ENABLE_BACKUPS_DURING_INSTALL@ ENABLE_WARNINGS = @ENABLE_WARNINGS@ ENABLE_ABI6 = @ENABLE_ABI6@ ENABLE_CYGWIN_HACKS = @ENABLE_CYGWIN_HACKS@ ENABLE_COMPAT_SYMLINKS = @ENABLE_COMPAT_SYMLINKS@ ENABLE_DEBUG = @ENABLE_DEBUG@ TESTER = TEST_ARGS = ## real.make - The One Makefile that builds them all. ## ## Copyright © 2012-2014 Ben Longbons ## ## This file is part of The Mana World (Athena server) ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 3 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . # With the One Makefile, you never have to remember to update the list of # objects you need to link your programs. It is designed to behave (almost) # exactly the way you expect a Makefile to act - which cannot be said of # automake, cmake, or qmake. # # The One Makefile lives under the name 'real.make', because it is # reponsible for doing all the actual building, but it is not the file # that the user actually calls (using make) directly. The reason for this # is that the One Makefile requires a certain environment that it cannot # arrange for on its own. # # Specifically: # The -r and -R flags must be passed. # A list of variables must be included, but regenerating Makefile from # Makefile.in is time-consuming, so real.make actually *includes* the # file that called it. # # For an example of how to do this safely, look at the Makefile.in shipped # by TMWA. Of course, you could use any other mechanism to generate a # Makefile, as long as it supplies the appropriate list of variables. # TODO: # 1. Implement support for static libraries # This should be trivial. # 2. Implement support for shared libraries # This requires building two different .o files and such. # 3. Implement support for mixed C and C++ source trees # This just requires writing more patsubst in various places # At that point, they should *probably* be refactored out into functions. # However, it would be hard to allow linking some binaries as pure C. # Unless maybe we should use .c.o and .cpp.o ? # 4. See if something can be done about all the mkdirs. # 5. Remove the few (obvious) bits that are hard-coded for TMWA. # 6. Handle testing better. I'm guessing I should actually compile just # one foo_test.cpp file into each executable test ... # 7. Refactor into several files after all. We need extensibility! # # IWBNMI: # 1. Add 'make check' and 'make installcheck'. # 2. 'make distclean' should remove "anything that ./configure created". # 3. 'make dist' should be implemented. Git only or not? # 4. 'make install' should install documentation. # 5. Split 'make install-exec' and 'make install-data'. Beware etc and var! # 6. '--program-prefix' and '--program-suffix' (easy). What about sed? # 7. Support DESTDIR during 'make install' (URGENT). # 8. 'make distcheck' using the 'make dist' tarball? # 9. Add rules for gettext. # 10. Support per-target build flags? (requires renaming) ifeq ($(findstring s,$(firstword ${MAKEFLAGS})),) ifndef MAKE_RESTARTS # TODO: should I write this in tengwar? # The major problem is that it's usually encoded in the PUA # and thus requires a font specification. # There *is* a formal request and tentative allocation in the SMP, # but it has been languishing for 15 years. # TODO: regardless of the preceding, look up the words for 'build' and 'link'. # (Does there exist a word that could mean "makefile"? # Maybe something like 'instructional scroll') # Note: the space is necessary $(info ) $(info Welcome to the One Makefile) $(info Copyright 2012-2014 Ben Longbons) $(info ) $(info One Makefile to build them all,) $(info One Makefile to find them,) $(info One Makefile to bring them all) $(info and in the darkness link them.) $(info ) else $(info The Road goes ever on and on ...) endif endif c = @colorize 6: 2:$< 3:$@ : ifneq '$c' '' l = @colorize 6: $(patsubst %,2:%,$^) 3:$@ : endif .SUFFIXES: # make 3.81 doesn't support 'undefine' and Debian hasn't shipped it yet # even though all the related bugs have already been fixed. $(foreach var,$(filter-out .% MAKE% SUFFIXES,${.VARIABLES}),$(if $(filter default,$(origin ${var})),$(eval ${var} =))) .SECONDARY: export PATH:=$(realpath ${SRC_DIR}/tools):${PATH} # bash is needed for 'set -o pipefail' below - I have had real bugs there! # It's just not worth the bother to see if another shell works when it # needs to *and* fails when it needs to. Just use bash. SHELL=bash # path lists REAL_SOURCES := $(shell cd ${SRC_DIR}; find src/ -name '*.cpp') REAL_HEADERS := $(shell cd ${SRC_DIR}; find src/ -name '*.hpp' -o -name '*.tcc') SOURCES := ${REAL_SOURCES} HEADERS := ${REAL_HEADERS} DEPENDS := $(patsubst src/%.cpp,obj/%.d,${SOURCES}) PREPROCESSED := $(patsubst %.d,%.ii,${DEPENDS}) IRS := $(patsubst %.d,%.ll,${DEPENDS}) BITCODES := $(patsubst %.d,%.bc,${DEPENDS}) ASSEMBLED := $(patsubst %.d,%.s,${DEPENDS}) OBJECTS := $(patsubst %.d,%.o,${DEPENDS}) MAIN_SOURCES := $(filter %/main.cpp,${SOURCES}) BINARIES := $(patsubst src/%/main.cpp,bin/tmwa-%,${MAIN_SOURCES}) TEST_DEPENDS := $(patsubst src/%/test.cpp,obj/%/autolist.d,$(filter %/test.cpp,${SOURCES})) DEPENDS += ${TEST_DEPENDS} TEST_BINARIES := $(patsubst obj/%/autolist.d,bin/test-%,${TEST_DEPENDS}) # tricky part # We can't put comments in a macro so here goes: # 1: Include the contents of the current %.d file ($1). # 2: For each header, substitute the corresponding %.o's dependency file. # 3: Blank, reserved for header->libtmwa-common.a if that gets implemented. # 4: Remove all non-deps - clutter and lonely headers. # 5: Prevent infinite loops later by filtering out deps we've already seen. # 6: Merge new deps into the existing dep list. # 7: Recurse over all new deps (see 5). define RECURSIVE_DEPS_IMPL $(eval more_deps := $(shell cat $(patsubst %/test.d,%/autolist.d,${1}))) $(eval more_deps := $(patsubst src/%.hpp,obj/%.d,${more_deps})) $(eval more_deps := $(filter ${DEPENDS},${more_deps})) $(eval more_deps := $(filter-out ${cur_deps},${more_deps})) $(eval cur_deps += ${more_deps}) $(foreach dep,${more_deps},$(call RECURSIVE_DEPS_IMPL,${dep})) endef # 1: Initialize the dep list ($1 is a %.d). # 2: Call the real function on it. # 3: Blank for clarity. # 4: Expand to text. Note that *nothing* else actually produces anything! define RECURSIVE_DEPS $(eval cur_deps := ${1}) $(call RECURSIVE_DEPS_IMPL,${1}) ${cur_deps} endef # Apply the rules to all the main.cpp files $(foreach exe,${BINARIES},$(eval ${exe}: $(strip $(patsubst %.d,%.o,$(call RECURSIVE_DEPS,$(patsubst bin/tmwa-%,obj/%/main.d,${exe})))))) $(foreach exe,${TEST_BINARIES},$(eval ${exe}: $(strip $(patsubst %.d,%.o,$(call RECURSIVE_DEPS,$(patsubst bin/test-%,obj/%/test.d,${exe})))))) # utility functions for the rules MKDIR_FIRST = @mkdir -p ${@D} # Stuff sensitive to attoconf CXXFLAGS += ${WARNINGS} ifeq (${ENABLE_WARNINGS},yes) WARNINGS := -include ${SRC_DIR}/src/warnings.hpp endif # related to gdb bug 15801 ifeq (${ENABLE_ABI6},yes) CXXFLAGS += -fabi-version=6 endif # This needs to edit CXX instead of CXXFLAGS in order to make # the %.ii rule work. ifeq (${ENABLE_CYGWIN_HACKS},yes) override CXX += -std=gnu++0x else override CXX += -std=c++0x endif CXXFLAGS += -fstack-protector override CXXFLAGS += -fno-strict-aliasing override CXXFLAGS += -fvisibility=hidden # actual rules vpath %.cpp ${SRC_DIR} vpath %.hpp ${SRC_DIR} vpath %.tcc ${SRC_DIR} vpath tools/% ${SRC_DIR} .DELETE_ON_ERROR: .DEFAULT_GOAL := all # main goals all: ${BINARIES} ii: ${PREPROCESSED} ll: ${IRS} bc: ${BITCODES} s: ${ASSEMBLED} o: ${OBJECTS} mostlyclean: $c rm -rf obj conf-raw clean: mostlyclean $c rm -rf bin distclean: clean gen-clean gen-clean: ; ifndef MAKE_RESTARTS # prevent errors if missing header obj/%.d: src/%.cpp $(MKDIR_FIRST) set -o pipefail; \ ${CXX} ${CPPFLAGS} -DGENERATING_DEPENDENCIES ${CXXFLAGS} -MG -MP -MM $< \ -MT '$(patsubst %.d,%.ii,$@) $(patsubst %.d,%.ll,$@) $(patsubst %.d,%.bc,$@) $(patsubst %.d,%.s,$@) $(patsubst %.d,%.o,$@) $@' \ | sed -e ':again; s:/[^/. ]*/\.\./:/:; t again' \ -e 's: ${SRC_DIR}/: :g' \ > $@ endif # the above SRC_DIR replacement is not really safe, but it works okayish. obj/%.ii: src/%.cpp $(MKDIR_FIRST) $c ${CXX} ${CPPFLAGS} -E -o $@ $< obj/%.ll: src/%.cpp $(MKDIR_FIRST) $c ${CXX} ${CPPFLAGS} ${CXXFLAGS} -S -emit-llvm -o $@ $< obj/%.bc: src/%.cpp $(MKDIR_FIRST) $c ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c -emit-llvm -o $@ $< obj/%.s: src/%.cpp $(MKDIR_FIRST) $c ${CXX} ${CPPFLAGS} ${CXXFLAGS} -S -o $@ $< obj/%.o: src/%.cpp $(MKDIR_FIRST) $c ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c -o $@ $< obj/%/autolist.d: $(filter-out %/autolist.d,${DEPENDS}) echo $@: $(filter %_test.d,$^) > $@ include ${DEPENDS} # I'm not convinced keeping the bin/ is a good idea # TODO - for development, regenerate -gdb.py even if the binary # doesn't need to be rebuilt. For release, the version forces rebuild. bin/%: $(MKDIR_FIRST) $l ${CXX} ${LDFLAGS} $^ ${LDLIBS} -o $@ cat ${SRC_DIR}/src/main-gdb-head.py \ $(wildcard $(patsubst obj/%.o,${SRC_DIR}/src/%.py,$^)) \ ${SRC_DIR}/src/main-gdb-tail.py \ > $@-gdb.py ${TEST_BINARIES}: obj/gtest-all.o # This isn't perfect. $(filter %_test.o,${OBJECTS}) obj/gtest-all.o: override CPPFLAGS += -DGTEST_HAS_PTHREAD=0 -I${GTEST_DIR} obj/gtest-all.o: override WARNINGS := obj/gtest-all.o: ${GTEST_DIR}/src/gtest-all.cc $(MKDIR_FIRST) $c ${CXX} ${CPPFLAGS} ${CXXFLAGS} -c -o $@ $< test: $(patsubst bin/%,.run-%,${TEST_BINARIES}) .run-%: bin/% ${TESTER} $< ${TEST_ARGS} install := install --backup=${ENABLE_BACKUPS_DURING_INSTALL} install_exe := ${install} install_dir := ${install} -d install_data := ${install} -m 0644 install_symlink := ln --backup=${ENABLE_BACKUPS_DURING_INSTALL} -sf install: @echo = Done installing install: install-bin install-bin: @echo + Installing binaries ${install_dir} ${DESTDIR}${BINDIR} ${install_exe} -t ${DESTDIR}${BINDIR} \ ${BINARIES} install-bin: install-bin-compat install-bin-compat: ifeq (${ENABLE_COMPAT_SYMLINKS},yes) @echo + Installing compatibility symlinks ${install_dir} ${DESTDIR}${BINDIR} ${install_symlink} tmwa-login ${DESTDIR}${BINDIR}/login-server ${install_symlink} tmwa-char ${DESTDIR}${BINDIR}/char-server ${install_symlink} tmwa-map ${DESTDIR}${BINDIR}/map-server ${install_symlink} tmwa-admin ${DESTDIR}${BINDIR}/ladmin ${install_symlink} tmwa-monitor ${DESTDIR}${BINDIR}/eathena-monitor else @echo - Not installing compatibility symlinks endif install: install-debug install-debug: ifeq (${ENABLE_DEBUG},yes) @echo + Installing debug files ${install_dir} ${DESTDIR}${DEBUGDIR}${BINDIR} ${install_data} -t ${DESTDIR}${DEBUGDIR}${BINDIR} \ $(patsubst %,%-gdb.py,${BINARIES}) else @echo - Not installing debug files endif tags: ${SOURCES} ${HEADERS} $l ctags --totals -h .tcc --langmap=C++:+.tcc --c-kinds=+px -f $@ $^ Makefile: ${SRC_DIR}/Makefile.in @echo Makefile.in updated, reconfiguring ... ./config.status include ${SRC_DIR}/version.make # TODO - fix pattern priority bug so I can make these .hpp # # This is complicated and still isn't optimal. conf-raw/int-%.h: FORCE $(MKDIR_FIRST) echo '#define $* $(value $*)' | maybe-replace $@ bool_yes := true bool_no := false conf-raw/bool-%.h: FORCE $(MKDIR_FIRST) echo '#define $* $(bool_$(value $*))' | maybe-replace $@ conf-raw/str-%.h: FORCE $(MKDIR_FIRST) echo '#define $* "$(value $*)"_s' | maybe-replace $@ FORCE: ; .PHONY: FORCE override CPPFLAGS += -I . # distribution tarballs # this only works from within a git checkout dist/%/version.make: $(MKDIR_FIRST) git --git-dir=${SRC_DIR}/.git show HEAD:version.make > $@ sed 's/^VERSION_FULL := .*/#&\nVERSION_FULL := ${VERSION_FULL}/' -i $@ sed 's/^VERSION_HASH := .*/#&\nVERSION_HASH := ${VERSION_HASH}/' -i $@ dist/%-src.tar: dist/%/version.make git --git-dir=${SRC_DIR}/.git archive --prefix=$*/ -o $@ HEAD ( mtime="$$(git --git-dir=${SRC_DIR}/.git log -n1 --pretty=%ci)" && cd dist && tar uf $*-src.tar --mtime="$${mtime}" --mode=664 --owner=root --group=root $*/version.make ) rm dist/$*/version.make rmdir dist/$*/ dist/%-attoconf-only.tar: $(MKDIR_FIRST) git --git-dir=${SRC_DIR}/deps/attoconf/.git archive --prefix=$*/deps/attoconf/ HEAD -o $@ dist/%-bundled.tar: dist/%-src.tar dist/%-attoconf-only.tar cp dist/$*-src.tar $@ tar Af $@ dist/$*-attoconf-only.tar dist: dist/tmwa-${VERSION_FULL}-src.tar dist/tmwa-${VERSION_FULL}-bundled.tar .PHONY: dist format: format-cpp format-hpp format-cpp: $(patsubst src/%,obj/%.formatted,${REAL_SOURCES}) format-hpp: $(patsubst src/%,obj/%.formatted,${REAL_HEADERS}) obj/%.cpp.formatted: src/%.cpp tools/indenter $(MKDIR_FIRST) apply-filter 'indenter -cpp' $< fgrep -q Copyright $< fgrep -q ../poison.hpp $< touch $@ obj/%.hpp.formatted: src/%.hpp tools/indenter $(MKDIR_FIRST) apply-filter 'indenter -cpp' $< fgrep -q Copyright $< if [[ $< == *fwd* ]]; then fgrep -q ../sanity.hpp $<; else fgrep -q '"fwd.hpp"' $<; fi touch $@ obj/%.tcc.formatted: src/%.tcc tools/indenter $(MKDIR_FIRST) apply-filter 'indenter -cpp' $< fgrep -q Copyright $< touch $@ .PHONY: format format-cpp format-hpp