Here are two cumulative patches that try to simplify the rules.mk and
module.mk used by the FreeType 2 build system. The point is to remove
GNU-Make specific statements from these files, making them declarative
instead. I.e. after applying the first patch, a module.mk will contain
something like:

# See builds/modules.mk for documentation about this file's content
MODULE_CLASS_NAME := sfnt
MODULE_CLASS_TYPE := module
MODULE_CLASS_DESCRIPTION := Helper module for TrueType & OpenType formats

Similarly, after applying the second patch, a rules.mk will look like:

MODULE_SOURCES := \
  pngshim.c   \
  sfdriver.c  \
  sfobjs.c    \
  sfwoff.c    \
  sfwoff2.c   \
  ttbdf.c     \
  ttcmap.c    \
  ttcolr.c    \
  ttcpal.c    \
  ttkern.c    \
  ttload.c    \
  ttmtx.c     \
  ttpost.c    \
  ttsbit.c    \
  woff2tags.c \

MODULE_HEADERS := \
  $(MODULE_SOURCES:%.c=%.h) \
  sferrors.h \

MODULE_WRAPPER := sfnt.c

Compared to their previous content, this drastically simplifies the file,
and makes them much easier to understand and maintain. All of this comes at
the price of more "magic" in the GNU Make scripts under builds/freetype.mk
and builds/modules.mk, which now heavily use custom and non-trivial GNU
Make functions (that I've tried to document as most as possible).

The goal is to make these files (rules.mk / module.mk) much easier to parse
with something else, so we can start experimenting with better build
systems. It shouldn't be difficult to write a small Python script that
reads these files for example, then transform that into something more
interesting (e.g. a CMake / Meson / GN fragment).

Please take a look and let me know if you have any issue with the approach
used here.

NOTE: These patches should not change the output of the current build
system at all!

- David
From 4095fc04234a73b825dc122b86d903835988863d Mon Sep 17 00:00:00 2001
From: David Turner <david.turner....@gmail.com>
Date: Thu, 30 Apr 2020 11:03:33 +0200
Subject: build: Simplify module.mk syntax

This patch modifies the syntax expected from module.mk
files to make it considerably simpler. Now, each file
should only contain declarations like:

  MODULE_CLASS_NAME := psaux
  MODULE_CLASS_TYPE := module
  MODULE_CLASS_DESCRIPTION := Postcript Type 1 & Type 2 helper module

These are parsed by builds/modules.mk which uses them
to recreate ftmodule.h exactly as previously.

This makes the modules.mk much easier to understand, and
a future patch will do something similar for rules.mk files,
possibly merging them into a single file for each module.

Note that all of this introduces far more 'magic' in the build
system (i.e. the use of non-trivial custom GNU Make functions),
and comments have been added to try to explain what they are
doing.

However, this is a necessary first step before being able to
move the FreeType 2 build system in a different direction.
---
 builds/modules.mk        | 188 ++++++++++++++++++++++++++++++---------
 builds/toplevel.mk       |  11 +--
 builds/utils/common.mk   |  37 ++++++++
 builds/utils/compiler.mk |  88 ++++++++++++++++++
 builds/utils/host.mk     |  81 +++++++++++++++++
 src/autofit/module.mk    |  11 +--
 src/bdf/module.mk        |  11 +--
 src/cff/module.mk        |  11 +--
 src/cid/module.mk        |  11 +--
 src/gxvalid/module.mk    |  11 +--
 src/otvalid/module.mk    |  11 +--
 src/pcf/module.mk        |  11 +--
 src/pfr/module.mk        |  11 +--
 src/psaux/module.mk      |  11 +--
 src/pshinter/module.mk   |  11 +--
 src/psnames/module.mk    |  11 +--
 src/raster/module.mk     |  11 +--
 src/sfnt/module.mk       |  11 +--
 src/smooth/module.mk     |  19 ++--
 src/truetype/module.mk   |  11 +--
 src/type1/module.mk      |  11 +--
 src/type42/module.mk     |  11 +--
 src/winfonts/module.mk   |  11 +--
 23 files changed, 432 insertions(+), 179 deletions(-)
 create mode 100644 builds/utils/common.mk
 create mode 100644 builds/utils/compiler.mk
 create mode 100644 builds/utils/host.mk

diff --git a/builds/modules.mk b/builds/modules.mk
index ae2e238f1..e3f61ace4 100644
--- a/builds/modules.mk
+++ b/builds/modules.mk
@@ -16,64 +16,166 @@
 # DO NOT INVOKE THIS MAKEFILE DIRECTLY!  IT IS MEANT TO BE INCLUDED BY
 # OTHER MAKEFILES.
 
+include $(TOP_DIR)/builds/utils/common.mk
+include $(TOP_DIR)/builds/utils/host.mk
 
 # This file is in charge of handling the generation of the modules list
-# file.
+# file (i.e. ftmodule.h) from the content of the module.mk files found in
+# the source tree.
+#
+# Each module.mk should contain variable definitions that look like:
+#
+#  MODULE_CLASS_NAME := <varname>
+#  MODULE_CLASS_TYPE := <module-type>
+#  MODULE_CLASS_DESCRIPTION := <description>
+#
+# Where <varname> is the module's class variable name prefix.
+# Where <module-type> is one of "module", "driver" or "renderer"
+# And where <description> is a small one-line description of the module.
+#
+# Some module.mk may define up to three modules by using
+# MODULE_CLASS_XXX_2 and MODULE_CLASS_XXX_3 definitions as well, but this
+# is currently supported only for src/smooth/module.mk
+#
 
+# A variable that holds the list of all modules
+#
+# For each value in this list, the _ftmodule__<name>__<field> variable will
+# also be defined to record attributes associated with the <name> module.
+# See ft-add-module-class to see the list of <field> names used.
+_ftmodule_list :=
 
-# Build the modules list.
+# A function used to record a module class variable definition.
 #
-$(FTMODULE_H): $(MODULES_CFG)
-	$(FTMODULE_H_INIT)
-	$(FTMODULE_H_CREATE)
-	$(FTMODULE_H_DONE)
-
-ifneq ($(findstring $(PLATFORM),dos windows os2),)
-  OPEN_MODULE   := @echo$(space)
-  CLOSE_MODULE  :=  >> $(subst /,$(SEP),$(FTMODULE_H))
-  REMOVE_MODULE := @-$(DELETE) $(subst /,$(SEP),$(FTMODULE_H))
-else
-  OPEN_MODULE   := @echo "
-  CLOSE_MODULE  := " >> $(FTMODULE_H)
-  REMOVE_MODULE := @-$(DELETE) $(FTMODULE_H)
+# $1: module name / prefix (e.g. sfnt).
+# $2: module interface struct name (e.g. FT_Module or FT_DriverClassRec).
+# $3: variable name suffix (e.g. "_module_class" or "_driver_class").
+# $4: Module description text. Must not contain a comma.
+ft-add-module-class = $(eval $(call ft-add-module-class-eval,$1,$2,$3,$4))
+define ft-add-module-class-eval
+_name = $(strip $1)
+
+_ftmodule_list += $$(_name)
+_ftmodule__$$(_name)__class := $(strip $2)
+_ftmodule__$$(_name)__varname := $$(_name)$(strip $3)
+_ftmodule__$$(_name)__description := $(strip $4)
+
+$$(call log,MODULES: Adding module "$$(_name)" class=$$(_ftmodule__$$(_name)__class) varname=$$(ft_module__$$(_name)__varname) description=[$$(_ftmodule__$$(_name)__description)])
+
+_name :=
+endef  # ft-add-module-class-eval
+
+# The list of valid values for MODULE_CLASS_TYPE variables.
+FT_VALID_MODULE_CLASS_TYPES := module driver renderer
+
+# $1: MODULE_CLASS_NAME value.
+# $2: MODULE_CLASS_TYPE value (one of $(FT_VALID_MODULE_CLASS_TYPES))
+# $3: MODULE_CLASS_DESCRIPTION value
+# $4: MODULE_FILE value used for error messages.
+ft-add-module-class-entry = $(eval $(call ft-add-module-class-entry-eval,$1,$2,$3,$4))
+define ft-add-module-class-entry-eval
+$$(if $$(filter-out $(FT_VALID_MODULE_CLASS_TYPES),$2),\
+  $$(error Invalid MODULE_CLASS_TYPE value $2 in $4. Must be one of $(FT_VALID_MODULE_CLASS_TYPES)))
+
+ifeq ($2,module)
+$$(call ft-add-module-class,$1,FT_Module,_module_class,$3)
+endif
+ifeq ($2,driver)
+$$(call ft-add-module-class,$1,FT_Driver_ClassRec,_driver_class,$3)
+endif
+ifeq ($2,renderer)
+$$(call ft-add-module-class,$1,FT_Renderer_Class,_renderer_class,$3)
 endif
 
+endef  # ft-add-modle-class-entry-eval
 
-define FTMODULE_H_INIT
-$(REMOVE_MODULE)
-$(info Generating modules list in $(FTMODULE_H)...)
-$(OPEN_MODULE)/* This is a generated file. */$(CLOSE_MODULE)
-endef
-
-# It is no mistake that the final closing parenthesis is on the
-# next line -- it produces proper newlines during the expansion
-# of `foreach'.
+# A function used to include src/$1/module.mk if it exists, and parse it
+# to add entries to the module list / table.
 #
-define FTMODULE_H_CREATE
-$(foreach COMMAND,$(FTMODULE_H_COMMANDS),$($(COMMAND))
-)
-endef
+# $1: Module sub-directory name (e.g. 'truetype').
+#
+ft-include-module-mk = $(eval $(call ft-include-module-mk-eval,$1))
 
-define FTMODULE_H_DONE
-$(OPEN_MODULE)/* EOF */$(CLOSE_MODULE)
-$(info done.)
-endef
+define ft-include-module-mk-eval
 
+MODULE_DIR := $(TOP_DIR)/src/$1
+MODULE_FILE := $$(MODULE_DIR)/module.mk
 
-# $(OPEN_DRIVER) & $(CLOSE_DRIVER) are used to specify a given font driver
-# in the `module.mk' rules file.
-#
-OPEN_DRIVER  := $(OPEN_MODULE)FT_USE_MODULE(
-CLOSE_DRIVER := )$(CLOSE_MODULE)
+# Not all modules have a module.mk (e.g. src/cache/)
+ifneq ($$(strip $$(wildcard $$(MODULE_FILE))),)
+
+# These variables should always be defined by module.mk, and this is checked
+# after the file is included.
+MODULE_CLASS_NAME :=
+MODULE_CLASS_TYPE :=
+MODULE_CLASS_DESCRIPTION :=
+
+# These are optional extras. In practice only used by the smooth module to
+# be able to add 3 renderers from a single module.mk.
+MODULE_CLASS_NAME_2 :=
+MODULE_CLASS_TYPE_2 :=
+MODULE_CLASS_DESCRIPTION_2 :=
 
-ECHO_DRIVER      := @echo "* module:$(space)
-ECHO_DRIVER_DESC := (
-ECHO_DRIVER_DONE := )"
+MODULE_CLASS_NAME_3 :=
+MODULE_CLASS_TYPE_3 :=
+MODULE_CLASS_DESCRIPTION_3 :=
 
-# Each `module.mk' in the `src/*' subdirectories adds a variable with
-# commands to $(FTMODULE_H_COMMANDS).  Note that we can't use SRC_DIR here.
+include $$(MODULE_FILE)
+
+$$(foreach _var,MODULE_CLASS_NAME MODULE_CLASS_TYPE MODULE_CLASS_DESCRIPTION,\
+  $$(if $$(strip $$($$(_var))),,\
+    $$(error $$(_var) definition missing from $$(MODULE_FILE))))
+
+$$(call ft-add-module-class-entry,\
+  $$(MODULE_CLASS_NAME),$$(MODULE_CLASS_TYPE),$$(MODULE_CLASS_DESCRIPTION),$$(MODULE_FILE))
+
+# Other entries are optional.
+ifneq ($$(strip $$(MODULE_CLASS_NAME_2)),)
+$$(call ft-add-module-class-entry,\
+  $$(MODULE_CLASS_NAME_2),$$(MODULE_CLASS_TYPE_2),$$(MODULE_CLASS_DESCRIPTION_2),$$(MODULE_FILE))
+endif
+
+ifneq ($$(strip $$(MODULE_CLASS_NAME_3)),)
+$$(call ft-add-module-class-entry,\
+  $$(MODULE_CLASS_NAME_3),$$(MODULE_CLASS_TYPE_3),$$(MODULE_CLASS_DESCRIPTION_3),$$(MODULE_FILE))
+endif
+
+endif  # wildcard $$(MODULE_FILE)
+
+endef  # ft-include-module-mk
+
+# Try to include all module.mk and record their entries.
+$(foreach _module,$(MODULES),\
+  $(call ft-include-module-mk,$(_module)))
+
+
+# A function that expands to a shell command that append one line to the auto-generated
+# ftmodule.h and prints a line about it to stdout.
+#
+# $1: module name.
 #
--include $(patsubst %,$(TOP_DIR)/src/%/module.mk,$(MODULES))
+define ft-module-expand
+@$(call cmd-echo-begin)FT_USE_MODULE( $(_ftmodule__$1__class), $(_ftmodule__$1__varname) )$(call cmd-echo-end) >> $(call host-path,$@)
+@echo "* module: $1 ($(_ftmodule__$1__description))"
+endef
+
+# This is expanded in toplevel.mk to create the shell commands used to generate ftmodule.h
+# NOTE: Do not remove the newline at the end of the $(foreach call below, it is required
+# to ensure this expands correctly.
+define FTMODULE_H_CREATE
+$(foreach _module,$(_ftmodule_list),$(call ft-module-expand,$(_module))
+)
+endef
 
+# Build the modules list (ftmodule.h) when the modules configuration changes
+# (i.e. modules.cfg).
+#
+$(FTMODULE_H): $(MODULES_CFG)
+	@-$(DELETE) $(call host-path,$@)
+	@$(call cmd-echo,Generating modules list in $@)
+	@$(call cmd-echo-to,/* This is a generated file. */,$@)
+	$(FTMODULE_H_CREATE)
+	@$(call cmd-echo-to,/* EOF */,$@)
+	@$(call cmd-echo,done)
 
 # EOF
diff --git a/builds/toplevel.mk b/builds/toplevel.mk
index 5de61c113..a43059ad6 100644
--- a/builds/toplevel.mk
+++ b/builds/toplevel.mk
@@ -153,19 +153,14 @@ else
 
 endif # test check_platform
 
+# Include common GNU Make functions.
+include $(TOP_DIR)/builds/utils/common.mk
 
 # We always need the list of modules in ftmodule.h.
 #
 all setup: $(FTMODULE_H)
 
 
-# The `modules' target unconditionally rebuilds the module list.
-#
-modules:
-	$(FTMODULE_H_INIT)
-	$(FTMODULE_H_CREATE)
-	$(FTMODULE_H_DONE)
-
 include $(TOP_DIR)/builds/modules.mk
 
 
@@ -173,7 +168,7 @@ include $(TOP_DIR)/builds/modules.mk
 # poor man's `sed' emulation with make's built-in string functions
 #
 work := $(strip $(shell $(CAT) \
-                  $(subst /,$(SEP),$(TOP_DIR)/include/freetype/freetype.h)))
+                  $(call host-path,$(TOP_DIR)/include/freetype/freetype.h)))
 work := $(subst |,x,$(work))
 work := $(subst $(space),|,$(work))
 work := $(subst \#define|FREETYPE_MAJOR|,$(space),$(work))
diff --git a/builds/utils/common.mk b/builds/utils/common.mk
new file mode 100644
index 000000000..6778e33b5
--- /dev/null
+++ b/builds/utils/common.mk
@@ -0,0 +1,37 @@
+#
+# FreeType 2 common GNU Make functions
+#
+
+ifndef __utils_common_mk_included
+__utils_common_mk_included := true
+
+# Function to print a log message during the build if BUILD_LOGS is defined.
+# For example, by invoking make with:  make BUILD_LOGS=1
+# Example: $(call log,Some simple message to print to the log)
+ifdef BUILD_LOGS
+log = $(info BUILD:$1$2$3$4$5$6$7$8$9)
+else
+log = # empty on purpose
+endif
+
+# Call this function at the start of a given GNU Makefile script to verify
+# that certain required variables are properly defined. If this is not the
+# case, an error with the appropriate variable name will be printed and
+# GNU Make will abort. E.g.:
+#
+#  $(call check-required-variables,SEP I OBJ_DIR)
+#
+check-required-variables = $(strip \
+  $(foreach _var,$1,\
+    $(if $(strip $($(_var))),,\
+      $(error "$(_var) must be defined before including this file"))))
+
+# Return true (i.e. a non-empty string) if $1 and $2 are two identical strings.
+# Usage: $(if $(call str-eq,<a>,<b>),<then>,<else>)
+str-eq = $(strip $(if $(subst x$1,,x$2)$(subst x$2,,x$1),,T))
+
+# Return true (i.e. a non-empty string) if $1 and $2 are two different strings.
+# Usage: $(if $(call str-neq,<a>,<b>),<then>,<else>)
+str-neq = $(strip $(if $(subst x$1,,x$2)$(subst x$2,,x$1),T,))
+
+endif  # __utils_common_mk_included
diff --git a/builds/utils/compiler.mk b/builds/utils/compiler.mk
new file mode 100644
index 000000000..ae26cdfad
--- /dev/null
+++ b/builds/utils/compiler.mk
@@ -0,0 +1,88 @@
+#
+# FreeType 2 host-specific GNU Make functions
+#
+
+# GNU Make functions related to compilation of FreeType sources.
+#
+
+ifndef __utils_compiler_mk_included
+__utils_compiler_mk_included := true
+
+include $(TOP_DIR)/builds/utils/common.mk
+
+# Expected variables used by this file:
+# ANSIFLAGS: Optional set of compiler flags to enfore standard compliance.
+# CC: C Compiler binary for target objects.
+# COMPILER_SEP: Directory separator to use when invoking the compiler.
+# I: Compiler option prefix to add an include path (e.g. "-I" or "/I")
+# T: Compiler option prefix to set object path (e,g. "-o")
+# OBJ_DIR: Directory where all object files will be stored.
+$(call check-required-variables, CC COMPILER_SEP I T OBJ_DIR)
+
+# Convert a path to a host-specific one used by the compiler.
+# This is used to replace / with \ on Windows.
+#
+# $1: File or directory path.
+# Output: host-specific path to the same file.
+# Examples:
+#   $(call compiler-host-path,foo/bar /zoo) ->
+#       "foo/bar /zoo" (Unix) or
+#       "foo\bar \zoo" (on Windows)
+#
+compiler-host-path = $(strip $(subst /,$(COMPILER_SEP),$1))
+
+# Convert a list of include paths into the corresponding compiler flags.
+# $1: List of include paths, possibly empty.
+# Output: List of compiler flags.
+# Example:
+#   $(call include-flags-for,foo bar/zoo)
+#        -> "-Ifoo -Ibar/zoo" (on Unix)
+#        -> "/Ifoo /Ibar\zoo  (on Windows with cl.exe).
+#
+compiler-include-flags-for = $(strip \
+  $(foreach _path,$1,$I$(call compiler-host-path,$(_path))))
+
+# Convert a list of source files into the corresponding list of object files.
+# $1: List of source file paths.
+# Output: List of object file paths.
+# Example:
+#    $(call compiler-obj-from-src,foo/bar.c zoo.c)
+#      => "objs/bar.o objs/zoo.o"
+#      or "objs/bar.obj obj/zoo.obj"
+compiler-obj-from-src = $(strip \
+  $(foreach _src,$1,$(OBJ_DIR)/$(notdir $(_src:%.c=%.$O))))
+
+# Compile a single object file from one source file and
+# its dependencies. May include additional include paths as well.
+# The object file is computed through compiler-obj-from-src.
+#
+# $1: Object file.
+# $2: Source file + other dependencies
+# $3: Optional list of include paths.
+# $4: Optional list of extra compilation flags.
+# $5: Optional variable name. If not empty, the object file path will be
+# appended to it.
+# Example:
+#   $(call ft-compile,<src-file>,<dependencies>[,<includes>]))
+compiler-cc-obj = $(eval $(call compiler-cc-obj-eval,$1,$2,$3,$4,$5))
+define compiler-cc-obj-eval
+_obj := $1
+_src := $$(firstword $2)
+$$(_obj): $2
+	$$(CC) $$(ANSIFLAGS) $$(call compiler-include-flags-for,$3) \
+	$4 $$T$$(call compiler-host-path,$$@ $$<)
+
+$$(if $(strip $5),$$(eval $5 += $$(_obj)))
+endef
+
+# Compile a list of source files, computing their object file automatically
+# with compiler-obj-from-src.
+# $1: List of source files.
+# $2: List of their common dependencies.
+# $3: List of include directories.
+# $4: List of extra compiler flags.
+# $5: Optional variable name. If defined, object file paths will be appended to it.
+compiler-cc-sources = $(strip $(foreach _src,$1,\
+  $(call compiler-cc-obj,$(call compiler-obj-from-src,$(_src)),$(_src) $2,$3,$4,$5)))
+
+endif  # __utils_compiler_mk_included
diff --git a/builds/utils/host.mk b/builds/utils/host.mk
new file mode 100644
index 000000000..9de511037
--- /dev/null
+++ b/builds/utils/host.mk
@@ -0,0 +1,81 @@
+#
+# FreeType 2 host-specific GNU Make functions
+#
+
+# The host platform is the system used to build the FreeType binaries, even
+# if they may later run on a different target system (in the case of
+# cross-compiling).
+
+ifndef __utils_host_mk_included
+__utils_host_mk_included := true
+
+include $(TOP_DIR)/builds/utils/common.mk
+
+# DELETE: Shell command to delete one ore more files.
+# NO_OUTPUT: Optional shell command suffix to disable output of DELETE command.
+# PLATFORM: A string describing the host platform where the build happens.
+$(call check-required-variables, DELETE PLATFORM)
+
+# non-empty if this is a DOS-like platform
+_dos_platform := $(strip $(findstring $(PLATFORM),dos windows os2))
+
+# Convert a Unix-style path to a host-specific path.
+# E.g. used to convert / into \ on Windows, before invoking shell utilities.
+#
+# NOTE: compiler-host-path should be used for paths as used by the compiler
+# because some compilers expect Unix paths even on Windows.
+#
+# $1: input path
+# out: converted path, if needed.
+#
+ifneq ($(_dos_platform),)
+host-path = $(subst /,\,$1)
+else
+host-path = $1
+endif
+
+# cmd-rm expands to a shell command that can be used to remove files.
+# $1: List of files to remove, using / as the directory separator.
+ifneq ($(_dos_platform),)
+cmd-rm = $(DELETE) $(call host-path,$1) $(NO_OUTPUT)
+else
+cmd-rm = rm -f $1 2>/dev/null
+endif
+
+# cmd-echo expands to a shell command that can be used to print a simple
+# message to the standard output. Such messages cannot contain a comma or
+# a newline due to GNU Make limitations. For more complex messages, use
+# cmd-echo-begin with cmd-echo-end. Example:
+#
+#   $(call cmd-echo,This is a simple message)
+#
+# $1: Message. NOTE: It cannot contain a comma due to GNU Make limitations.
+#
+ifneq ($(_dos_platform),)
+  cmd-echo = echo $1
+else
+  cmd-echo = echo "$1"
+endif
+
+# cmd-echo-begin and cmd-echo-end can be used to expand a shell command that
+# print a complex message to standard output. E.g.:
+#
+#  $(call cmd-echo-begin)This is a message, with a comma in it$(call cmd-echo-end)
+#
+ifneq ($(_dos_platform),)
+  cmd-echo-begin = echo$(space)
+  cmd-echo-end =
+else
+  cmd-echo-begin = echo "
+  cmd-echo-end = "
+endif
+
+# A convenience function that expands as a shell command that appends a
+# simple message to a given file.
+#
+# $1: Simple message.
+# $2: Destination file.
+#
+cmd-echo-to = $(call cmd-echo,$1) >> $(call host-path,$2)
+
+endif  # __utils_host_mk_included
diff --git a/src/autofit/module.mk b/src/autofit/module.mk
index c32781f47..d07902bd1 100644
--- a/src/autofit/module.mk
+++ b/src/autofit/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += AUTOFIT_MODULE
-
-define AUTOFIT_MODULE
-$(OPEN_DRIVER) FT_Module_Class, autofit_module_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)autofit   $(ECHO_DRIVER_DESC)automatic hinting module$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := autofit
+MODULE_CLASS_TYPE := module
+MODULE_CLASS_DESCRIPTION := Automatic hinting module
 
 # EOF
