# Use this file with GNU make to compile and install Yoink.
#
-# Set V to `1' to echo each build command in full. The default behavior
-# will keep output minimal.
-V = 0
+# Set V to `1' to echo each build command in full. The default behavior will
+# keep output minimal so that warnings are clearly visible.
+V = 0
SHELL = /bin/sh
ifeq (menuconfig,$(MAKECMDGOALS))
+# If all we want is to bring up the configure script, then we really don't
+# need to define any other functions or recipes process anything else.
menuconfig:
./configure --interactive
.PHONY: menuconfig
else
+# Recipe to create `config.h' which is included by some source files.
config.h: config.mk
$(Q)$(configure) --export-header=$@
+# Recipe to create `config.sed' which is used by some recipes to search and
+# replace certain terms in some files, usually with a `.in' suffix.
config.sed: config.mk
$(Q)$(configure) --export-terms=$@
+# Recipe to create `config.mk' which is the makefile with important configure
+# options set.
config.mk:
$(Q)$(configure) --interactive --print-instructions=no
+
+# Include the configure makefile; fail if it doesn't exist.
include config.mk
-#
-# Define some useful functions.
-#
+# This simple variable will be appended with targets which must be built
+# before the `install-data' target.
+data_targets :=
-DATA_TARGETS :=
-EXEC_TARGETS :=
-SUBDIRS :=
+# This simple variable will be appended with targets which must be built
+# before the `install-exec' target.
+exec_targets :=
+# This simple variable will be appended with subdirectories which contain
+# rules as well as their corresponding subdirectories under the build
+# directory, any directory which might have files to be installed.
+subdirs :=
+
+# Function to include rules from subdirectories. This is the core of the
+# non-recursive make method this build system uses. It works by managing
+# several stacks and helping to append the appropriate values to the simple
+# variables defined above.
define include_func =
ifeq (,$$d)
dir := $1
else
dir := $$d/$1
endif
-sp := $$(sp).x
+sp := $$(sp).x
dirstack_$$(sp) := $$d
datstack_$$(sp) := $$(data)
exestack_$$(sp) := $$(exec)
-d := $$(dir)
-data :=
-exec :=
+d := $$(dir)
+data :=
+exec :=
include $$(dir)/rules.mk
-SUBDIRS += $$d $$b
-data := $$(sort $$(data) $$(appdir_$$b) $$(mandir_$$b) $$(pkgdatadir_$$b) $$(desktop_$$b))
+subdirs += $$d $$b
+data := $$(sort $$(data) $$(mandir_$$b) $$(datadir_$$b) $$(pkgdatadir_$$b) $$(desktop_$$b))
exec := $$(sort $$(exec) $$(bindir_$$b) $$(libdir_$$b))
-deps := $$(filter %.d,$$(exec:%.o=%.d))
+deps := $$(filter %.d,$$(call depfile,$$(exec)))
clean := $$(clean) $$(deps) $$(exec) $$(data)
DEPFILE := $$(DEPFILE) $$(deps)
-DATA_TARGETS += $$(data)
-EXEC_TARGETS += $$(exec)
-d := $$(dirstack_$$(sp))
+data_targets += $$(data)
+exec_targets += $$(exec)
+d := $$(dirstack_$$(sp))
data := $$(datstack_$$(sp))
exec := $$(exestack_$$(sp))
sp := $$(basename $$(sp))
endef
include = $(foreach i,$1,$(eval $(call include_func,$i)))
-targets = $(EXEC_TARGETS) $(DATA_TARGETS)
+# These shortcut variables are meant to be used to simplify included rules.
builddir = build/obj
-b = $(builddir)/$(d)
+b = $(builddir)/$(d)
this = $(d)/rules.mk
+# Function to convert object file paths to dependency file paths. This also
+# supports pre-compiled header files.
+depfile = $(patsubst %.h.gch,%.d,$(patsubst %.hh.gch,%.d,$(1:%.o=%.d)))
+
+# Determine the suffix that installed manual pages should have.
ifeq (gzip,$(manCompression))
MANEXT=.gz
else
endif
endif
-
-#
-# Include the subdirectories--order is not important.
-#
-
+# Now do the actual inclusion of the rules from these subdirectories.
$(call include,src data doc)
--include build/$(platform)/rules.mk
-
-#
-# Define some common rules.
-#
+# Also include platform-specific rules, if they exist.
+-include build/arch/$(platform)/rules.mk
+# Handle automatic tracking of dependencies, if enabled. This adds overhead
+# and should be disabled if not needed.
ifeq (true,$(tracking))
-PKG_CFLAGS += -MD -MP -MF $(@:%.o=%.d) -MT $@
+PKG_CFLAGS += -MT $@ -MMD -MP -MF $(call depfile,$@)
-include $(DEPFILE)
endif
+# Handle the inclusion of a PCH file, if enabled. Using a PCH file can
+# improve build times in some (but not all) cases. The rules should also
+# define the `pchfile' variable to the path of the PCH file. Only one PCH
+# file can be used. If the PCH file has a `.h' suffix, it is included only
+# when compiling C sources, otherwise it will be included only when compiling
+# C++ sources.
+ifeq (true,$(pch))
+ifneq (,$(pchfile))
+PCH_FLAGS = -Winvalid-pch -include $(pchfile)
+endif
+endif
+
# Include current directory to allow sources to include config.h.
-override CPPFLAGS += -I.
+override CPPFLAGS += -I. -DHAVE_CONFIG_H=1
+
+# The target C++ flags should mirror the target C flags by default.
TGT_CXXFLAGS = $(TGT_CFLAGS)
+
+# Define the default flags passed to the library archiver.
ARFLAGS = rc
+
+# Define the default install program.
INSTALL = install
+# Define the `tarname' to be used as a base filename for distributable
+# archives.
tarname = $(TARNAME)-$(VERSION)
-cmd_compile_c = $(CC) $(PKG_CFLAGS) $(TGT_CFLAGS) $(CFLAGS) $(CPPFLAGS) \
- $(TARGET_ARCH) -c $(OUTPUT_OPTION) $<
-cmd_compile_cc = $(CXX) $(PKG_CXXFLAGS) $(TGT_CXXFLAGS) $(CXXFLAGS) $(CPPFLAGS) \
- $(TARGET_ARCH) -c $(OUTPUT_OPTION) $<
-cmd_link_c = $(CC) $(PKG_CFLAGS) $(TGT_CFLAGS) $(CFLAGS) $(CPPFLAGS) \
- $(PKG_LDFLAGS) $(TGT_LDFLAGS) $(LDFLAGS) \
- $(TARGET_ARCH) $^ $(LOADLIBES) $(PKG_LDLIBS) $(TGT_LDLIBS) $(LDLIBS) -o $@
-cmd_link_cc = $(CXX) $(PKG_CXXFLAGS) $(TGT_CXXFLAGS) $(CXXFLAGS) $(CPPFLAGS) \
- $(PKG_LDFLAGS) $(TGT_LDFLAGS) $(LDFLAGS) \
- $(TARGET_ARCH) $^ $(LOADLIBES) $(PKG_LDLIBS) $(TGT_LDLIBS) $(LDLIBS) -o $@
-cmd_ar = $(AR) $(ARFLAGS) $@ $^; $(RANLIB) $@
+# Define the commands needed to make several different types of files. Don't
+# used these commands directly because they don't account for verbosity;
+# instead use the commands without the `cmd_' prefix (defined below).
+cmd_compile_c = $(CC) $(PKG_CFLAGS) $(TGT_CFLAGS) $(PCH_CFLAGS) $(CFLAGS) $(MYCFLAGS) $(CPPFLAGS) \
+ $(TARGET_ARCH) -c $(OUTPUT_OPTION) $<
+cmd_compile_cc = $(CXX) $(PKG_CXXFLAGS) $(TGT_CXXFLAGS) $(PCH_CXXFLAGS) $(CXXFLAGS) $(MYCXXFLAGS) $(CPPFLAGS) \
+ $(TARGET_ARCH) -c $(OUTPUT_OPTION) $<
+cmd_link_c = $(CC) $(PKG_CFLAGS) $(TGT_CFLAGS) $(PCH_CFLAGS) $(CFLAGS) $(MYCFLAGS) $(CPPFLAGS) \
+ $(PKG_LDFLAGS) $(TGT_LDFLAGS) $(LDFLAGS) $(MYLDFLAGS) \
+ $(TARGET_ARCH) $^ $(LOADLIBES) $(PKG_LDLIBS) $(TGT_LDLIBS) $(LDLIBS) -o $@
+cmd_link_cc = $(CXX) $(PKG_CXXFLAGS) $(TGT_CXXFLAGS) $(PCH_CXXFLAGS) $(CXXFLAGS) $(MYCXXFLAGS) $(CPPFLAGS) \
+ $(PKG_LDFLAGS) $(TGT_LDFLAGS) $(LDFLAGS) $(MYLDFLAGS) \
+ $(TARGET_ARCH) $^ $(LOADLIBES) $(PKG_LDLIBS) $(TGT_LDLIBS) $(LDLIBS) -o $@
+cmd_compile_pch = $(CC) $(PKG_CFLAGS) $(TGT_CFLAGS) $(CFLAGS) $(MYCFLAGS) $(CPPFLAGS) \
+ $(TARGET_ARCH) -c $(OUTPUT_OPTION) $<
+cmd_compile_pchh = $(CXX) $(PKG_CXXFLAGS) $(TGT_CXXFLAGS) $(CXXFLAGS) $(MYCXXFLAGS) $(CPPFLAGS) \
+ $(TARGET_ARCH) -c $(OUTPUT_OPTION) $<
+cmd_ar = $(AR) $(ARFLAGS) $@ $^; $(RANLIB) $@
cmd_compile_rc = $(WINDRES) $(CPPFLAGS) $(TARGET_ARCH) $(OUTPUT_OPTION) -i $<
-cmd_sed = sed -f config.sed <$< >$@
-cmd_gzip = gzip -c $< >$@
-cmd_bzip2 = bzip2 -c $< >$@
-cmd_xxd = id=$(subst .,_,$(notdir $<)); \
- printf "\#include <stddef.h>\nsize_t\tdata_%s_size = %d;\nchar\tdata_%s[] = {\n" \
- $$id $$(wc -c <$<) $$id >$@; \
- xxd -i <$< >>$@; \
- printf ", 0x00\n};\n" >>$@
+cmd_sed = sed -f config.sed <$< >$@
+cmd_gzip = gzip -c $< >$@
+cmd_bzip2 = bzip2 -c $< >$@
+cmd_xxd = id=$(subst .,_,$(notdir $<)); \
+ printf "\#include <stddef.h>\nsize_t\tdata_%s_size = %d;\nchar\tdata_%s[] = {\n" \
+ $$id $$(wc -c <$<) $$id >$@; \
+ xxd -i <$< >>$@; \
+ printf ", 0x00\n};\n" >>$@
cmd_configure = ./configure
-func_install = mkdir -p $(DESTDIR)$3 && $(INSTALL) -m $1 $2 $(DESTDIR)$3
-func_remove = rm -f $1
-func_dist = git archive HEAD --prefix='$(tarname)/' | $1 >"$(tarname).tar.$2" && \
- (md5 "$(tarname).tar.$2" 2>/dev/null || md5sum "$(tarname).tar.$2")
-
-compile_c = $(call func_print,CC,$@) $(cmd_compile_c)
-compile_cc = $(call func_print,CXX,$@) $(cmd_compile_cc)
-compile_rc = $(call func_print,RC,$@) $(cmd_compile_rc)
-link_c = $(call func_print,LINK,$@) $(cmd_link_c)
-link_cc = $(call func_print,LINK,$@) $(cmd_link_cc)
-ar = $(call func_print,AR,$@) $(cmd_ar)
-sed = $(call func_print,SED,$@) $(cmd_sed)
-gzip = $(call func_print,GZIP,$@) $(cmd_gzip)
-bzip2 = $(call func_print,BZIP2,$@) $(cmd_bzip2)
-xxd = $(call func_print,XXD,$@) $(cmd_xxd)
-configure = $(call func_print,MAKE,$@) $(cmd_configure)
-dist = $(call func_print,DIST,$1) $(func_dist)
-install = $(call func_print,CP,$(DESTDIR)$(3:%/=%)/$(notdir $2)) $(func_install)
-remove = $(call func_print,RM,$1) $(func_remove)
+# Define some useful functions. Don't use these functions directly because
+# they don't account for verbosity; instead use the functions without the
+# `func_' prefix (defined below).
+func_remove = rm -f $1
+func_install = mkdir -p $(DESTDIR)$3 && $(INSTALL) -m $1 $2 $(DESTDIR)$3
+func_dist = git archive HEAD --prefix='$(tarname)/' | $1 >"$(tarname).tar.$2" && \
+ (openssl md5 "$(tarname).tar.$2" 2>/dev/null || \
+ md5 "$(tarname).tar.$2" 2>/dev/null || \
+ md5sum "$(tarname).tar.$2" 2>/dev/null)
+
+# These commands and functions should be used in most recipes to compile
+# sources, link objects, compress files, create distfiles, install files, etc.
+compile_c = $(call func_print,CC,$@) $(cmd_compile_c)
+compile_cc = $(call func_print,CXX,$@) $(cmd_compile_cc)
+compile_rc = $(call func_print,RC,$@) $(cmd_compile_rc)
+link_c = $(call func_print,LINK,$@) $(cmd_link_c)
+link_cc = $(call func_print,LINK,$@) $(cmd_link_cc)
+compile_pch = $(call func_print,CC,$@) $(cmd_compile_pch)
+compile_pchh = $(call func_print,CXX,$@) $(cmd_compile_pchh)
+ar = $(call func_print,AR,$@) $(cmd_ar)
+sed = $(call func_print,SED,$@) $(cmd_sed)
+gzip = $(call func_print,GZIP,$@) $(cmd_gzip)
+bzip2 = $(call func_print,BZIP2,$@) $(cmd_bzip2)
+xxd = $(call func_print,XXD,$@) $(cmd_xxd)
+configure = $(call func_print,MAKE,$@) $(cmd_configure)
+dist = $(call func_print,DIST,$1) $(func_dist)
+remove = $(call func_print,RM,$1) $(func_remove)
+install = $(call func_print,CP,$(DESTDIR:%/=%)$(3:%/=%)/$(notdir $2)) $(func_install)
+
+# The mechanism used by this makefile to handle verbosity simply requires (by
+# convention) commands in rules to be prefixed with `$Q' and the commands and
+# functions defined above will then print a single line describing the command
+# instead of the entire command.
ifeq (1,$V)
-Q =
+Q =
func_print =
else
-Q = @
+Q = @
func_print = printf "\x20\x20$1\t%s\n" $2;
endif
-#
-# Define the implicit rules.
-#
-
+# Define the implicit rules. Targets and sources which match do not need to
+# be explicitly stated in the rule definitions.
+%.o: %.c
+ $(Q)$(compile_c)
+%.h.gch: %.h
+ $(Q)$(compile_pch)
%.o: %.cc
$(Q)$(compile_cc)
%.o: %.cpp
$(Q)$(compile_cc)
-%.o: %.c
- $(Q)$(compile_c)
+%.hh.gch: %.hh
+ $(Q)$(compile_pchh)
%.o: %.rc
$(Q)$(compile_rc)
%: %.in
%.bz2: %
$(Q)$(bzip2)
+# More explicit rules which allow targets in the build directory to be made
+# directly. A special rules is also defined to create directories or copy
+# files under the build directory to match the structure of the source
+# directory.
$(builddir)/%.o: %.cc
$(Q)$(compile_cc)
$(builddir)/%.o: %.cpp
$(Q)if [ -d $< ]; then mkdir -p $@; elif [ -f $< ]; then cp -f $< $@; fi
-#
-# Define the phony targets.
-#
-
+# Define the default target(s). This should `make' just about everything.
.DEFAULT_GOAL :=
-all: $(targets)
+all: $(exec_targets) $(data_targets)
+# Define the recipe to clean the targets. This will remove the target files
+# and the empty directories of the build hierarchy.
clean:
$(Q)$(foreach f,$(clean),$(call remove,$f);)
$(Q)if [ -d $(builddir) ]; then find $(builddir) -type d -empty -delete; fi
+# Make the project even cleaner by removing files created by configuration.
distclean: clean
$(Q)cd build && $(MAKE) clean
$(Q)$(call remove,config.h);$(call remove,config.mk);$(call remove,config.sed)
-
+# Installation happens in two separated phases, installing the executable
+# (i.e. platform-specific) files and installing the data files.
install: install-data install-exec
-install-data: $(DATA_TARGETS)
- $(Q)$(foreach d,$(SUBDIRS),$(foreach f,$(pkgdatadir_$d),\
+# Recipe to install the data files. Right now, that includes files destined
+# for `pkgdatadir' and `mandir'.
+install-data: $(data_targets)
+ $(Q)$(foreach d,$(subdirs),$(foreach f,$(pkgdatadir_$d),\
$(call install,644,$f,$(dir $(f:$d%=$(pkgdatadir)%)));))
- $(Q)$(foreach d,$(SUBDIRS),$(foreach f,$(mandir_$d),\
- $(call install,644,$f,$(dir $(f:$d%=$(mandir)/man$(shell echo "$f" | sed 's/[^.]*\.\([^.]*\).*/\1/')%)));))
-
-install-exec: $(EXEC_TARGETS)
- $(Q)$(foreach d,$(SUBDIRS),$(foreach f,$(bindir_$d),\
+ $(Q)$(foreach d,$(subdirs),$(foreach f,$(mandir_$d),\
+ $(call install,644,$f,\
+ $(dir $(f:$d%=$(mandir)/man$(shell echo "$f" | sed 's/[^.]*\.\([^.]*\).*/\1/')%)));))
+
+# Recipe to install executable files. Right now, that includes files destined
+# for `bindir'.
+install-exec: $(exec_targets)
+ $(Q)$(foreach d,$(subdirs),$(foreach f,$(bindir_$d),\
$(call install,755,$f,$(dir $(f:$d%=$(bindir)%)));))
-install-desktop-entry: $(DATA_TARGETS)
- $(Q)$(foreach d,$(SUBDIRS),$(foreach f,$(desktop_$d),\
+# Recipe to install desktop entry file(s) to /usr/share/applications.
+install-desktop-entry: $(data_targets)
+ $(Q)$(foreach d,$(subdirs),$(foreach f,$(desktop_$d),\
$(call install,644,$f,/usr/share/applications);))
-
+# Target used to create distfiles in all the supported compression formats.
dist-all: dist-bzip2 dist-gzip dist-lzma dist-xz
+# Separate targets to create distfiles in the corresponding formats.
dist-bzip2:
$(Q)$(call dist,bzip2,bz2)
-
dist-gzip:
$(Q)$(call dist,gzip,gz)
-
dist-lzma:
$(Q)$(call dist,lzma,lzma)
-
dist-xz:
$(Q)$(call dist,xz,xz)
+# The `dist' target will create the distfile in whatever format was set in the
+# configuration.
dist: dist-$(archiveFormat)
-#
-# Prevent make from removing any build targets.
-#
-
-.SECONDARY: $(clean)
-
-.PHONY: all clean distclean install install-data install-exec
-.PHONY: install-desktop-entry
+# Define which targets do not actually correspond to real files.
+.PHONY: all clean distclean
+.PHONY: install install-data install-exec install-desktop-entry
.PHONY: dist dist-all dist-gzip dist-bzip2 dist-xz dist-lzma
endif # menuconfig