diff --git a/src/bdf/module.mk b/src/bdf/module.mk
index fe06ae8e0..cd287acd1 100644
--- a/src/bdf/module.mk
+++ b/src/bdf/module.mk
@@ -23,12 +23,9 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 # THE SOFTWARE.
 
-
-FTMODULE_H_COMMANDS += BDF_DRIVER
-
-define BDF_DRIVER
-$(OPEN_DRIVER) FT_Driver_ClassRec, bdf_driver_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)bdf       $(ECHO_DRIVER_DESC)bdf bitmap fonts$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := bdf
+MODULE_CLASS_TYPE := driver
+MODULE_CLASS_DESCRIPTION := BDF bitmap fonts
 
 # EOF
diff --git a/src/cff/module.mk b/src/cff/module.mk
index bd728c6a3..15eae5171 100644
--- a/src/cff/module.mk
+++ b/src/cff/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += CFF_DRIVER
-
-define CFF_DRIVER
-$(OPEN_DRIVER) FT_Driver_ClassRec, cff_driver_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)cff       $(ECHO_DRIVER_DESC)OpenType fonts with extension *.otf$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := cff
+MODULE_CLASS_TYPE := driver
+MODULE_CLASS_DESCRIPTION := OpenType fonts with extension *.otf
 
 # EOF
diff --git a/src/cid/module.mk b/src/cid/module.mk
index 9fb02235e..6369a5aea 100644
--- a/src/cid/module.mk
+++ b/src/cid/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += TYPE1CID_DRIVER
-
-define TYPE1CID_DRIVER
-$(OPEN_DRIVER) FT_Driver_ClassRec, t1cid_driver_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)cid       $(ECHO_DRIVER_DESC)Postscript CID-keyed fonts, no known extension$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := t1cid
+MODULE_CLASS_TYPE := driver
+MODULE_CLASS_DESCRIPTION := Postscript CID-keyed fonts; no known extension
 
 # EOF
diff --git a/src/gxvalid/module.mk b/src/gxvalid/module.mk
index e7d408df9..887551878 100644
--- a/src/gxvalid/module.mk
+++ b/src/gxvalid/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += GXVALID_MODULE
-
-define GXVALID_MODULE
-$(OPEN_DRIVER) FT_Module_Class, gxv_module_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)gxvalid   $(ECHO_DRIVER_DESC)TrueTypeGX/AAT validation module$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := gxv
+MODULE_CLASS_TYPE := module
+MODULE_CLASS_DESCRIPTION := TrueTypeGX/AAT validation module
 
 # EOF
diff --git a/src/otvalid/module.mk b/src/otvalid/module.mk
index 67b9820d8..d7291fa2c 100644
--- a/src/otvalid/module.mk
+++ b/src/otvalid/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += OTVALID_MODULE
-
-define OTVALID_MODULE
-$(OPEN_DRIVER) FT_Module_Class, otv_module_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)otvalid   $(ECHO_DRIVER_DESC)OpenType validation module$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := otv
+MODULE_CLASS_TYPE := module
+MODULE_CLASS_DESCRIPTION := OpenType validation module
 
 # EOF
diff --git a/src/pcf/module.mk b/src/pcf/module.mk
index df383ff0f..0ecdcd865 100644
--- a/src/pcf/module.mk
+++ b/src/pcf/module.mk
@@ -23,12 +23,9 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 # THE SOFTWARE.
 
-
-FTMODULE_H_COMMANDS += PCF_DRIVER
-
-define PCF_DRIVER
-$(OPEN_DRIVER) FT_Driver_ClassRec, pcf_driver_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)pcf       $(ECHO_DRIVER_DESC)pcf bitmap fonts$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := pcf
+MODULE_CLASS_TYPE := driver
+MODULE_CLASS_DESCRIPTION := PCF bitmap fonts
 
 # EOF
diff --git a/src/pfr/module.mk b/src/pfr/module.mk
index 762353dda..006793bb1 100644
--- a/src/pfr/module.mk
+++ b/src/pfr/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += PFR_DRIVER
-
-define PFR_DRIVER
-$(OPEN_DRIVER) FT_Driver_ClassRec, pfr_driver_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)pfr       $(ECHO_DRIVER_DESC)PFR/TrueDoc font files with extension *.pfr$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := pfr
+MODULE_CLASS_TYPE := driver
+MODULE_CLASS_DESCRIPTION := PFR/TrueDoc font files with extension *.pfr
 
 # EOF
diff --git a/src/psaux/module.mk b/src/psaux/module.mk
index 651db0142..fed4bdd16 100644
--- a/src/psaux/module.mk
+++ b/src/psaux/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += PSAUX_MODULE
-
-define PSAUX_MODULE
-$(OPEN_DRIVER) FT_Module_Class, psaux_module_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)psaux     $(ECHO_DRIVER_DESC)Postscript Type 1 & Type 2 helper module$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := psaux
+MODULE_CLASS_TYPE := module
+MODULE_CLASS_DESCRIPTION := Postscript Type 1 & Type 2 helper module
 
 # EOF
diff --git a/src/pshinter/module.mk b/src/pshinter/module.mk
index b440d2e76..c4ec51ad4 100644
--- a/src/pshinter/module.mk
+++ b/src/pshinter/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += PSHINTER_MODULE
-
-define PSHINTER_MODULE
-$(OPEN_DRIVER) FT_Module_Class, pshinter_module_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)pshinter  $(ECHO_DRIVER_DESC)Postscript hinter module$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := pshinter
+MODULE_CLASS_TYPE := module
+MODULE_CLASS_DESCRIPTION := Postscript hinter module
 
 # EOF
diff --git a/src/psnames/module.mk b/src/psnames/module.mk
index 675bb3713..c2b8ae2f1 100644
--- a/src/psnames/module.mk
+++ b/src/psnames/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += PSNAMES_MODULE
-
-define PSNAMES_MODULE
-$(OPEN_DRIVER) FT_Module_Class, psnames_module_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)psnames   $(ECHO_DRIVER_DESC)Postscript & Unicode Glyph name handling$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := psnames
+MODULE_CLASS_TYPE := module
+MODULE_CLASS_DESCRIPTION := Postscript & Unicode Glyph name handling
 
 # EOF
diff --git a/src/raster/module.mk b/src/raster/module.mk
index 3600732b1..fecb19b35 100644
--- a/src/raster/module.mk
+++ b/src/raster/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += RASTER_MODULE
-
-define RASTER_MODULE
-$(OPEN_DRIVER) FT_Renderer_Class, ft_raster1_renderer_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)raster    $(ECHO_DRIVER_DESC)monochrome bitmap renderer$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := ft_raster1
+MODULE_CLASS_TYPE := renderer
+MODULE_CLASS_DESCRIPTION := Monochrome bitmap renderer
 
 # EOF
diff --git a/src/sfnt/module.mk b/src/sfnt/module.mk
index 0f459d842..1073ad422 100644
--- a/src/sfnt/module.mk
+++ b/src/sfnt/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += SFNT_MODULE
-
-define SFNT_MODULE
-$(OPEN_DRIVER) FT_Module_Class, sfnt_module_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)sfnt      $(ECHO_DRIVER_DESC)helper module for TrueType & OpenType formats$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := sfnt
+MODULE_CLASS_TYPE := module
+MODULE_CLASS_DESCRIPTION := Helper module for TrueType & OpenType formats
 
 # EOF
diff --git a/src/smooth/module.mk b/src/smooth/module.mk
index ad8b47dab..06d895ba9 100644
--- a/src/smooth/module.mk
+++ b/src/smooth/module.mk
@@ -12,16 +12,17 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := ft_smooth
+MODULE_CLASS_TYPE := renderer
+MODULE_CLASS_DESCRIPTION := anti-aliased bitnap renderer
 
-FTMODULE_H_COMMANDS += SMOOTH_RENDERER
+MODULE_CLASS_NAME_2 := ft_smooth_lcd
+MODULE_CLASS_TYPE_2 := renderer
+MODULE_CLASS_DESCRIPTION_2 := anti-aliased bitnap renderer for LCDs
 
-define SMOOTH_RENDERER
-$(OPEN_DRIVER) FT_Renderer_Class, ft_smooth_renderer_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)smooth    $(ECHO_DRIVER_DESC)anti-aliased bitmap renderer$(ECHO_DRIVER_DONE)
-$(OPEN_DRIVER) FT_Renderer_Class, ft_smooth_lcd_renderer_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)smooth    $(ECHO_DRIVER_DESC)anti-aliased bitmap renderer for LCDs$(ECHO_DRIVER_DONE)
-$(OPEN_DRIVER) FT_Renderer_Class, ft_smooth_lcdv_renderer_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)smooth    $(ECHO_DRIVER_DESC)anti-aliased bitmap renderer for vertical LCDs$(ECHO_DRIVER_DONE)
-endef
+MODULE_CLASS_NAME_3 := ft_smooth_lcdv
+MODULE_CLASS_TYPE_3 := renderer
+MODULE_CLASS_DESCRIPTION_3 := anti-aliased bitnap renderer for vertical LCDs
 
 # EOF
diff --git a/src/truetype/module.mk b/src/truetype/module.mk
index 2d8d39d1f..e2385d156 100644
--- a/src/truetype/module.mk
+++ b/src/truetype/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += TRUETYPE_DRIVER
-
-define TRUETYPE_DRIVER
-$(OPEN_DRIVER) FT_Driver_ClassRec, tt_driver_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)truetype  $(ECHO_DRIVER_DESC)Windows/Mac font files with extension *.ttf or *.ttc$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := tt
+MODULE_CLASS_TYPE := driver
+MODULE_CLASS_DESCRIPTION := Windows/Mac font files with extension *.ttf or *.ttc
 
 # EOF
diff --git a/src/type1/module.mk b/src/type1/module.mk
index cffb774b4..58eeb44c5 100644
--- a/src/type1/module.mk
+++ b/src/type1/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += TYPE1_DRIVER
-
-define TYPE1_DRIVER
-$(OPEN_DRIVER) FT_Driver_ClassRec, t1_driver_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)type1     $(ECHO_DRIVER_DESC)Postscript font files with extension *.pfa or *.pfb$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := t1
+MODULE_CLASS_TYPE := driver
+MODULE_CLASS_DESCRIPTION := Postscript font files with extension *.pfr or *.pfb
 
 # EOF
diff --git a/src/type42/module.mk b/src/type42/module.mk
index 6ef3a95ea..819012758 100644
--- a/src/type42/module.mk
+++ b/src/type42/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += TYPE42_DRIVER
-
-define TYPE42_DRIVER
-$(OPEN_DRIVER) FT_Driver_ClassRec, t42_driver_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)type42    $(ECHO_DRIVER_DESC)Type 42 font files with no known extension$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := t42
+MODULE_CLASS_TYPE := driver
+MODULE_CLASS_DESCRIPTION := Type 42 font files with no known extension
 
 # EOF
diff --git a/src/winfonts/module.mk b/src/winfonts/module.mk
index 4614c55fd..590cff00d 100644
--- a/src/winfonts/module.mk
+++ b/src/winfonts/module.mk
@@ -12,12 +12,9 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-FTMODULE_H_COMMANDS += WINDOWS_DRIVER
-
-define WINDOWS_DRIVER
-$(OPEN_DRIVER) FT_Driver_ClassRec, winfnt_driver_class $(CLOSE_DRIVER)
-$(ECHO_DRIVER)winfnt    $(ECHO_DRIVER_DESC)Windows bitmap fonts with extension *.fnt or *.fon$(ECHO_DRIVER_DONE)
-endef
+# See builds/modules.mk for documentation about this file's content
+MODULE_CLASS_NAME := winfnt
+MODULE_CLASS_TYPE := driver
+MODULE_CLASS_DESCRIPTION := Windows bitmap fonts with extension *.fnt or *.fon
 
 # EOF
-- 
2.20.1

From f57ad9ae528e6348b8baa1621f4bb4d0e88eea44 Mon Sep 17 00:00:00 2001
From: David Turner <david.turner....@gmail.com>
Date: Thu, 30 Apr 2020 18:07:28 +0200
Subject: [build] Simplify rules.mk syntax

Simplify the content of rules.mk files to ensure
they only need to include variable declarations
that look like:

  MODULE_SOURCES := <list-of-sources>
  MODULE_HEADERS := <list-of-headers>
  MODULE_WRAPPER := <single-wrapper-source-file>

This is done by adding sufficient 'magic' to
builds/freetype.mk to parse these files and get
something that is equivalent to their previous
content.

Note that this magic will likely be replaced by
something else that can parse the rules.mk and
modules.mk and does not rely on custom GNU Make
functions.
---
 builds/exports.mk     |  12 +--
 builds/freetype.mk    | 221 ++++++++++++++++++++++++++++++------------
 src/autofit/rules.mk  |  97 +++++-------------
 src/base/rules.mk     | 108 +++++----------------
 src/bdf/rules.mk      |  62 ++----------
 src/bzip2/rules.mk    |  49 +---------
 src/cache/rules.mk    |  88 ++++-------------
 src/cff/rules.mk      |  73 +++-----------
 src/cid/rules.mk      |  70 +++----------
 src/gxvalid/rules.mk  | 115 +++++++---------------
 src/gzip/rules.mk     |  84 ++++++----------
 src/lzw/rules.mk      |  57 +----------
 src/otvalid/rules.mk  |  83 ++++------------
 src/pcf/rules.mk      |  60 ++----------
 src/pfr/rules.mk      |  74 +++-----------
 src/psaux/rules.mk    |  99 +++++--------------
 src/pshinter/rules.mk |  66 ++-----------
 src/psnames/rules.mk  |  61 ++----------
 src/raster/rules.mk   |  61 ++----------
 src/sfnt/rules.mk     |  91 +++++------------
 src/smooth/rules.mk   |  63 ++----------
 src/truetype/rules.mk |  74 +++-----------
 src/type1/rules.mk    |  74 +++-----------
 src/type42/rules.mk   |  64 ++----------
 src/winfonts/rules.mk |  55 +----------
 25 files changed, 498 insertions(+), 1463 deletions(-)

diff --git a/builds/exports.mk b/builds/exports.mk
index eba966875..fdb8cb4c1 100644
--- a/builds/exports.mk
+++ b/builds/exports.mk
@@ -51,8 +51,8 @@ ifneq ($(EXPORTS_LIST),)
   #
   # Note that $(APINAMES_OPTIONS) is empty, except for Windows compilers.
   #
-  APINAMES_SRC := $(subst /,$(SEP),$(TOP_DIR)/src/tools/apinames.c)
-  APINAMES_EXE := $(subst /,$(SEP),$(OBJ_DIR)/apinames$(E_BUILD))
+  APINAMES_SRC := $(call host-path,$(TOP_DIR)/src/tools/apinames.c)
+  APINAMES_EXE := $(call host-path,$(OBJ_DIR)/apinames$(E_BUILD))
 
   $(APINAMES_EXE): $(APINAMES_SRC)
 	  $(CCexe) $(CCexe_CFLAGS) $(TE)$@ $< $(CCexe_LDFLAGS)
@@ -65,16 +65,16 @@ ifneq ($(EXPORTS_LIST),)
   # debuggers, to the EXPORTS_LIST.
   #
   $(EXPORTS_LIST): $(APINAMES_EXE) $(PUBLIC_HEADERS)
-	  $(subst /,$(SEP),$(APINAMES_EXE)) -o$@ $(APINAMES_OPTIONS) $(PUBLIC_HEADERS)
-	  @echo TT_New_Context >> $(EXPORTS_LIST)
-	  @echo TT_RunIns >> $(EXPORTS_LIST)
+	  $(call host-path,$(APINAMES_EXE)) -o$@ $(APINAMES_OPTIONS) $(PUBLIC_HEADERS)
+	  @$(call cmd-echo-to,TT_New_Context,$(EXPORTS_LIST))
+	  @$(call cmd-echo-to,TT_RunIns,$(EXPORTS_LIST))
 
   $(PROJECT_LIBRARY): $(EXPORTS_LIST)
 
   CLEAN += $(EXPORTS_LIST) \
            $(APINAMES_EXE)
 
-endif
+endif  # EXPORTS_LIST
 
 
 # EOF
diff --git a/builds/freetype.mk b/builds/freetype.mk
index a0d7c5803..85034a884 100644
--- a/builds/freetype.mk
+++ b/builds/freetype.mk
@@ -21,7 +21,7 @@
 # environment, or on the command line) are used:
 #
 #   BUILD_DIR      The architecture dependent directory,
-#                  e.g. `$(TOP_DIR)/builds/unix'.  Added to INCLUDES also.
+#                  e.g. `$(TOP_DIR)/builds/unix'.  Added to include dirs also.
 #
 #   OBJ_DIR        The directory in which object files are created.
 #
@@ -29,9 +29,7 @@
 #
 #   DOC_DIR        The directory in which the API reference is created.
 #
-#   INCLUDES       A list of directories to be included additionally.
-#
-#   DEVEL_DIR      Development directory which is added to the INCLUDES
+#   DEVEL_DIR      Development directory which is added to the include dirs
 #                  variable before the standard include directories.
 #
 #   CFLAGS         Compilation flags.  This overrides the default settings
@@ -53,9 +51,6 @@
 #                  object builds, respectively).  Set up in
 #                  `src/base/rules.mk'.
 #
-#   BASE_EXT_OBJ   A list of base extension objects.  Set up in
-#                  `src/base/rules.mk'.
-#
 #   DRV_OBJ_S
 #   DRV_OBJ_M      A list of driver objects (for single object and multiple
 #                  object builds, respectively).  Set up cumulatively in
@@ -110,6 +105,27 @@ DOC_DIR ?= $(TOP_DIR)/docs
 #
 PROJECT_LIBRARY := $(LIB_DIR)/$(LIBRARY).$A
 
+# Disable built-in rules.
+.SUFFIXES:
+
+# Define these now with := to ensure that future += statement will operate
+# immediately.
+
+# The list of object files belonging to a single build.
+# Updated by $(call ft-define-module function, ...)
+#
+DRV_OBJS_S :=
+
+# The list of object files belonging to a multi build.
+# Updated by $(call ft-define-module function, ...)
+#
+DRV_OBJS_M :=
+
+#
+# Define a few useful functions here!
+#
+
+include $(TOP_DIR)/builds/utils/compiler.mk
 
 # include paths
 #
@@ -119,12 +135,8 @@ PROJECT_LIBRARY := $(LIB_DIR)/$(LIBRARY).$A
 #                 in the `builds/<system>' directory, as these files will
 #                 override the default sources.
 #
-INCLUDES := $(subst /,$(COMPILER_SEP),$(OBJ_DIR) \
-                                      $(DEVEL_DIR) \
-                                      $(BUILD_DIR) \
-                                      $(TOP_DIR)/include)
-
-INCLUDE_FLAGS := $(INCLUDES:%=$I%)
+INCLUDE_FLAGS := $(call compiler-include-flags-for,\
+  $(OBJ_DIR) $(DEVEL_DIR) $(BUILD_DIR) $(TOP_DIR)/include)
 
 ifdef DEVEL_DIR
   # We assume that all library dependencies for FreeType are fulfilled for a
@@ -166,17 +178,6 @@ FT_CFLAGS  = $(CPPFLAGS) \
              $DFT_CONFIG_MODULES_H="<ftmodule.h>" \
              $(FTOPTION_FLAG)
 
-
-# Include the `exports' rules file.
-#
-include $(TOP_DIR)/builds/exports.mk
-
-
-# Initialize the list of objects.
-#
-OBJECTS_LIST :=
-
-
 # Define $(PUBLIC_H) as the list of all public header files located in
 # `$(TOP_DIR)/include/freetype'.  $(INTERNAL_H), and $(CONFIG_H) are defined
 # similarly.
@@ -195,37 +196,140 @@ DEVEL_H    := $(wildcard $(TOP_DIR)/devel/*.h)
 
 FREETYPE_H := $(PUBLIC_H) $(INTERNAL_H) $(CONFIG_H) $(DEVEL_H)
 
+# Initialize the list of objects.
+#
+OBJECTS_LIST :=
+
+# Compile a list of FreeType source files.
+#
+# $1: List of source files.
+# $2: List of their common dependencies.
+# $3: List of include directories.
+# $4: List of extra compiler flags.
+# $5: Optional variable name to collect object file paths. If not set, the
+# the paths will be appended to OBJECTS_LIST.
+ft-compile-sources = $(call compiler-cc-sources,\
+  $1, \
+  $2 $(FREETYPE_H),\
+  $3, \
+  $(INCLUDE_FLAGS) $(FT_CFLAGS) $4, \
+  $(if $(strip $5),$5,OBJECTS_LIST) \
+)
+
+# Define a simple module to be built with the FreeType library:
+#
+# $1: Module source directory.
+# $2: List of header files, relative to $1
+# $3: List of individual source files, relative to $1
+# $4: Wrapper source file (which includes all sources in $3), relative to $1.
+#
+ft-define-module = $(eval $(call ft-define-module-ev,$(1),$(2),$(3),$(4)))
+define ft-define-module-ev =
+_module_dir     := $1
+
+# If MODULE_DIR is empty, don't add it as a prefix to headers and sources.
+ifeq ($$(_module_dir),)
+_module_headers := $2
+_module_sources := $3
+else
+_module_headers := $$(addprefix $$(_module_dir)/,$2)
+_module_sources := $$(addprefix $$(_module_dir)/,$3)
+endif
+_module_wrapper := $4
+
+# Compile single sources.
+$$(call ft-compile-sources,\
+  $$(_module_sources),     \
+  $$(_module_headers),     \
+  $$(_module_dir),         \
+  ,                        \
+  DRV_OBJS_M               \
+)
+
+# Compile the wrapper source file
+$$(call ft-compile-sources,                      \
+  $$(_module_dir)/$$(_module_wrapper),           \
+  $$(_module_headers) $$(_module_sources),       \
+  $$(_module_dir),                               \
+  ,                                              \
+  DRV_OBJS_S                                     \
+)                                                \
+
+_module_sources :=
+_module_headers :=
+_module_wrapper :=
+
+endef  # ft-define-module-ev
+
+# Define a single base extension. This is a single source file that
+# implements a part of the FreeType API if it is listed in BASE_EXTENSIONS
+#
+#  $1: extension source file name (e.x. ftbbox.c)
+#  $2: headers this extension depends on, if any.
+ft-add-base-extension = $(call ft-compile-sources, $(SRC_DIR)/base/$1,$(addprefix $(SRC_DIR)/base/,$2),$(SRC_DIR)/base)
 
-FT_COMPILE := $(CC) $(ANSIFLAGS) $(INCLUDE_FLAGS) $(FT_CFLAGS)
+# Include the `exports' rules file.
+#
+include $(TOP_DIR)/builds/exports.mk
 
 # ftsystem component
 #
 FTSYS_SRC ?= $(BASE_DIR)/ftsystem.c
+$(call ft-compile-sources, $(FTSYS_SRC))
 
-FTSYS_OBJ := $(OBJ_DIR)/ftsystem.$O
 
-OBJECTS_LIST += $(FTSYS_OBJ)
+# ftdebug component
+#
+FTDEBUG_SRC ?= $(BASE_DIR)/ftdebug.c
+$(call ft-compile-sources, $(FTDEBUG_SRC))
 
-$(FTSYS_OBJ): $(FTSYS_SRC) $(FREETYPE_H)
-	$(FT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
 
+check-missing-var-definition = $(strip \
+    $(if $(strip $($1)),,$(error "$1 definition missing from $2")))
 
-# ftdebug component
+# Use internally to include the rules.mk of a given module.
 #
-FTDEBUG_SRC ?= $(BASE_DIR)/ftdebug.c
+# The following variables are defined before the module is included:
+#
+#   MODULE_DIR: module directory path.
+#   MODULE_FILE: path to the rules.mk itself.
+#
+# The MODULE_FILE should define the following variables:
+#
+#   MODULE_SOURCES: list of individual source files for a multi build.
+#      Each path is relative to MODULE_DIR.
+#
+#   MODULE_HEADERS: list of module header files, relative to MODULE_DIR.
+#
+#   MODULE_WRAPPER: The wrapper C source that includes all others in single
+#      builds. Relative to MODULE_DIR.
+#
+# $1: Module source directory (i.e. src/cff).
+define ft-include-module-rules-eval
+MODULE_DIR := $1
+MODULE_FILE := $$(MODULE_DIR)/rules.mk
+MODULE_HEADERS :=
+MODULE_SOURCES :=
+MODULE_WRAPPER :=
+MODULE_VARNAME :=
+MODULE_CLASS   :=
+MODULE_DESCRIPTION :=
 
-FTDEBUG_OBJ := $(OBJ_DIR)/ftdebug.$O
+include $$(MODULE_FILE)
 
-OBJECTS_LIST += $(FTDEBUG_OBJ)
+$$(call check-missing-var-definition,MODULE_SOURCES,$$(MODULE_FILE))
+$$(call check-missing-var-definition,MODULE_WRAPPER,$$(MODULE_FILE))
 
-$(FTDEBUG_OBJ): $(FTDEBUG_SRC) $(FREETYPE_H)
-	$(FT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+$$(call ft-define-module,\
+    $$(MODULE_DIR),\
+    $$(MODULE_HEADERS),\
+    $$(MODULE_SOURCES),\
+    $$(MODULE_WRAPPER))
 
+endef  # ft-include-module-rules-eval
 
-# Include all rule files from FreeType components.
-#
-include $(SRC_DIR)/base/rules.mk
-include $(patsubst %,$(SRC_DIR)/%/rules.mk,$(MODULES))
+$(foreach _module,base $(MODULES),\
+  $(eval $(call ft-include-module-rules-eval,$(SRC_DIR)/$(_module))))
 
 
 # ftinit component
@@ -240,13 +344,7 @@ include $(patsubst %,$(SRC_DIR)/%/rules.mk,$(MODULES))
 #   single `ftinit.c' source.
 #
 FTINIT_SRC := $(BASE_DIR)/ftinit.c
-FTINIT_OBJ := $(OBJ_DIR)/ftinit.$O
-
-OBJECTS_LIST += $(FTINIT_OBJ)
-
-$(FTINIT_OBJ): $(FTINIT_SRC) $(FREETYPE_H)
-	$(FT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
+$(call ft-compile-sources, $(FTINIT_SRC))
 
 # ftver component
 #
@@ -263,12 +361,15 @@ ifneq ($(RC),)
 	$(RC) -o $@ $<
 endif
 
+# Handle for the base extensions.
+$(foreach _ext,$(BASE_EXTENSIONS),\
+  $(call ft-add-base-extension,$(_ext),$(BASE_HEADERS)) \
+)
 
 # All FreeType library objects.
 #
-OBJ_M := $(BASE_OBJ_M) $(BASE_EXT_OBJ) $(DRV_OBJS_M)
-OBJ_S := $(BASE_OBJ_S) $(BASE_EXT_OBJ) $(DRV_OBJS_S)
-
+OBJ_M := $(BASE_OBJ_M) $(DRV_OBJS_M)
+OBJ_S := $(BASE_OBJ_S) $(DRV_OBJS_S)
 
 # The target `multi' on the Make command line indicates that we want to
 # compile each source file independently.
@@ -317,9 +418,9 @@ refdoc:
 # `mkdocs.yml' are relative to the current working directory.
 #
 VENV_NAME  := env
-VENV_DIR   := $(DOC_DIR)$(SEP)$(VENV_NAME)
-ENV_PYTHON := $(VENV_DIR)$(SEP)$(BIN)$(SEP)$(PYTHON)
-ENV_PIP    := $(VENV_DIR)$(SEP)$(BIN)$(SEP)$(PIP)
+VENV_DIR   := $(call host-path,$(DOC_DIR)/$(VENV_NAME))
+ENV_PYTHON := $(call host-path,$(VENV_DIR)/$(BIN)/$(PYTHON))
+ENV_PIP    := $(call host-path,$(VENV_DIR)/$(BIN)/$(PIP))
 
 refdoc-venv:
 	@echo Setting up virtualenv for Python...
@@ -345,11 +446,11 @@ refdoc-venv:
 # on all systems though.
 #
 clean_project_std:
-	-$(DELETE) $(BASE_OBJECTS) $(OBJ_M) $(OBJ_S) $(CLEAN)
+	-$(call cmd-rm,$(BASE_OBJECTS) $(OBJ_M) $(OBJ_S) $(CLEAN))
 
 distclean_project_std: clean_project_std
-	-$(DELETE) $(PROJECT_LIBRARY)
-	-$(DELETE) *.orig *~ core *.core $(DISTCLEAN)
+	-$(call cmd-rm,$PROJECT_LIBRARY)
+	-$(call cmd-rm,*.orig *~ core *.core $(DISTCLEAN))
 
 
 .PHONY: clean_project_dos distclean_project_dos
@@ -361,10 +462,10 @@ distclean_project_std: clean_project_std
 # working correctly on Win9x.
 #
 clean_project_dos:
-	-$(DELETE) $(subst /,$(SEP),$(OBJ_DIR)/*.$O $(CLEAN) $(NO_OUTPUT))
+	-$(call cmd-rm, $(OBJ_DIR)/*.$O $(CLEAN))
 
 distclean_project_dos: clean_project_dos
-	-$(DELETE) $(subst /,$(SEP),$(PROJECT_LIBRARY) $(DISTCLEAN) $(NO_OUTPUT))
+	-$(call cmd-rm, $(PROJECT_LIBRARY) $(DISTCLEAN))
 
 
 .PHONY: remove_config_mk remove_ftmodule_h
@@ -372,12 +473,12 @@ distclean_project_dos: clean_project_dos
 # Remove configuration file (used for distclean).
 #
 remove_config_mk:
-	-$(DELETE) $(subst /,$(SEP),$(CONFIG_MK) $(NO_OUTPUT))
+	-$(call cmd-rm,$(CONFIG_MK))
 
 # Remove module list (used for distclean).
 #
 remove_ftmodule_h:
-	-$(DELETE) $(subst /,$(SEP),$(FTMODULE_H) $(NO_OUTPUT))
+	-$(call cmd-rm,$(FTMODULE_H))
 
 
 .PHONY: clean distclean
@@ -388,7 +489,7 @@ remove_ftmodule_h:
 #
 clean: clean_project
 distclean: distclean_project remove_config_mk remove_ftmodule_h
-	-$(DELETE) $(subst /,$(SEP),$(DOC_DIR)/*.html $(NO_OUTPUT))
+	-$(call cmd-rm, $(DOC_DIR)/*.html)
 
 
 # EOF
diff --git a/src/autofit/rules.mk b/src/autofit/rules.mk
index 553ddce6b..03f48a5a7 100644
--- a/src/autofit/rules.mk
+++ b/src/autofit/rules.mk
@@ -12,77 +12,30 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# AUTOF driver directory
-#
-AUTOF_DIR := $(SRC_DIR)/autofit
-
-
-# compilation flags for the driver
-#
-AUTOF_COMPILE := $(CC) $(ANSIFLAGS)                              \
-                       $I$(subst /,$(COMPILER_SEP),$(AUTOF_DIR)) \
-                       $(INCLUDE_FLAGS)                          \
-                       $(FT_CFLAGS)
-
-
-# AUTOF driver sources (i.e., C files)
-#
-AUTOF_DRV_SRC := $(AUTOF_DIR)/afangles.c \
-                 $(AUTOF_DIR)/afblue.c   \
-                 $(AUTOF_DIR)/afcjk.c    \
-                 $(AUTOF_DIR)/afdummy.c  \
-                 $(AUTOF_DIR)/afglobal.c \
-                 $(AUTOF_DIR)/afhints.c  \
-                 $(AUTOF_DIR)/afindic.c  \
-                 $(AUTOF_DIR)/aflatin.c  \
-                 $(AUTOF_DIR)/afloader.c \
-                 $(AUTOF_DIR)/afmodule.c \
-                 $(AUTOF_DIR)/afranges.c \
-                 $(AUTOF_DIR)/afshaper.c \
-                 $(AUTOF_DIR)/afwarp.c
-
-# AUTOF driver headers
-#
-AUTOF_DRV_H := $(AUTOF_DRV_SRC:%c=%h)  \
-               $(AUTOF_DIR)/afcover.h  \
-               $(AUTOF_DIR)/aferrors.h \
-               $(AUTOF_DIR)/afscript.h \
-               $(AUTOF_DIR)/afstyles.h \
-               $(AUTOF_DIR)/aftypes.h  \
-               $(AUTOF_DIR)/afwrtsys.h
-
-
-# AUTOF driver object(s)
-#
-#   AUTOF_DRV_OBJ_M is used during `multi' builds.
-#   AUTOF_DRV_OBJ_S is used during `single' builds.
-#
-AUTOF_DRV_OBJ_M := $(AUTOF_DRV_SRC:$(AUTOF_DIR)/%.c=$(OBJ_DIR)/%.$O)
-AUTOF_DRV_OBJ_S := $(OBJ_DIR)/autofit.$O
-
-# AUTOF driver source file for single build
-#
-AUTOF_DRV_SRC_S := $(AUTOF_DIR)/autofit.c
-
-
-# AUTOF driver - single object
-#
-$(AUTOF_DRV_OBJ_S): $(AUTOF_DRV_SRC_S) $(AUTOF_DRV_SRC) \
-                   $(FREETYPE_H) $(AUTOF_DRV_H)
-	$(AUTOF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(AUTOF_DRV_SRC_S))
-
-
-# AUTOF driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(AUTOF_DIR)/%.c $(FREETYPE_H) $(AUTOF_DRV_H)
-	$(AUTOF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(AUTOF_DRV_OBJ_S)
-DRV_OBJS_M += $(AUTOF_DRV_OBJ_M)
-
+MODULE_SOURCES := \
+  afangles.c \
+  afblue.c \
+  afcjk.c \
+  afdummy.c \
+  afglobal.c \
+  afhints.c \
+  afindic.c \
+  aflatin.c \
+  afloader.c \
+  afmodule.c \
+  afranges.c \
+  afshaper.c \
+  afwarp.c \
+
+MODULE_HEADERS := \
+  $(MODULE_SOURCES:%.c=%.h) \
+  afcover.h \
+  aferrors.h \
+  afscript.h \
+  afstyles.h \
+  aftypes.h \
+  afwrtsys.h \
+
+MODULE_WRAPPER := autofit.c
 
 # EOF
diff --git a/src/base/rules.mk b/src/base/rules.mk
index 411c4c821..fd779654f 100644
--- a/src/base/rules.mk
+++ b/src/base/rules.mk
@@ -12,97 +12,41 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# It sets the following variables which are used by the master Makefile
-# after the call:
-#
-#   BASE_OBJ_S:   The single-object base layer.
-#   BASE_OBJ_M:   A list of all objects for a multiple-objects build.
-#   BASE_EXT_OBJ: A list of base layer extensions, i.e., components found
-#                 in `src/base' which are not compiled within the base
-#                 layer proper.
-
-
-BASE_COMPILE := $(CC) $(ANSIFLAGS)                             \
-                      $I$(subst /,$(COMPILER_SEP),$(BASE_DIR)) \
-                      $(INCLUDE_FLAGS)                         \
-                      $(FT_CFLAGS)
-
-
 # Base layer sources
 #
 #   ftsystem, ftinit, and ftdebug are handled by freetype.mk
 #
-# All files listed here should be included in `ftbase.c' (for a `single'
-# build).
-#
-BASE_SRC := $(BASE_DIR)/ftadvanc.c \
-            $(BASE_DIR)/ftcalc.c   \
-            $(BASE_DIR)/ftcolor.c  \
-            $(BASE_DIR)/ftdbgmem.c \
-            $(BASE_DIR)/fterrors.c \
-            $(BASE_DIR)/ftfntfmt.c \
-            $(BASE_DIR)/ftgloadr.c \
-            $(BASE_DIR)/fthash.c   \
-            $(BASE_DIR)/ftlcdfil.c \
-            $(BASE_DIR)/ftobjs.c   \
-            $(BASE_DIR)/ftoutln.c  \
-            $(BASE_DIR)/ftpsprop.c \
-            $(BASE_DIR)/ftrfork.c  \
-            $(BASE_DIR)/ftsnames.c \
-            $(BASE_DIR)/ftstream.c \
-            $(BASE_DIR)/fttrigon.c \
-            $(BASE_DIR)/ftutil.c
 
+MODULE_SOURCES := \
+  ftadvanc.c \
+  ftcalc.c   \
+  ftcolor.c  \
+  ftdbgmem.c \
+  fterrors.c \
+  ftfntfmt.c \
+  ftgloadr.c \
+  fthash.c   \
+  ftlcdfil.c \
+  ftobjs.c   \
+  ftoutln.c  \
+  ftpsprop.c \
+  ftrfork.c  \
+  ftsnames.c \
+  ftstream.c \
+  fttrigon.c \
+  ftutil.c   \
+  $(ftmac_c) \
 
-ifneq ($(ftmac_c),)
-  BASE_SRC += $(BASE_DIR)/$(ftmac_c)
-endif
 
 # for simplicity, we also handle `md5.c' (which gets included by `ftobjs.h')
-BASE_H := $(BASE_DIR)/ftbase.h  \
-          $(BASE_DIR)/md5.c     \
-          $(BASE_DIR)/md5.h
-
-# Base layer `extensions' sources
-#
-# An extension is added to the library file as a separate object.  It is
-# then linked to the final executable only if one of its symbols is used by
-# the application.
-#
-BASE_EXT_SRC := $(patsubst %,$(BASE_DIR)/%,$(BASE_EXTENSIONS))
-
-# Default extensions objects
-#
-BASE_EXT_OBJ := $(BASE_EXT_SRC:$(BASE_DIR)/%.c=$(OBJ_DIR)/%.$O)
+MODULE_HEADERS := \
+  ftbase.h \
+  md5.c \
+  md5.h \
 
+MODULE_WRAPPER := ftbase.c
 
-# Base layer object(s)
-#
-#   BASE_OBJ_M is used during `multi' builds (each base source file compiles
-#   to a single object file).
-#
-#   BASE_OBJ_S is used during `single' builds (the whole base layer is
-#   compiled as a single object file using ftbase.c).
-#
-BASE_OBJ_M := $(BASE_SRC:$(BASE_DIR)/%.c=$(OBJ_DIR)/%.$O)
-BASE_OBJ_S := $(OBJ_DIR)/ftbase.$O
-
-# Base layer root source file for single build
-#
-BASE_SRC_S := $(BASE_DIR)/ftbase.c
-
-
-# Base layer - single object build
-#
-$(BASE_OBJ_S): $(BASE_SRC_S) $(BASE_SRC) $(FREETYPE_H) $(BASE_H)
-	$(BASE_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(BASE_SRC_S))
-
-
-# Multiple objects build + extensions
-#
-$(OBJ_DIR)/%.$O: $(BASE_DIR)/%.c $(FREETYPE_H) $(BASE_H)
-	$(BASE_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
+# Used by the caller  to list the headers that all base extensions will depend on.
+BASE_HEADERS := $(MODULE_HEADERS)
 
 # EOF
diff --git a/src/bdf/rules.mk b/src/bdf/rules.mk
index d1dd76b1c..25059924f 100644
--- a/src/bdf/rules.mk
+++ b/src/bdf/rules.mk
@@ -24,61 +24,15 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 # THE SOFTWARE.
 
+MODULE_HEADERS := \
+  bdf.h \
+  bdfdrivr.h \
+  bdferror.h \
 
+MODULE_SOURCES := \
+  bdflib.c \
+  bdfdrivr.c \
 
-
-# bdf driver directory
-#
-BDF_DIR := $(SRC_DIR)/bdf
-
-
-BDF_COMPILE := $(CC) $(ANSIFLAGS)                            \
-                     $I$(subst /,$(COMPILER_SEP),$(BDF_DIR)) \
-                     $(INCLUDE_FLAGS)                        \
-                     $(FT_CFLAGS)
-
-
-# bdf driver sources (i.e., C files)
-#
-BDF_DRV_SRC := $(BDF_DIR)/bdflib.c \
-               $(BDF_DIR)/bdfdrivr.c
-
-
-# bdf driver headers
-#
-BDF_DRV_H := $(BDF_DIR)/bdf.h \
-             $(BDF_DIR)/bdfdrivr.h \
-             $(BDF_DIR)/bdferror.h
-
-# bdf driver object(s)
-#
-#   BDF_DRV_OBJ_M is used during `multi' builds
-#   BDF_DRV_OBJ_S is used during `single' builds
-#
-BDF_DRV_OBJ_M := $(BDF_DRV_SRC:$(BDF_DIR)/%.c=$(OBJ_DIR)/%.$O)
-BDF_DRV_OBJ_S := $(OBJ_DIR)/bdf.$O
-
-# bdf driver source file for single build
-#
-BDF_DRV_SRC_S := $(BDF_DIR)/bdf.c
-
-
-# bdf driver - single object
-#
-$(BDF_DRV_OBJ_S): $(BDF_DRV_SRC_S) $(BDF_DRV_SRC) $(FREETYPE_H) $(BDF_DRV_H)
-	$(BDF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(BDF_DRV_SRC_S))
-
-
-# bdf driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(BDF_DIR)/%.c $(FREETYPE_H) $(BDF_DRV_H)
-	$(BDF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(BDF_DRV_OBJ_S)
-DRV_OBJS_M += $(BDF_DRV_OBJ_M)
-
+MODULE_WRAPPER := bdf.c
 
 # EOF
diff --git a/src/bzip2/rules.mk b/src/bzip2/rules.mk
index eed0f4baa..b065c7371 100644
--- a/src/bzip2/rules.mk
+++ b/src/bzip2/rules.mk
@@ -13,52 +13,7 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# BZIP2 driver directory
-#
-BZIP2_DIR := $(SRC_DIR)/bzip2
-
-
-# compilation flags for the driver
-#
-BZIP2_COMPILE := $(CC) $(ANSIFLAGS)     \
-                       $(INCLUDE_FLAGS) \
-                       $(FT_CFLAGS)
-
-
-# BZIP2 support sources (i.e., C files)
-#
-BZIP2_DRV_SRC := $(BZIP2_DIR)/ftbzip2.c
-
-# BZIP2 driver object(s)
-#
-#   BZIP2_DRV_OBJ_M is used during `multi' builds
-#   BZIP2_DRV_OBJ_S is used during `single' builds
-#
-BZIP2_DRV_OBJ_M := $(OBJ_DIR)/ftbzip2.$O
-BZIP2_DRV_OBJ_S := $(OBJ_DIR)/ftbzip2.$O
-
-# BZIP2 support source file for single build
-#
-BZIP2_DRV_SRC_S := $(BZIP2_DIR)/ftbzip2.c
-
-
-# BZIP2 support - single object
-#
-$(BZIP2_DRV_OBJ_S): $(BZIP2_DRV_SRC_S) $(BZIP2_DRV_SRC) $(FREETYPE_H) $(BZIP2_DRV_H)
-	$(BZIP2_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(BZIP2_DRV_SRC_S))
-
-
-# BZIP2 support - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(BZIP2_DIR)/%.c $(FREETYPE_H) $(BZIP2_DRV_H)
-	$(BZIP2_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(BZIP2_DRV_OBJ_S)
-DRV_OBJS_M += $(BZIP2_DRV_OBJ_M)
-
+MODULE_SOURCES := ftbzip2.c
+MODULE_WRAPPER := ftbzip2.c
 
 # EOF
diff --git a/src/cache/rules.mk b/src/cache/rules.mk
index 4738b5153..ab0d7b54c 100644
--- a/src/cache/rules.mk
+++ b/src/cache/rules.mk
@@ -13,73 +13,25 @@
 # fully.
 
 
-# Cache driver directory
-#
-CACHE_DIR := $(SRC_DIR)/cache
-
-
-# compilation flags for the driver
-#
-CACHE_COMPILE := $(CC) $(ANSIFLAGS)                              \
-                       $I$(subst /,$(COMPILER_SEP),$(CACHE_DIR)) \
-                       $(INCLUDE_FLAGS)                          \
-                       $(FT_CFLAGS)
-
-
-# Cache driver sources (i.e., C files)
-#
-CACHE_DRV_SRC := $(CACHE_DIR)/ftcbasic.c \
-                 $(CACHE_DIR)/ftccache.c \
-                 $(CACHE_DIR)/ftccmap.c  \
-                 $(CACHE_DIR)/ftcglyph.c \
-                 $(CACHE_DIR)/ftcimage.c \
-                 $(CACHE_DIR)/ftcmanag.c \
-                 $(CACHE_DIR)/ftcmru.c   \
-                 $(CACHE_DIR)/ftcsbits.c
-
-
-# Cache driver headers
-#
-CACHE_DRV_H := $(CACHE_DIR)/ftccache.h \
-               $(CACHE_DIR)/ftccback.h \
-               $(CACHE_DIR)/ftcerror.h \
-               $(CACHE_DIR)/ftcglyph.h \
-               $(CACHE_DIR)/ftcimage.h \
-               $(CACHE_DIR)/ftcmanag.h \
-               $(CACHE_DIR)/ftcmru.h   \
-               $(CACHE_DIR)/ftcsbits.h
-
-
-# Cache driver object(s)
-#
-#   CACHE_DRV_OBJ_M is used during `multi' builds.
-#   CACHE_DRV_OBJ_S is used during `single' builds.
-#
-CACHE_DRV_OBJ_M := $(CACHE_DRV_SRC:$(CACHE_DIR)/%.c=$(OBJ_DIR)/%.$O)
-CACHE_DRV_OBJ_S := $(OBJ_DIR)/ftcache.$O
-
-# Cache driver source file for single build
-#
-CACHE_DRV_SRC_S := $(CACHE_DIR)/ftcache.c
-
-
-# Cache driver - single object
-#
-$(CACHE_DRV_OBJ_S): $(CACHE_DRV_SRC_S) $(CACHE_DRV_SRC) \
-                   $(FREETYPE_H) $(CACHE_DRV_H)
-	$(CACHE_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(CACHE_DRV_SRC_S))
-
-
-# Cache driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(CACHE_DIR)/%.c $(FREETYPE_H) $(CACHE_DRV_H)
-	$(CACHE_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(CACHE_DRV_OBJ_S)
-DRV_OBJS_M += $(CACHE_DRV_OBJ_M)
-
+MODULE_HEADERS := \
+  ftccache.h \
+  ftccback.h \
+  ftcerror.h \
+  ftcimage.h \
+  ftcmanag.h \
+  ftcmru.h   \
+  ftcsbits.h \
+
+MODULE_SOURCES := \
+  ftcbasic.c \
+  ftccache.c \
+  ftccmap.c  \
+  ftcglyph.c \
+  ftcimage.c \
+  ftcmru.c   \
+  ftcsbits.c \
+
+MODULE_WRAPPER := \
+  ftcache.c
 
 # EOF
diff --git a/src/cff/rules.mk b/src/cff/rules.mk
index 70bb92d50..4ecfebf1c 100644
--- a/src/cff/rules.mk
+++ b/src/cff/rules.mk
@@ -12,64 +12,19 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# OpenType driver directory
-#
-CFF_DIR := $(SRC_DIR)/cff
-
-
-CFF_COMPILE := $(CC) $(ANSIFLAGS)                            \
-                     $I$(subst /,$(COMPILER_SEP),$(CFF_DIR)) \
-                     $(INCLUDE_FLAGS)                        \
-                     $(FT_CFLAGS)
-
-
-# CFF driver sources (i.e., C files)
-#
-CFF_DRV_SRC := $(CFF_DIR)/cffcmap.c  \
-               $(CFF_DIR)/cffdrivr.c \
-               $(CFF_DIR)/cffgload.c \
-               $(CFF_DIR)/cffload.c  \
-               $(CFF_DIR)/cffobjs.c  \
-               $(CFF_DIR)/cffparse.c
-
-
-# CFF driver headers
-#
-CFF_DRV_H := $(CFF_DRV_SRC:%.c=%.h) \
-             $(CFF_DIR)/cfferrs.h   \
-             $(CFF_DIR)/cfftoken.h
-
-
-# CFF driver object(s)
-#
-#   CFF_DRV_OBJ_M is used during `multi' builds
-#   CFF_DRV_OBJ_S is used during `single' builds
-#
-CFF_DRV_OBJ_M := $(CFF_DRV_SRC:$(CFF_DIR)/%.c=$(OBJ_DIR)/%.$O)
-CFF_DRV_OBJ_S := $(OBJ_DIR)/cff.$O
-
-# CFF driver source file for single build
-#
-CFF_DRV_SRC_S := $(CFF_DIR)/cff.c
-
-
-# CFF driver - single object
-#
-$(CFF_DRV_OBJ_S): $(CFF_DRV_SRC_S) $(CFF_DRV_SRC) $(FREETYPE_H) $(CFF_DRV_H)
-	$(CFF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(CFF_DRV_SRC_S))
-
-
-# CFF driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(CFF_DIR)/%.c $(FREETYPE_H) $(CFF_DRV_H)
-	$(CFF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(CFF_DRV_OBJ_S)
-DRV_OBJS_M += $(CFF_DRV_OBJ_M)
-
+MODULE_SOURCES := \
+  cffcmap.c \
+  cffdrivr.c \
+  cffgload.c \
+  cffload.c \
+  cffobjs.c \
+  cffparse.c \
+
+MODULE_HEADERS := \
+  $(MODULE_SOURCES:%.c=%.h) \
+  cfferrs.h \
+  cfftoken.h \
+
+MODULE_WRAPPER := cff.c
 
 # EOF
diff --git a/src/cid/rules.mk b/src/cid/rules.mk
index 94f663c80..003e24b48 100644
--- a/src/cid/rules.mk
+++ b/src/cid/rules.mk
@@ -12,62 +12,18 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# CID driver directory
-#
-CID_DIR := $(SRC_DIR)/cid
-
-
-CID_COMPILE := $(CC) $(ANSIFLAGS)                            \
-                     $I$(subst /,$(COMPILER_SEP),$(CID_DIR)) \
-                     $(INCLUDE_FLAGS)                        \
-                     $(FT_CFLAGS)
-
-
-# CID driver sources (i.e., C files)
-#
-CID_DRV_SRC := $(CID_DIR)/cidparse.c \
-               $(CID_DIR)/cidload.c  \
-               $(CID_DIR)/cidriver.c \
-               $(CID_DIR)/cidgload.c \
-               $(CID_DIR)/cidobjs.c
-
-# CID driver headers
-#
-CID_DRV_H := $(CID_DRV_SRC:%.c=%.h) \
-             $(CID_DIR)/cidtoken.h  \
-             $(CID_DIR)/ciderrs.h
-
-
-# CID driver object(s)
-#
-#   CID_DRV_OBJ_M is used during `multi' builds
-#   CID_DRV_OBJ_S is used during `single' builds
-#
-CID_DRV_OBJ_M := $(CID_DRV_SRC:$(CID_DIR)/%.c=$(OBJ_DIR)/%.$O)
-CID_DRV_OBJ_S := $(OBJ_DIR)/type1cid.$O
-
-# CID driver source file for single build
-#
-CID_DRV_SRC_S := $(CID_DIR)/type1cid.c
-
-
-# CID driver - single object
-#
-$(CID_DRV_OBJ_S): $(CID_DRV_SRC_S) $(CID_DRV_SRC) $(FREETYPE_H) $(CID_DRV_H)
-	$(CID_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(CID_DRV_SRC_S))
-
-
-# CID driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(CID_DIR)/%.c $(FREETYPE_H) $(CID_DRV_H)
-	$(CID_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(CID_DRV_OBJ_S)
-DRV_OBJS_M += $(CID_DRV_OBJ_M)
-
+MODULE_SOURCES := \
+  cidparse.c \
+  cidload.c \
+  cidriver.c \
+  cidgload.c \
+  cidobjs.c \
+
+MODULE_HEADERS := \
+  $(MODULE_SOURCE:%.c=%.h) \
+  cidtoken.h \
+  ciderrs.h \
+
+MODULE_WRAPPER := type1cid.c
 
 # EOF
diff --git a/src/gxvalid/rules.mk b/src/gxvalid/rules.mk
index d55a4935e..35fc6bdcb 100644
--- a/src/gxvalid/rules.mk
+++ b/src/gxvalid/rules.mk
@@ -13,86 +13,39 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# GXV driver directory
-#
-GXV_DIR := $(SRC_DIR)/gxvalid
-
-
-# compilation flags for the driver
-#
-GXV_COMPILE := $(CC) $(ANSIFLAGS)                            \
-                     $I$(subst /,$(COMPILER_SEP),$(GXV_DIR)) \
-                     $(INCLUDE_FLAGS)                        \
-                     $(FT_CFLAGS)
-
-
-# GXV driver sources (i.e., C files)
-#
-GXV_DRV_SRC := $(GXV_DIR)/gxvcommn.c \
-               $(GXV_DIR)/gxvfeat.c  \
-               $(GXV_DIR)/gxvbsln.c  \
-               $(GXV_DIR)/gxvtrak.c  \
-               $(GXV_DIR)/gxvopbd.c  \
-               $(GXV_DIR)/gxvprop.c  \
-               $(GXV_DIR)/gxvjust.c  \
-               $(GXV_DIR)/gxvmort.c  \
-               $(GXV_DIR)/gxvmort0.c \
-               $(GXV_DIR)/gxvmort1.c \
-               $(GXV_DIR)/gxvmort2.c \
-               $(GXV_DIR)/gxvmort4.c \
-               $(GXV_DIR)/gxvmort5.c \
-               $(GXV_DIR)/gxvmorx.c  \
-               $(GXV_DIR)/gxvmorx0.c \
-               $(GXV_DIR)/gxvmorx1.c \
-               $(GXV_DIR)/gxvmorx2.c \
-               $(GXV_DIR)/gxvmorx4.c \
-               $(GXV_DIR)/gxvmorx5.c \
-               $(GXV_DIR)/gxvlcar.c  \
-               $(GXV_DIR)/gxvkern.c  \
-               $(GXV_DIR)/gxvmod.c
-
-# GXV driver headers
-#
-GXV_DRV_H := $(GXV_DIR)/gxvalid.h  \
-             $(GXV_DIR)/gxverror.h \
-             $(GXV_DIR)/gxvcommn.h \
-             $(GXV_DIR)/gxvfeat.h  \
-             $(GXV_DIR)/gxvmod.h   \
-             $(GXV_DIR)/gxvmort.h  \
-             $(GXV_DIR)/gxvmorx.h
-
-
-# GXV driver object(s)
-#
-#   GXV_DRV_OBJ_M is used during `multi' builds.
-#   GXV_DRV_OBJ_S is used during `single' builds.
-#
-GXV_DRV_OBJ_M := $(GXV_DRV_SRC:$(GXV_DIR)/%.c=$(OBJ_DIR)/%.$O)
-GXV_DRV_OBJ_S := $(OBJ_DIR)/gxvalid.$O
-
-# GXV driver source file for single build
-#
-GXV_DRV_SRC_S := $(GXV_DIR)/gxvalid.c
-
-
-# GXV driver - single object
-#
-$(GXV_DRV_OBJ_S): $(GXV_DRV_SRC_S) $(GXV_DRV_SRC) \
-                   $(FREETYPE_H) $(GXV_DRV_H)
-	$(GXV_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(GXV_DRV_SRC_S))
-
-
-# GXV driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(GXV_DIR)/%.c $(FREETYPE_H) $(GXV_DRV_H)
-	$(GXV_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(GXV_DRV_OBJ_S)
-DRV_OBJS_M += $(GXV_DRV_OBJ_M)
-
+MODULE_HEADERS := \
+  gxvalid.h \
+  gxverror.h \
+  gxvcommn.h \
+  gxvfeat.h \
+  gxvmod.h \
+  gxvmort.h \
+  gxvmorx.h \
+
+MODULE_SOURCES := \
+  gxvcommn.c \
+  gxvfeat.c  \
+  gxvbsln.c  \
+  gxvtrak.c  \
+  gxvopbd.c  \
+  gxvprop.c  \
+  gxvjust.c  \
+  gxvmort.c  \
+  gxvmort0.c \
+  gxvmort1.c \
+  gxvmort2.c \
+  gxvmort4.c \
+  gxvmort5.c \
+  gxvmorx.c  \
+  gxvmorx0.c \
+  gxvmorx1.c \
+  gxvmorx2.c \
+  gxvmorx4.c \
+  gxvmorx5.c \
+  gxvlcar.c  \
+  gxvkern.c  \
+  gxvmod.c   \
+
+MODULE_WRAPPER := gxvalid.c
 
 # EOF
diff --git a/src/gzip/rules.mk b/src/gzip/rules.mk
index 4ea823f8d..f1e375397 100644
--- a/src/gzip/rules.mk
+++ b/src/gzip/rules.mk
@@ -13,71 +13,41 @@
 # fully.
 
 
-# gzip driver directory
-#
-GZIP_DIR := $(SRC_DIR)/gzip
-
-
-# compilation flags for the driver
-#
 ifeq ($(SYSTEM_ZLIB),)
-  GZIP_COMPILE := $(CC) $(ANSIFLAGS)                             \
-                        $I$(subst /,$(COMPILER_SEP),$(GZIP_DIR)) \
-                        $(INCLUDE_FLAGS)                         \
-                        $(FT_CFLAGS)
-else
-  GZIP_COMPILE := $(CC) $(ANSIFLAGS)     \
-                        $(INCLUDE_FLAGS) \
-                        $(FT_CFLAGS)
-endif
-
-
-# gzip support sources
-#
-# All source and header files get loaded by `ftgzip.c' only if SYSTEM_ZLIB
-# is not defined (regardless whether we have a `single' or a `multi' build).
-# However, it doesn't harm if we add everything as a dependency
-# unconditionally.
-#
-GZIP_DRV_SRCS := $(GZIP_DIR)/adler32.c  \
-                 $(GZIP_DIR)/ftzconf.h  \
-                 $(GZIP_DIR)/infblock.c \
-                 $(GZIP_DIR)/infblock.h \
-                 $(GZIP_DIR)/infcodes.c \
-                 $(GZIP_DIR)/infcodes.h \
-                 $(GZIP_DIR)/inffixed.h \
-                 $(GZIP_DIR)/inflate.c  \
-                 $(GZIP_DIR)/inftrees.c \
-                 $(GZIP_DIR)/inftrees.h \
-                 $(GZIP_DIR)/infutil.c  \
-                 $(GZIP_DIR)/infutil.h  \
-                 $(GZIP_DIR)/zlib.h     \
-                 $(GZIP_DIR)/zutil.c    \
-                 $(GZIP_DIR)/zutil.h
 
+# When not using the system zlib, only compile ftgzip.c, even in multi-build
+# mode. It includes all headers and sources listed below:
 
-# gzip driver object(s)
-#
-#   GZIP_DRV_OBJ is used during both `single' and `multi' builds
-#
-GZIP_DRV_OBJ := $(OBJ_DIR)/ftgzip.$O
+# All headers and sources are included directly by ftgzip.c
+MODULE_HEADERS := \
+  adler32.c  \
+  ftzconf.h  \
+  infblock.c \
+  infblock.h \
+  infcodes.c \
+  infcodes.h \
+  inffixed.h \
+  inflate.c  \
+  inftrees.c \
+  inftrees.h \
+  infutil.c  \
+  infutil.h  \
+  zlib.h     \
+  zutil.c    \
+  zutil.h
 
+MODULE_SOURCES := ftgzip.c
 
-# gzip main source file
-#
-GZIP_DRV_SRC := $(GZIP_DIR)/ftgzip.c
+else  # SYSTEM_ZLIB
 
-
-# gzip support - object
+# When using the system zlib, clear MODULE_DIR to ensure that it will not be
+# part of the include path when compiling ftgzip.c.
 #
-$(GZIP_DRV_OBJ): $(GZIP_DRV_SRC) $(GZIP_DRV_SRCS) $(FREETYPE_H)
-	$(GZIP_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(GZIP_DRV_SRC))
-
+MODULE_DIR :=
+MODULE_SOURCES := $(SRC_DIR)/gzip/ftgzip.c
 
-# update main driver object lists
-#
-DRV_OBJS_S += $(GZIP_DRV_OBJ)
-DRV_OBJS_M += $(GZIP_DRV_OBJ)
+endif  # SYSTEM_ZLIB
 
+MODULE_WRAPPER := $(MODULE_SOURCES)
 
 # EOF
diff --git a/src/lzw/rules.mk b/src/lzw/rules.mk
index 3468ee024..1e86254fb 100644
--- a/src/lzw/rules.mk
+++ b/src/lzw/rules.mk
@@ -14,59 +14,8 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# LZW driver directory
-#
-LZW_DIR := $(SRC_DIR)/lzw
-
-
-# compilation flags for the driver
-#
-LZW_COMPILE := $(CC) $(ANSIFLAGS)                            \
-                     $I$(subst /,$(COMPILER_SEP),$(LZW_DIR)) \
-                     $(INCLUDE_FLAGS)                        \
-                     $(FT_CFLAGS)
-
-
-# LZW support sources (i.e., C files)
-#
-LZW_DRV_SRC := $(LZW_DIR)/ftlzw.c
-
-# LZW support headers
-#
-LZW_DRV_H := $(LZW_DIR)/ftzopen.h \
-             $(LZW_DIR)/ftzopen.c
-
-
-# LZW driver object(s)
-#
-#   LZW_DRV_OBJ_M is used during `multi' builds
-#   LZW_DRV_OBJ_S is used during `single' builds
-#
-LZW_DRV_OBJ_M := $(OBJ_DIR)/ftlzw.$O
-LZW_DRV_OBJ_S := $(OBJ_DIR)/ftlzw.$O
-
-# LZW support source file for single build
-#
-LZW_DRV_SRC_S := $(LZW_DIR)/ftlzw.c
-
-
-# LZW support - single object
-#
-$(LZW_DRV_OBJ_S): $(LZW_DRV_SRC_S) $(LZW_DRV_SRC) $(FREETYPE_H) $(LZW_DRV_H)
-	$(LZW_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(LZW_DRV_SRC_S))
-
-
-# LZW support - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(LZW_DIR)/%.c $(FREETYPE_H) $(LZW_DRV_H)
-	$(LZW_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(LZW_DRV_OBJ_S)
-DRV_OBJS_M += $(LZW_DRV_OBJ_M)
-
+MODULE_HEADERS := ftzopen.h ftzopen.c
+MODULE_SOURCES := ftlzw.c
+MODULE_WRAPPER := ftlzw.c
 
 # EOF
diff --git a/src/otvalid/rules.mk b/src/otvalid/rules.mk
index 7f0169fd8..22d6142cf 100644
--- a/src/otvalid/rules.mk
+++ b/src/otvalid/rules.mk
@@ -12,70 +12,23 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# OTV driver directory
-#
-OTV_DIR := $(SRC_DIR)/otvalid
-
-
-# compilation flags for the driver
-#
-OTV_COMPILE := $(CC) $(ANSIFLAGS)                            \
-                     $I$(subst /,$(COMPILER_SEP),$(OTV_DIR)) \
-                     $(INCLUDE_FLAGS)                        \
-                     $(FT_CFLAGS)
-
-
-# OTV driver sources (i.e., C files)
-#
-OTV_DRV_SRC := $(OTV_DIR)/otvbase.c  \
-               $(OTV_DIR)/otvcommn.c \
-               $(OTV_DIR)/otvgdef.c  \
-               $(OTV_DIR)/otvgpos.c  \
-               $(OTV_DIR)/otvgsub.c  \
-               $(OTV_DIR)/otvjstf.c  \
-               $(OTV_DIR)/otvmath.c  \
-               $(OTV_DIR)/otvmod.c
-
-# OTV driver headers
-#
-OTV_DRV_H := $(OTV_DIR)/otvalid.h  \
-             $(OTV_DIR)/otvcommn.h \
-             $(OTV_DIR)/otverror.h \
-             $(OTV_DIR)/otvgpos.h  \
-             $(OTV_DIR)/otvmod.h
-
-
-# OTV driver object(s)
-#
-#   OTV_DRV_OBJ_M is used during `multi' builds.
-#   OTV_DRV_OBJ_S is used during `single' builds.
-#
-OTV_DRV_OBJ_M := $(OTV_DRV_SRC:$(OTV_DIR)/%.c=$(OBJ_DIR)/%.$O)
-OTV_DRV_OBJ_S := $(OBJ_DIR)/otvalid.$O
-
-# OTV driver source file for single build
-#
-OTV_DRV_SRC_S := $(OTV_DIR)/otvalid.c
-
-
-# OTV driver - single object
-#
-$(OTV_DRV_OBJ_S): $(OTV_DRV_SRC_S) $(OTV_DRV_SRC) \
-                   $(FREETYPE_H) $(OTV_DRV_H)
-	$(OTV_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(OTV_DRV_SRC_S))
-
-
-# OTV driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(OTV_DIR)/%.c $(FREETYPE_H) $(OTV_DRV_H)
-	$(OTV_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(OTV_DRV_OBJ_S)
-DRV_OBJS_M += $(OTV_DRV_OBJ_M)
-
+MODULE_HEADERS := \
+  otvalid.h \
+  otvcommn.h \
+  otverror.h \
+  otvgpos.h \
+  otvmod.h \
+
+MODULE_SOURCES := \
+  otvbase.c \
+  otvcommn.c \
+  otvgdef.c \
+  otvgpos.c \
+  otvgsub.c \
+  otvjstf.c \
+  otvmath.c \
+  otvmod.c \
+
+MODULE_WRAPPER := otvalid.c
 
 # EOF
diff --git a/src/pcf/rules.mk b/src/pcf/rules.mk
index 1b55daf4f..a1a82da23 100644
--- a/src/pcf/rules.mk
+++ b/src/pcf/rules.mk
@@ -25,58 +25,16 @@
 # THE SOFTWARE.
 
 
-# pcf driver directory
-#
-PCF_DIR := $(SRC_DIR)/pcf
-
-
-PCF_COMPILE := $(CC) $(ANSIFLAGS)                            \
-                     $I$(subst /,$(COMPILER_SEP),$(PCF_DIR)) \
-                     $(INCLUDE_FLAGS)                        \
-                     $(FT_CFLAGS)
-
-
-# pcf driver sources (i.e., C files)
-#
-PCF_DRV_SRC := $(PCF_DIR)/pcfdrivr.c \
-               $(PCF_DIR)/pcfread.c  \
-               $(PCF_DIR)/pcfutil.c
-
-# pcf driver headers
-#
-PCF_DRV_H := $(PCF_DRV_SRC:%.c=%.h) \
-             $(PCF_DIR)/pcf.h       \
-             $(PCF_DIR)/pcferror.h
-
-# pcf driver object(s)
-#
-#   PCF_DRV_OBJ_M is used during `multi' builds
-#   PCF_DRV_OBJ_S is used during `single' builds
-#
-PCF_DRV_OBJ_M := $(PCF_DRV_SRC:$(PCF_DIR)/%.c=$(OBJ_DIR)/%.$O)
-PCF_DRV_OBJ_S := $(OBJ_DIR)/pcf.$O
+MODULE_SOURCES := \
+  pcfdrivr.c \
+  pcfread.c \
+  pcfutil.c \
 
-# pcf driver source file for single build
-#
-PCF_DRV_SRC_S := $(PCF_DIR)/pcf.c
-
-
-# pcf driver - single object
-#
-$(PCF_DRV_OBJ_S): $(PCF_DRV_SRC_S) $(PCF_DRV_SRC) $(FREETYPE_H) $(PCF_DRV_H)
-	$(PCF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PCF_DRV_SRC_S))
-
-
-# pcf driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(PCF_DIR)/%.c $(FREETYPE_H) $(PCF_DRV_H)
-	$(PCF_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(PCF_DRV_OBJ_S)
-DRV_OBJS_M += $(PCF_DRV_OBJ_M)
+MODULE_HEADERS := \
+  $(MODULE_SOURCES:%.c=%.h) \
+  pcf.h \
+  pcferror.h \
 
+MODULE_WRAPPER := pcf.c
 
 # EOF
diff --git a/src/pfr/rules.mk b/src/pfr/rules.mk
index a1fe82baf..086a611c2 100644
--- a/src/pfr/rules.mk
+++ b/src/pfr/rules.mk
@@ -12,65 +12,19 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# pfr driver directory
-#
-PFR_DIR := $(SRC_DIR)/pfr
-
-
-# compilation flags for the driver
-#
-PFR_COMPILE := $(CC) $(ANSIFLAGS)                            \
-                     $I$(subst /,$(COMPILER_SEP),$(PFR_DIR)) \
-                     $(INCLUDE_FLAGS)                        \
-                     $(FT_CFLAGS)
-
-
-# pfr driver sources (i.e., C files)
-#
-PFR_DRV_SRC := $(PFR_DIR)/pfrload.c  \
-               $(PFR_DIR)/pfrgload.c \
-               $(PFR_DIR)/pfrcmap.c  \
-               $(PFR_DIR)/pfrdrivr.c \
-               $(PFR_DIR)/pfrsbit.c  \
-               $(PFR_DIR)/pfrobjs.c
-
-# pfr driver headers
-#
-PFR_DRV_H := $(PFR_DRV_SRC:%.c=%.h) \
-             $(PFR_DIR)/pfrerror.h  \
-             $(PFR_DIR)/pfrtypes.h
-
-
-# Pfr driver object(s)
-#
-#   PFR_DRV_OBJ_M is used during `multi' builds
-#   PFR_DRV_OBJ_S is used during `single' builds
-#
-PFR_DRV_OBJ_M := $(PFR_DRV_SRC:$(PFR_DIR)/%.c=$(OBJ_DIR)/%.$O)
-PFR_DRV_OBJ_S := $(OBJ_DIR)/pfr.$O
-
-# pfr driver source file for single build
-#
-PFR_DRV_SRC_S := $(PFR_DIR)/pfr.c
-
-
-# pfr driver - single object
-#
-$(PFR_DRV_OBJ_S): $(PFR_DRV_SRC_S) $(PFR_DRV_SRC) $(FREETYPE_H) $(PFR_DRV_H)
-	$(PFR_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PFR_DRV_SRC_S))
-
-
-# pfr driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(PFR_DIR)/%.c $(FREETYPE_H) $(PFR_DRV_H)
-	$(PFR_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(PFR_DRV_OBJ_S)
-DRV_OBJS_M += $(PFR_DRV_OBJ_M)
-
+MODULE_SOURCES := \
+  pfrload.c \
+  pfrgload.c \
+  pfrcmap.c \
+  pfrdrivr.c \
+  pfrsbit.c \
+  pfrobjs.c \
+
+MODULE_HEADERS := \
+  $(MODULE_SOURCES:%.c=%.h) \
+  pfrerror.h \
+  pfrtypes.h \
+
+MODULE_WRAPPER := pfr.c
 
 # EOF
diff --git a/src/psaux/rules.mk b/src/psaux/rules.mk
index f49aecbc7..1053bd636 100644
--- a/src/psaux/rules.mk
+++ b/src/psaux/rules.mk
@@ -12,78 +12,31 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# PSAUX driver directory
-#
-PSAUX_DIR := $(SRC_DIR)/psaux
-
-
-# compilation flags for the driver
-#
-PSAUX_COMPILE := $(CC) $(ANSIFLAGS)                              \
-                       $I$(subst /,$(COMPILER_SEP),$(PSAUX_DIR)) \
-                       $(INCLUDE_FLAGS)                          \
-                       $(FT_CFLAGS)
-
-
-# PSAUX driver sources (i.e., C files)
-#
-PSAUX_DRV_SRC := $(PSAUX_DIR)/psobjs.c   \
-                 $(PSAUX_DIR)/t1decode.c \
-                 $(PSAUX_DIR)/t1cmap.c   \
-                 $(PSAUX_DIR)/afmparse.c \
-                 $(PSAUX_DIR)/psconv.c   \
-                 $(PSAUX_DIR)/psauxmod.c \
-                 $(PSAUX_DIR)/psarrst.c \
-                 $(PSAUX_DIR)/psblues.c \
-                 $(PSAUX_DIR)/pserror.c \
-                 $(PSAUX_DIR)/psfont.c  \
-                 $(PSAUX_DIR)/psft.c    \
-                 $(PSAUX_DIR)/pshints.c \
-                 $(PSAUX_DIR)/psintrp.c \
-                 $(PSAUX_DIR)/psread.c  \
-                 $(PSAUX_DIR)/psstack.c \
-                 $(PSAUX_DIR)/cffdecode.c
-
-# PSAUX driver headers
-#
-PSAUX_DRV_H := $(PSAUX_DRV_SRC:%c=%h)  \
-               $(PSAUX_DIR)/psauxerr.h \
-               $(PSAUX_DIR)/psfixed.h \
-               $(PSAUX_DIR)/psglue.h  \
-               $(PSAUX_DIR)/pstypes.h
-
-
-# PSAUX driver object(s)
-#
-#   PSAUX_DRV_OBJ_M is used during `multi' builds.
-#   PSAUX_DRV_OBJ_S is used during `single' builds.
-#
-PSAUX_DRV_OBJ_M := $(PSAUX_DRV_SRC:$(PSAUX_DIR)/%.c=$(OBJ_DIR)/%.$O)
-PSAUX_DRV_OBJ_S := $(OBJ_DIR)/psaux.$O
-
-# PSAUX driver source file for single build
-#
-PSAUX_DRV_SRC_S := $(PSAUX_DIR)/psaux.c
-
-
-# PSAUX driver - single object
-#
-$(PSAUX_DRV_OBJ_S): $(PSAUX_DRV_SRC_S) $(PSAUX_DRV_SRC) \
-                   $(FREETYPE_H) $(PSAUX_DRV_H)
-	$(PSAUX_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PSAUX_DRV_SRC_S))
-
-
-# PSAUX driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(PSAUX_DIR)/%.c $(FREETYPE_H) $(PSAUX_DRV_H)
-	$(PSAUX_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(PSAUX_DRV_OBJ_S)
-DRV_OBJS_M += $(PSAUX_DRV_OBJ_M)
-
+MODULE_SOURCES := \
+  psobjs.c   \
+  t1decode.c \
+  t1cmap.c   \
+  afmparse.c \
+  psconv.c   \
+  psauxmod.c \
+  psarrst.c \
+  psblues.c \
+  pserror.c \
+  psfont.c  \
+  psft.c    \
+  pshints.c \
+  psintrp.c \
+  psread.c  \
+  psstack.c \
+  cffdecode.c
+
+MODULE_HEADERS := \
+  $(MODULE_SOURCE:%.c=%.h) \
+  psauxerr.h \
+  psfixed.h \
+  psglue.h \
+  pstypes.h \
+
+MODULE_WRAPPER := psaux.c
 
 # EOF
diff --git a/src/pshinter/rules.mk b/src/pshinter/rules.mk
index c845c255c..c881dd9dc 100644
--- a/src/pshinter/rules.mk
+++ b/src/pshinter/rules.mk
@@ -12,64 +12,16 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
+MODULE_SOURCES := \
+  pshalgo.c \
+  pshglob.c \
+  pshmod.c \
+  pshrec.c \
 
-# PSHINTER driver directory
-#
-PSHINTER_DIR := $(SRC_DIR)/pshinter
-
-
-# compilation flags for the driver
-#
-PSHINTER_COMPILE := $(CC) $(ANSIFLAGS)                                 \
-                          $I$(subst /,$(COMPILER_SEP),$(PSHINTER_DIR)) \
-                          $(INCLUDE_FLAGS)                             \
-                          $(FT_CFLAGS)
-
-
-# PSHINTER driver sources (i.e., C files)
-#
-PSHINTER_DRV_SRC := $(PSHINTER_DIR)/pshalgo.c \
-                    $(PSHINTER_DIR)/pshglob.c \
-                    $(PSHINTER_DIR)/pshmod.c  \
-                    $(PSHINTER_DIR)/pshrec.c
-
-
-# PSHINTER driver headers
-#
-PSHINTER_DRV_H := $(PSHINTER_DRV_SRC:%c=%h) \
-                  $(PSHINTER_DIR)/pshnterr.h
-
-
-# PSHINTER driver object(s)
-#
-#   PSHINTER_DRV_OBJ_M is used during `multi' builds.
-#   PSHINTER_DRV_OBJ_S is used during `single' builds.
-#
-PSHINTER_DRV_OBJ_M := $(PSHINTER_DRV_SRC:$(PSHINTER_DIR)/%.c=$(OBJ_DIR)/%.$O)
-PSHINTER_DRV_OBJ_S := $(OBJ_DIR)/pshinter.$O
-
-# PSHINTER driver source file for single build
-#
-PSHINTER_DRV_SRC_S := $(PSHINTER_DIR)/pshinter.c
-
-
-# PSHINTER driver - single object
-#
-$(PSHINTER_DRV_OBJ_S): $(PSHINTER_DRV_SRC_S) $(PSHINTER_DRV_SRC) \
-                       $(FREETYPE_H) $(PSHINTER_DRV_H)
-	$(PSHINTER_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PSHINTER_DRV_SRC_S))
-
-
-# PSHINTER driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(PSHINTER_DIR)/%.c $(FREETYPE_H) $(PSHINTER_DRV_H)
-	$(PSHINTER_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(PSHINTER_DRV_OBJ_S)
-DRV_OBJS_M += $(PSHINTER_DRV_OBJ_M)
+MODULE_HEADERS := \
+  $(MODULE_SOURCES:%.c=%.h) \
+  pshnterr.h \
 
+MODULE_WRAPPER := pshinter.c
 
 # EOF
diff --git a/src/psnames/rules.mk b/src/psnames/rules.mk
index 14cdda3ad..8d6aa6121 100644
--- a/src/psnames/rules.mk
+++ b/src/psnames/rules.mk
@@ -12,62 +12,13 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
+MODULE_SOURCES := psmodule.c
 
-# psnames driver directory
-#
-PSNAMES_DIR := $(SRC_DIR)/psnames
-
-
-# compilation flags for the driver
-#
-PSNAMES_COMPILE := $(CC) $(ANSIFLAGS)                                \
-                         $I$(subst /,$(COMPILER_SEP),$(PSNAMES_DIR)) \
-                         $(INCLUDE_FLAGS)                            \
-                         $(FT_CFLAGS)
-
-
-# psnames driver sources (i.e., C files)
-#
-PSNAMES_DRV_SRC := $(PSNAMES_DIR)/psmodule.c
-
-
-# psnames driver headers
-#
-PSNAMES_DRV_H := $(PSNAMES_DRV_SRC:%.c=%.h) \
-                 $(PSNAMES_DIR)/psnamerr.h  \
-                 $(PSNAMES_DIR)/pstables.h
-
-
-# psnames driver object(s)
-#
-#   PSNAMES_DRV_OBJ_M is used during `multi' builds
-#   PSNAMES_DRV_OBJ_S is used during `single' builds
-#
-PSNAMES_DRV_OBJ_M := $(PSNAMES_DRV_SRC:$(PSNAMES_DIR)/%.c=$(OBJ_DIR)/%.$O)
-PSNAMES_DRV_OBJ_S := $(OBJ_DIR)/psnames.$O
-
-# psnames driver source file for single build
-#
-PSNAMES_DRV_SRC_S := $(PSNAMES_DIR)/psnames.c
-
-
-# psnames driver - single object
-#
-$(PSNAMES_DRV_OBJ_S): $(PSNAMES_DRV_SRC_S) $(PSNAMES_DRV_SRC) \
-                      $(FREETYPE_H) $(PSNAMES_DRV_H)
-	$(PSNAMES_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(PSNAMES_DRV_SRC_S))
-
-
-# psnames driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(PSNAMES_DIR)/%.c $(FREETYPE_H) $(PSNAMES_DRV_H)
-	$(PSNAMES_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(PSNAMES_DRV_OBJ_S)
-DRV_OBJS_M += $(PSNAMES_DRV_OBJ_M)
+MODULE_HEADERS := \
+  psmodule.h \
+  psnamerr.h \
+  pstables.h \
 
+MODULE_WRAPPER := psnames.c
 
 # EOF
diff --git a/src/raster/rules.mk b/src/raster/rules.mk
index 3e949d774..df400dee6 100644
--- a/src/raster/rules.mk
+++ b/src/raster/rules.mk
@@ -12,61 +12,14 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
+MODULE_SOURCES := \
+  ftraster.c \
+  ftrend1.c \
 
-# raster driver directory
-#
-RASTER_DIR := $(SRC_DIR)/raster
-
-# compilation flags for the driver
-#
-RASTER_COMPILE := $(CC) $(ANSIFLAGS)                               \
-                        $I$(subst /,$(COMPILER_SEP),$(RASTER_DIR)) \
-                        $(INCLUDE_FLAGS)                           \
-                        $(FT_CFLAGS)
-
-
-# raster driver sources (i.e., C files)
-#
-RASTER_DRV_SRC := $(RASTER_DIR)/ftraster.c \
-                  $(RASTER_DIR)/ftrend1.c
-
-
-# raster driver headers
-#
-RASTER_DRV_H := $(RASTER_DRV_SRC:%.c=%.h) \
-                $(RASTER_DIR)/rasterrs.h
-
-
-# raster driver object(s)
-#
-#   RASTER_DRV_OBJ_M is used during `multi' builds.
-#   RASTER_DRV_OBJ_S is used during `single' builds.
-#
-RASTER_DRV_OBJ_M := $(RASTER_DRV_SRC:$(RASTER_DIR)/%.c=$(OBJ_DIR)/%.$O)
-RASTER_DRV_OBJ_S := $(OBJ_DIR)/raster.$O
-
-# raster driver source file for single build
-#
-RASTER_DRV_SRC_S := $(RASTER_DIR)/raster.c
-
-
-# raster driver - single object
-#
-$(RASTER_DRV_OBJ_S): $(RASTER_DRV_SRC_S) $(RASTER_DRV_SRC) \
-                     $(FREETYPE_H) $(RASTER_DRV_H)
-	$(RASTER_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(RASTER_DRV_SRC_S))
-
-
-# raster driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(RASTER_DIR)/%.c $(FREETYPE_H) $(RASTER_DRV_H)
-	$(RASTER_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(RASTER_DRV_OBJ_S)
-DRV_OBJS_M += $(RASTER_DRV_OBJ_M)
+MODULE_HEADERS := \
+  $(MODULE_SOURCES:%.c=%.h) \
+  rasterrs.h \
 
+MODULE_WRAPPER := raster.c
 
 # EOF
diff --git a/src/sfnt/rules.mk b/src/sfnt/rules.mk
index f56ef060e..9c0d90a00 100644
--- a/src/sfnt/rules.mk
+++ b/src/sfnt/rules.mk
@@ -12,74 +12,27 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# SFNT driver directory
-#
-SFNT_DIR := $(SRC_DIR)/sfnt
-
-
-# compilation flags for the driver
-#
-SFNT_COMPILE := $(CC) $(ANSIFLAGS)                             \
-                      $I$(subst /,$(COMPILER_SEP),$(SFNT_DIR)) \
-                      $(INCLUDE_FLAGS)                         \
-                      $(FT_CFLAGS)
-
-
-# SFNT driver sources (i.e., C files)
-#
-SFNT_DRV_SRC := $(SFNT_DIR)/pngshim.c   \
-                $(SFNT_DIR)/sfdriver.c  \
-                $(SFNT_DIR)/sfobjs.c    \
-                $(SFNT_DIR)/sfwoff.c    \
-                $(SFNT_DIR)/sfwoff2.c   \
-                $(SFNT_DIR)/ttbdf.c     \
-                $(SFNT_DIR)/ttcmap.c    \
-                $(SFNT_DIR)/ttcolr.c    \
-                $(SFNT_DIR)/ttcpal.c    \
-                $(SFNT_DIR)/ttkern.c    \
-                $(SFNT_DIR)/ttload.c    \
-                $(SFNT_DIR)/ttmtx.c     \
-                $(SFNT_DIR)/ttpost.c    \
-                $(SFNT_DIR)/ttsbit.c    \
-                $(SFNT_DIR)/woff2tags.c
-
-# SFNT driver headers
-#
-SFNT_DRV_H := $(SFNT_DRV_SRC:%c=%h)  \
-              $(SFNT_DIR)/sferrors.h
-
-
-# SFNT driver object(s)
-#
-#   SFNT_DRV_OBJ_M is used during `multi' builds.
-#   SFNT_DRV_OBJ_S is used during `single' builds.
-#
-SFNT_DRV_OBJ_M := $(SFNT_DRV_SRC:$(SFNT_DIR)/%.c=$(OBJ_DIR)/%.$O)
-SFNT_DRV_OBJ_S := $(OBJ_DIR)/sfnt.$O
-
-# SFNT driver source file for single build
-#
-SFNT_DRV_SRC_S := $(SFNT_DIR)/sfnt.c
-
-
-# SFNT driver - single object
-#
-$(SFNT_DRV_OBJ_S): $(SFNT_DRV_SRC_S) $(SFNT_DRV_SRC) \
-                   $(FREETYPE_H) $(SFNT_DRV_H)
-	$(SFNT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(SFNT_DRV_SRC_S))
-
-
-# SFNT driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(SFNT_DIR)/%.c $(FREETYPE_H) $(SFNT_DRV_H)
-	$(SFNT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(SFNT_DRV_OBJ_S)
-DRV_OBJS_M += $(SFNT_DRV_OBJ_M)
-
+MODULE_SOURCES := \
+  pngshim.c   \
+  sfdriver.c  \
+  sfobjs.c    \
+  sfwoff.c    \
+  sfwoff2.c   \
+  ttbdf.c     \
+  ttcmap.c    \
+  ttcolr.c    \
+  ttcpal.c    \
+  ttkern.c    \
+  ttload.c    \
+  ttmtx.c     \
+  ttpost.c    \
+  ttsbit.c    \
+  woff2tags.c \
+
+MODULE_HEADERS := \
+  $(MODULE_SOURCES:%.c=%.h) \
+  sferrors.h \
+
+MODULE_WRAPPER := sfnt.c
 
 # EOF
diff --git a/src/smooth/rules.mk b/src/smooth/rules.mk
index b08056fac..642a833bc 100644
--- a/src/smooth/rules.mk
+++ b/src/smooth/rules.mk
@@ -12,62 +12,15 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
+MODULE_SOURCES := \
+  ftgrays.c \
+  ftsmooth.c \
 
-# smooth driver directory
-#
-SMOOTH_DIR := $(SRC_DIR)/smooth
-
-
-# compilation flags for the driver
-#
-SMOOTH_COMPILE := $(CC) $(ANSIFLAGS)                               \
-                        $I$(subst /,$(COMPILER_SEP),$(SMOOTH_DIR)) \
-                        $(INCLUDE_FLAGS)                           \
-                        $(FT_CFLAGS)
-
-
-# smooth driver sources (i.e., C files)
-#
-SMOOTH_DRV_SRC := $(SMOOTH_DIR)/ftgrays.c  \
-                  $(SMOOTH_DIR)/ftsmooth.c
-
-
-# smooth driver headers
-#
-SMOOTH_DRV_H := $(SMOOTH_DRV_SRC:%c=%h)  \
-                $(SMOOTH_DIR)/ftsmerrs.h
-
-
-# smooth driver object(s)
-#
-#   SMOOTH_DRV_OBJ_M is used during `multi' builds.
-#   SMOOTH_DRV_OBJ_S is used during `single' builds.
-#
-SMOOTH_DRV_OBJ_M := $(SMOOTH_DRV_SRC:$(SMOOTH_DIR)/%.c=$(OBJ_DIR)/%.$O)
-SMOOTH_DRV_OBJ_S := $(OBJ_DIR)/smooth.$O
-
-# smooth driver source file for single build
-#
-SMOOTH_DRV_SRC_S := $(SMOOTH_DIR)/smooth.c
-
-
-# smooth driver - single object
-#
-$(SMOOTH_DRV_OBJ_S): $(SMOOTH_DRV_SRC_S) $(SMOOTH_DRV_SRC) \
-                     $(FREETYPE_H) $(SMOOTH_DRV_H)
-	$(SMOOTH_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(SMOOTH_DRV_SRC_S))
-
-
-# smooth driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(SMOOTH_DIR)/%.c $(FREETYPE_H) $(SMOOTH_DRV_H)
-	$(SMOOTH_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(SMOOTH_DRV_OBJ_S)
-DRV_OBJS_M += $(SMOOTH_DRV_OBJ_M)
+MODULE_HEADERS := \
+  ftgrays.h \
+  ftsmooth.h \
+  ftsmerrs.h \
 
+MODULE_WRAPPER := smooth.c
 
 # EOF
diff --git a/src/truetype/rules.mk b/src/truetype/rules.mk
index 2f6fecfc4..4819e683f 100644
--- a/src/truetype/rules.mk
+++ b/src/truetype/rules.mk
@@ -12,65 +12,19 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# TrueType driver directory
-#
-TT_DIR := $(SRC_DIR)/truetype
-
-
-# compilation flags for the driver
-#
-TT_COMPILE := $(CC) $(ANSIFLAGS)                           \
-                    $I$(subst /,$(COMPILER_SEP),$(TT_DIR)) \
-                    $(INCLUDE_FLAGS)                       \
-                    $(FT_CFLAGS)
-
-
-# TrueType driver sources (i.e., C files)
-#
-TT_DRV_SRC := $(TT_DIR)/ttdriver.c \
-              $(TT_DIR)/ttgload.c  \
-              $(TT_DIR)/ttgxvar.c  \
-              $(TT_DIR)/ttinterp.c \
-              $(TT_DIR)/ttobjs.c   \
-              $(TT_DIR)/ttpload.c  \
-              $(TT_DIR)/ttsubpix.c
-
-# TrueType driver headers
-#
-TT_DRV_H := $(TT_DRV_SRC:%.c=%.h) \
-            $(TT_DIR)/tterrors.h
-
-
-# TrueType driver object(s)
-#
-#   TT_DRV_OBJ_M is used during `multi' builds
-#   TT_DRV_OBJ_S is used during `single' builds
-#
-TT_DRV_OBJ_M := $(TT_DRV_SRC:$(TT_DIR)/%.c=$(OBJ_DIR)/%.$O)
-TT_DRV_OBJ_S := $(OBJ_DIR)/truetype.$O
-
-# TrueType driver source file for single build
-#
-TT_DRV_SRC_S := $(TT_DIR)/truetype.c
-
-
-# TrueType driver - single object
-#
-$(TT_DRV_OBJ_S): $(TT_DRV_SRC_S) $(TT_DRV_SRC) $(FREETYPE_H) $(TT_DRV_H)
-	$(TT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(TT_DRV_SRC_S))
-
-
-# driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(TT_DIR)/%.c $(FREETYPE_H) $(TT_DRV_H)
-	$(TT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(TT_DRV_OBJ_S)
-DRV_OBJS_M += $(TT_DRV_OBJ_M)
-
+MODULE_SOURCES := \
+  ttdriver.c \
+  ttgload.c \
+  ttgxvar.c \
+  ttinterp.c \
+  ttobjs.c \
+  ttpload.c \
+  ttsubpix.c \
+
+MODULE_HEADERS := \
+  $(MODULE_SOURCES:%.c=%.h) \
+  tterrors.h \
+
+MODULE_WRAPPER := truetype.c
 
 # EOF
diff --git a/src/type1/rules.mk b/src/type1/rules.mk
index 213e61924..f75d94a8a 100644
--- a/src/type1/rules.mk
+++ b/src/type1/rules.mk
@@ -12,65 +12,19 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# Type1 driver directory
-#
-T1_DIR := $(SRC_DIR)/type1
-
-
-# compilation flags for the driver
-#
-T1_COMPILE := $(CC) $(ANSIFLAGS)                           \
-                    $I$(subst /,$(COMPILER_SEP),$(T1_DIR)) \
-                    $(INCLUDE_FLAGS)                       \
-                    $(FT_CFLAGS)
-
-
-# Type1 driver sources (i.e., C files)
-#
-T1_DRV_SRC := $(T1_DIR)/t1parse.c  \
-              $(T1_DIR)/t1load.c   \
-              $(T1_DIR)/t1driver.c \
-              $(T1_DIR)/t1afm.c    \
-              $(T1_DIR)/t1gload.c  \
-              $(T1_DIR)/t1objs.c
-
-# Type1 driver headers
-#
-T1_DRV_H := $(T1_DRV_SRC:%.c=%.h) \
-            $(T1_DIR)/t1tokens.h  \
-            $(T1_DIR)/t1errors.h
-
-
-# Type1 driver object(s)
-#
-#   T1_DRV_OBJ_M is used during `multi' builds
-#   T1_DRV_OBJ_S is used during `single' builds
-#
-T1_DRV_OBJ_M := $(T1_DRV_SRC:$(T1_DIR)/%.c=$(OBJ_DIR)/%.$O)
-T1_DRV_OBJ_S := $(OBJ_DIR)/type1.$O
-
-# Type1 driver source file for single build
-#
-T1_DRV_SRC_S := $(T1_DIR)/type1.c
-
-
-# Type1 driver - single object
-#
-$(T1_DRV_OBJ_S): $(T1_DRV_SRC_S) $(T1_DRV_SRC) $(FREETYPE_H) $(T1_DRV_H)
-	$(T1_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(T1_DRV_SRC_S))
-
-
-# Type1 driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(T1_DIR)/%.c $(FREETYPE_H) $(T1_DRV_H)
-	$(T1_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(T1_DRV_OBJ_S)
-DRV_OBJS_M += $(T1_DRV_OBJ_M)
-
+MODULE_SOURCES := \
+  t1parse.c \
+  t1load.c \
+  t1driver.c \
+  t1afm.c \
+  t1gload.c \
+  t1objs.c \
+
+MODULE_HEADERS := \
+  $(MODULE_SOURCES:%.c=%.h) \
+  t1tokens.h \
+  t1errors.h \
+
+MODULE_WRAPPER := type1.c
 
 # EOF
diff --git a/src/type42/rules.mk b/src/type42/rules.mk
index f4ce91a3b..6202f00ef 100644
--- a/src/type42/rules.mk
+++ b/src/type42/rules.mk
@@ -12,62 +12,16 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
+MODULE_SOURCES := \
+  t42objs.c \
+  t42parse.c \
+  t42drivr.c \
 
-# Type42 driver directory
-#
-T42_DIR := $(SRC_DIR)/type42
-
-
-# compilation flags for the driver
-#
-T42_COMPILE := $(CC) $(ANSIFLAGS)                            \
-                     $I$(subst /,$(COMPILER_SEP),$(T42_DIR)) \
-                     $(INCLUDE_FLAGS)                        \
-                     $(FT_CFLAGS)
-
-
-# Type42 driver source
-#
-T42_DRV_SRC := $(T42_DIR)/t42objs.c  \
-               $(T42_DIR)/t42parse.c \
-               $(T42_DIR)/t42drivr.c
-
-# Type42 driver headers
-#
-T42_DRV_H := $(T42_DRV_SRC:%.c=%.h) \
-             $(T42_DIR)/t42error.h  \
-             $(T42_DIR)/t42types.h
-
-
-# Type42 driver object(s)
-#
-#   T42_DRV_OBJ_M is used during `multi' builds
-#   T42_DRV_OBJ_S is used during `single' builds
-#
-T42_DRV_OBJ_M := $(T42_DRV_SRC:$(T42_DIR)/%.c=$(OBJ_DIR)/%.$O)
-T42_DRV_OBJ_S := $(OBJ_DIR)/type42.$O
-
-# Type42 driver source file for single build
-#
-T42_DRV_SRC_S := $(T42_DIR)/type42.c
-
-
-# Type42 driver - single object
-#
-$(T42_DRV_OBJ_S): $(T42_DRV_SRC_S) $(T42_DRV_SRC) $(FREETYPE_H) $(T42_DRV_H)
-	$(T42_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(T42_DRV_SRC_S))
-
-
-# Type42 driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(T42_DIR)/%.c $(FREETYPE_H) $(T42_DRV_H)
-	$(T42_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(T42_DRV_OBJ_S)
-DRV_OBJS_M += $(T42_DRV_OBJ_M)
+MODULE_HEADERS := \
+  $(MODULE_SOURCES:%.c=%.h) \
+  t42error.h \
+  t42types.h \
 
+MODULE_WRAPPER := type42.c
 
 # EOF
diff --git a/src/winfonts/rules.mk b/src/winfonts/rules.mk
index e73ef5ea9..dff1fa96e 100644
--- a/src/winfonts/rules.mk
+++ b/src/winfonts/rules.mk
@@ -12,57 +12,8 @@
 # indicate that you have read the license and understand and accept it
 # fully.
 
-
-# Windows driver directory
-#
-FNT_DIR := $(SRC_DIR)/winfonts
-
-
-FNT_COMPILE := $(CC) $(ANSIFLAGS)                            \
-                     $I$(subst /,$(COMPILER_SEP),$(FNT_DIR)) \
-                     $(INCLUDE_FLAGS)                        \
-                     $(FT_CFLAGS)
-
-
-# Windows driver sources (i.e., C files)
-#
-FNT_DRV_SRC := $(FNT_DIR)/winfnt.c
-
-# Windows driver headers
-#
-FNT_DRV_H := $(FNT_DRV_SRC:%.c=%.h) \
-             $(FNT_DIR)/fnterrs.h
-
-
-# Windows driver object(s)
-#
-#   FNT_DRV_OBJ_M is used during `multi' builds
-#   FNT_DRV_OBJ_S is used during `single' builds
-#
-FNT_DRV_OBJ_M := $(FNT_DRV_SRC:$(FNT_DIR)/%.c=$(OBJ_DIR)/%.$O)
-FNT_DRV_OBJ_S := $(OBJ_DIR)/winfnt.$O
-
-# Windows driver source file for single build
-#
-FNT_DRV_SRC_S := $(FNT_DIR)/winfnt.c
-
-
-# Windows driver - single object
-#
-$(FNT_DRV_OBJ_S): $(FNT_DRV_SRC_S) $(FNT_DRV_SRC) $(FREETYPE_H) $(FNT_DRV_H)
-	$(FNT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(FNT_DRV_SRC_S))
-
-
-# Windows driver - multiple objects
-#
-$(OBJ_DIR)/%.$O: $(FNT_DIR)/%.c $(FREETYPE_H) $(FNT_DRV_H)
-	$(FNT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
-
-
-# update main driver object lists
-#
-DRV_OBJS_S += $(FNT_DRV_OBJ_S)
-DRV_OBJS_M += $(FNT_DRV_OBJ_M)
-
+MODULE_HEADERS := winfnt.h fnterrs.h
+MODULE_SOURCES := winfnt.c
+MODULE_WRAPPER := winfnt.c
 
 # EOF
-- 
2.20.1

Reply via email to