summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
l---------README.md1
-rw-r--r--build-aux/Makefile.README.mk20
-rw-r--r--build-aux/Makefile.README.old.txt35
-rw-r--r--build-aux/Makefile.README.txt571
-rw-r--r--build-aux/Makefile.each.head/00-dist.mk5
-rw-r--r--build-aux/Makefile.each.head/00-files.mk32
-rw-r--r--build-aux/Makefile.each.head/00-gitfiles.mk (renamed from build-aux/Makefile.once.head/10-std.mk)29
-rw-r--r--build-aux/Makefile.each.head/00-nested.mk16
-rw-r--r--build-aux/Makefile.each.head/00-texinfo.mk16
-rw-r--r--build-aux/Makefile.each.tail/00-dist.mk1
-rw-r--r--build-aux/Makefile.each.tail/00-mod.mk44
-rw-r--r--build-aux/Makefile.each.tail/00-var.mk22
-rw-r--r--build-aux/Makefile.each.tail/10-files.mk57
-rw-r--r--build-aux/Makefile.each.tail/10-nested.mk20
-rw-r--r--build-aux/Makefile.each.tail/10-std.mk53
-rw-r--r--build-aux/Makefile.each.tail/11-texinfo.mk42
-rw-r--r--build-aux/Makefile.head.mk105
-rw-r--r--build-aux/Makefile.once.head/00-gitfiles.mk75
-rw-r--r--build-aux/Makefile.once.head/00-quote.mk49
-rw-r--r--build-aux/Makefile.once.head/00-var.mk33
-rw-r--r--build-aux/Makefile.once.head/00-write-ifchanged.mk1
-rw-r--r--build-aux/Makefile.once.head/10-dist.mk79
-rw-r--r--build-aux/Makefile.once.head/10-files.mk87
-rw-r--r--build-aux/Makefile.once.head/10-gnuconf.mk (renamed from build-aux/Makefile.once.head/00-gnuconf.mk)72
-rw-r--r--build-aux/Makefile.once.head/10-nested.mk45
-rw-r--r--build-aux/Makefile.once.head/10-texinfo.mk51
-rw-r--r--build-aux/Makefile.once.head/10-write-atomic.mk23
-rw-r--r--build-aux/Makefile.once.head/10-write-ifchanged.mk23
-rw-r--r--build-aux/Makefile.once.head/zz-mod.mk59
-rw-r--r--build-aux/Makefile.once.tail/00-dist.mk20
-rw-r--r--build-aux/Makefile.tail.mk35
-rwxr-xr-x[-rw-r--r--]build-aux/write-atomic (renamed from build-aux/Makefile.once.head/00-dist.mk)33
-rwxr-xr-xbuild-aux/write-ifchanged2
-rw-r--r--config.mk.in198
35 files changed, 1632 insertions, 325 deletions
diff --git a/.gitignore b/.gitignore
index 9a81e7b..a94ae7b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,7 @@ emacsterm-xterm
*.sh
-.srcfiles.mk
.tmp.*
.var.*
+
+/gitfiles.mk
diff --git a/README.md b/README.md
new file mode 120000
index 0000000..5e5ea4a
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+build-aux/Makefile.README.txt \ No newline at end of file
diff --git a/build-aux/Makefile.README.mk b/build-aux/Makefile.README.mk
new file mode 100644
index 0000000..fe9010a
--- /dev/null
+++ b/build-aux/Makefile.README.mk
@@ -0,0 +1,20 @@
+# This Makefile is a minimal stub that exists to allow the
+# `at-modules` set of Make targets to print documentation for the
+# present Autothing modules.
+#
+# This file is part of the documentation for Autothing.
+#
+# Copyright (C) 2017 Luke Shumaker
+#
+# This documentation file is placed into the public domain. If that
+# is not possible in your legal system, I grant you permission to use
+# it in absolutely every way that I can legally grant to you.
+
+dist.pkgname = autothing
+dist.version = 1.0
+gnuconf.pkgname = autothing
+
+topoutdir ?= .
+topsrcdir ?= .
+include $(topsrcdir)/build-aux/Makefile.head.mk
+include $(topsrcdir)/build-aux/Makefile.tail.mk
diff --git a/build-aux/Makefile.README.old.txt b/build-aux/Makefile.README.old.txt
new file mode 100644
index 0000000..b4ea562
--- /dev/null
+++ b/build-aux/Makefile.README.old.txt
@@ -0,0 +1,35 @@
+Obsolete
+========
+
+The following was written for previous versions of Autothing. I'm
+leaving it here for now because I'll likely canibalize it for other
+bits of documentation, either for Autothing itself, the `files`
+module, or the `dist` module.
+
+High-level overview
+-------------------
+
+It also makes it easy to follow the GNU standards for your makefiles:
+it takes care of this entire table of .PHONY targets for you:
+
+| this | and this | are aliases for this |
+|------+------------------+--------------------------------------------------------|
+| all | build | $(outdir)/build |
+| | install | $(outdir)/install |
+| | uninstall | $(outdir)/uninstall |
+| | mostlyclean | $(outdir)/mostlyclean |
+| | clean | $(outdir)/clean |
+| | distclean | $(outdir)/distclean |
+| | maintainer-clean | $(outdir)/maintainer-clean |
+| | check | $(outdir)/check (not implemented for you) |
+| | dist | $(topoutdir)/$(PACKAGE)-$(VERSION).tar.gz (not .PHONY) |
+
+(You are still responsible for implementing the `$(outdir)/check`
+target in each of your Makefiles.)
+
+----
+Copyright (C) 2016 Luke Shumaker
+
+This documentation file is placed into the public domain. If that is
+not possible in your legal system, I grant you permission to use it in
+absolutely every way that I can legally grant to you.
diff --git a/build-aux/Makefile.README.txt b/build-aux/Makefile.README.txt
index e06ba52..f67ede2 100644
--- a/build-aux/Makefile.README.txt
+++ b/build-aux/Makefile.README.txt
@@ -1,163 +1,446 @@
-Luke's AutoMake
-===============
+# -*- Mode: markdown -*-
-Yo, this document is incomplete. It describes the magical
-automake.{head,tail}.mk Makefiles and how to use them, kinda.
+Autothing 3: The smart way to write GNU Makefiles
+=================================================
-I wrote a "clone" of automake. I say clone, because it works
-differently. Yeah, I need a new name for it.
+Autothing is a thing that does things automatically.
-High-level overview
--------------------
+Ok, more helpfully: Autothing is a pair of .mk Makefile fragments
+(`Makefile.head.mk` and `Makefile.tail.mk`) that you can `include`
+from your Makefiles to make them easier to write; specifically, it
+makes it _easy_ to write non-recursive Makefiles--and ones that are
+similar to plain recursive Makefiles, at that!
+
+To many people, talking about GNU Make directly is a non-starter
+because it means giving up the many other features that things like
+GNU Automake provide. Other projects like GNU Automake were created
+to plaster over differences between make(1) implementations; however,
+this isn't all that Automake provides, it also makes it easy to do
+complex things that users want, or the GNU Coding Standards require.
+That's silly; the implementation of these features should be
+orthogonal to plastering over the differences between Make
+implementations. So, in addition to the Automake core, Automake is
+distributed with several "modules" that implement similar feature sets
+to what Automake provides.
+
+Autothing does depend on GNU Make; other make(1) implementations will
+not work. However, if you are open to adding GNU Make as a
+dependency, then Autothing should obviate the need for GNU Automake,
+while also making your Makefiles better.
+
+Non-recursive?
+--------------
+
+ (For those of you who aren't up on Makefile jargon)
+
+When you have a project that spans multiple directories, you'll
+probably want to split up the Makefile, having the appropriate parts
+in each sub-directory. There are a number of strategies you can use
+to approach this.
+
+One of the more prevelant strategies (so much so that GNU make
+includes special support for it) is to write "recursive Makefiles";
+that is, have Make rules that include commands like
+
+ other-directory/libfoo.so:
+ $(MAKE) -C other-directory libfoo.so
+
+or
+
+ other-directory/libfoo.so
+ cd other-directory && $(MAKE) libfoo.so
+
+This approach is popular because it is both very easy to implement,
+and is supported by a wide variety of Make implementations. But, it
+also introduces a wide variety of issues; so much so that a rather
+famous paper was written about it: "Recursive Make Considered Harmful"
+(Miller, 1997).
+
+For all of the arguments against it, and all of the alternative
+approaches, recusive Makefiles are hard to beat because they are just
+so easy to write, and the alternatives... aren't. UNTIL NOW!
+
+Instead of having rules that spawn a separate Make process in another
+directory for targets in that directory, Autothing lets you provide a
+list of directories that include targets that targets in this
+directory might depend on, and Autothing will automagically include
+the Makefile in that other directory into *this* instance of the Make
+program.
+
+ Peter Miller (1997) "Recursive Make Considered Harmful"
+ <http://aegis.sourceforge.net/auug97.pdf>
+
+An example Makefile / Introduction
+----------------------------------
+
+Write your Makefiles of the form:
+
+ # Initialize basic information about how your project is structured.
+ topsrcdir ?= ...
+ topoutdir ?= ...
+
+ # Include the Autothing entry point
+ include $(topsrcdir)/build-aux/Makefile.head.mk
+
+ # Now write your Makefile very similarly to how you normally
+ # would. Just make sure that outputs are relative to $(outdir)
+ # and inputs relative to $(srcdir).
+ $(outdir)/%.o: $(srcdir)/%.c:
+ $(CC) -c -o $@ $<
+ $(outdir)/hello: $(outdir)/hello.o
+
+ # If any of the dependencies of files here are outputs of a
+ # Makefile in another directory, list those directories here.
+ at.subdirs = ...
+
+ # This part is kind of a pain: define a list of ouput targets that
+ # this Makefile produces.
+ at.targets = $(outdir)/%.o $(outdir)/hello
+
+ # Include the Autothing exit point
+ include $(topsrcdir)/build-aux/Makefile.tail.mk
+
+This is similar to, but not quite, the comfortable way that you probably
+already write your Makefiles.
+
+It is recommended that Autothing lives inside of the "build-aux"
+directory in the top level of your project sources; "build-aux" is a
+standard directory for auxiliary build programs and tools.
+
+What does Autothing do for me?
+------------------------------
+
+There are two fundamental things that Autothing provides:
+
+ 1. Variable namespacing
+ 2. Tools for dealing with paths
+
+The first is important because globals are bad for composability.
+
+The second is important because GNU Make is too dumb to know that
+`foo/bar/../baz` == `foo/baz`.
+
+Then, there's something that maybe doesn't belong, but I didn't have the heart
+to cut it out:
+
+ 3. A module (plugin) system, which allows for modules to provide
+ additional feature sets.
+
+The module system is "important" because there are very often common bits that
+you want to be included in every Makefile, and this gives some structure to
+that.
-Now, what this does for you is:
-
-It makes it _easy_ to write non-recursive Makefiles--and ones that are
-similar to plain recursive Makefiles, at that! (search for the paper
-"Recursive Make Considered Harmful") As harmful as recursive make is,
-it's historically been difficult to to write non-recursive Makefiles.
-This makes it easy.
-
-It also makes it easy to follow the GNU standards for your makefiles:
-it takes care of this entire table of .PHONY targets for you:
-
-| this | and this | are aliases for this |
-|------+------------------+--------------------------------------------------------|
-| all | build | $(outdir)/build |
-| | install | $(outdir)/install |
-| | uninstall | $(outdir)/uninstall |
-| | mostlyclean | $(outdir)/mostlyclean |
-| | clean | $(outdir)/clean |
-| | distclean | $(outdir)/distclean |
-| | maintainer-clean | $(outdir)/maintainer-clean |
-| | check | $(outdir)/check (not implemented for you) |
-| | dist | $(topoutdir)/$(PACKAGE)-$(VERSION).tar.gz (not .PHONY) |
-
-(You are still responsible for implementing the `$(outdir)/check`
-target in each of your Makefiles.)
-
-What you have to do is:
-
-In each source directory, you write a `Makefile`, very similarly to if
-you were writing for plain GNU Make, with
-
- topoutdir ?= ...
- topsrcdir ?= ...
- include $(topsrcdir)/build-aux/Makefile.head.mk
-
- # your makefile
-
- include $(topsrcdir)/build-aux/Makefile.tail.mk
-
-And in the top-level source directory, Write your own helper makefiles
-that get included:
- - `common.once.head.mk`: before parsing any of your Makefiles
- - `common.each.head.mk`: before parsing each of your Makefiles
- - `common.each.tail.mk`: after parsing each of your Makefiles
- - `common.each.tail.mk`: after parsing all of your Makefiles
-
-The `common.*.mk` makefiles are nice for including generic pattern
-rules and variables that aren't specific to a directory.
-
-You're probably thinking that this sounds too good to be true!
-Unfortunately, there are two major deviations from writing a plain
-recursive Makefile:
-
- 1. all targets and prerequisites (including .PHONY targets!) need to
- be prefixed with
- `$(srcdir)`/`$(outdir)`/`$(topsrcdir)`/`$(topoutdir)`.
- * sub-gotcha: this means that if a pattern rule has a
- prerequisite that may be in srcdir or outdir, then it must be
- specified twice, once for each case.
- 2. if a prerequisite is in a directory "owned" by another Makefile,
- you must filter the pathname through `am_path`:
- `$(call am_path,YOUR_PATH)`. Further, that path must NOT contain
- a `..` segment; if you need to refer to a sibling directory, do it
- relative to `$(topoutdir)` or `$(topsrcdir)`.
-
-Telling automake about your program
------------------------------------
-
-You tell automake what to do for you by setting some variables. They
-are all prefixed with `am_`; this prefix may be changed by editing the
-`_am` variable at the top of `automake.head.mk`.
-
-The exception to this is the `am_path` variable, which is a macro that
-is used to make a list of filenames relative to the appropriate
-directory, because unlike normal GNU (Auto)Make, `$(outdir)` isn't
-nescessarily equal to `.`. See above.
-
-There are several commands that generate files; simply record the list
-of files that each command generates as the following variable
-variables:
-
-| Variable | Create Command | Delete Command | Description | Relative to |
-|--------------+----------------+-----------------------------+-----------------------------------+-------------|
-| am_src_files | emacs | rm -rf . | Files that the developer writes | srcdir |
-| am_gen_files | ??? | make maintainer-clean | Files the developer compiles | srcdir |
-| am_cfg_files | ./configure | make distclean | Users' compile-time configuration | outdir |
-| am_out_files | make all | make mostlyclean/make clean | Files the user compiles | outdir |
-| am_sys_files | make install | make uninstall | Files the user installs | DESTDIR |
-
-In addition, there are two more variables that control not how files
-are created, but how they are deleted:
-
-| Variable | Affected command | Description | Relative to |
-|----------------+------------------+------------------------------------------------+-------------|
-| am_clean_files | make clean | A list of things to `rm` in addition to the | outdir |
-| | | files in `$(am_out_files)`. (Example: `*.o`) | |
-|----------------+------------------+------------------------------------------------+-------------|
-| am_slow_files | make mostlyclean | A list of things that (as an exception) should | outdir |
-| | | _not_ be deleted. (otherwise, `mostlyclean` | |
-| | | is the same as `clean`) | |
-
-Finally, there are two variables that express the relationships
-between directories:
-
-| Variable | Description |
-|------------+---------------------------------------------------------|
-| am_subdirs | A list of other directories (containing Makefiles) that |
-| | may be considered "children" of this |
-| | directory/Makefile; building a phony target in this |
-| | directory should also build it in the subdirectory. |
-| | They are not necesarily actually subdirectories of this |
-| | directory in the filesystem. |
-|------------+---------------------------------------------------------|
-| am_depdirs | A list of other directories (containing Makefiles) that |
-| | contain or generate files that are dependencies of |
-| | targets in this directory. They are not necesarily |
-| | actually subdirectories of this directory in the |
-| | filesystem. Except for files that are dependencies of |
-| | files in this directory, things in the dependency |
-| | directory will not be built. |
+Let's step through each of those features.
+
+## Variable namespacing
+
+When you write a Makefile, you quite likely use (global) variables.
+When you have a project that uses multiple Makefiles, each Makefile
+might have the same variable names, but with different values
+(especially if converting from recursive Make).
+
+You could be very disciplined and carefully name your variables so
+that they don't conflict. This is difficult and error prone normally,
+but becomes neigh-on-impossible if you are converting a large-ish
+project from recursive Make.
+
+So, Autothing provides a solution. If you provide Autothing with a
+list of targets defined in your Makefile (via the `at.targets`
+variable), Autothing will make any variables you defined local to that
+Makefile; they will be present when making targets listed in
+`at.targets`, but will be hidden from other Makfiles that get
+included.
+
+Any variables defined before `Makefile.head.mk` is included are
+treated as truly global; all Makefiles included will have access to
+them.
+
+## Tools for dealing with paths
+
+As stated above, GNU Make is too dumb to realize that `foo/bar/../baz`
+== `foo/baz`; so one has to be reasonably careful about path
+normalization. For dealing with path normalization problems that
+arise because of the way Autothing inclusions work, several global
+functions are provided for dealing with paths.
+
+`$(call at.is_subdir,a,b)` returns whether `b` is a sub-directory of
+`a` (including `a` as a sub-directory of itself).
+`at.is_strict_subdir` does the same, but does not treat `a` as a
+sub-directory of itself. (These function names mimic the terms
+"subset" and "strict subset" in mathematics.) These use an empty
+string for "false" and a non-empty string for "true".
+
+`$(call at.path,files...)` is a generic path-normalization routine.
+The outputs of the other `at.*` functions are already normalized, and
+do not need to be passed through this. Files immediately inside of
+`$(srcdir)` or `$(outdir)` (without another directory name after the
+variable) are already normalized, and do not need to be passed through
+this function either. However, it is always safe to pass a path
+through this function, so if in doubt, call `at.path`.
+
+`$(call at.relbase,dir,files...)` and its cousin `at.relto` take a
+directory and a list of files, and transform each of the filenames to
+be relative to the directory, if the file is inside of the directory.
+Where they differ is that if the file is not inside of the directory;
+`at.relbase` transforms it into an absolute path, while `at.relto`
+prepends as many `../` segments as necessary to make it relative to
+the directory. (These function names mimic the `--relative-base` and
+`--relative-to` flags of the `realpath` utility that is part of GNU
+coreutils.)
+
+If `$(srcdir)` and `$(outdir)` are the same, then `$(call
+at.out2src,files...)` is a no-op, but otherwise it takes a (possibly
+relative) path in `$(outdir)`, and transforms it to the equivalent
+filename in `$(srcdir)`.
+
+`$(call at.addprefix,dir,files...)` takes a directory and a list of
+filenames, and looks at each filename; if it is an absolute path, it
+passes it through (well, "only" normalizes it); if the filename is a
+relative path, it is joined with the given base directory.
+
+## Modules to provide feature sets
+
+The module system serves two purposes
+
+ 1. Allow your developers to share logic between Makefiles in multiple
+ directories.
+ 2. Allow your developers to import "standard" modules implementing
+ common feature sets, so they don't have to.
+
+Distributed along with autothing are some "standard" modules that
+provide commonly desired functionality from Makefiles; tricky little
+things that your developers shouldn't have to implement themselves for
+every project; the things that GNU Automake would take care of if you
+used Automake (a piece of software that Autothing hopes to replace).
+
+The module system is conceptually quite simple: have 4 directories for
+`.mk` makefile snippets that get included at certain points:
+
+ Makefile.once.head/*.mk
+
+ Makefile.each.head/*.mk
+ a/Makefile
+ Makefile.each.tail/*.mk
+
+ Makefile.each.head/*.mk
+ b/Makefile
+ Makefile.each.tail/*.mk
+
+ Makefile.each.head/*.mk
+ c/Makefile
+ Makefile.each.tail/*.mk
+
+ Makefile.once.tail/*.mk
+
+Deciding which of the 4 directories to put your snippets in... you'll
+figure it out pretty quickly once you start playing with it.
+
+Beyond these 4 directories, Autothing itself imposes no structure, but
+there are some conventions that are followed by the distributed along
+with Autothing, and I recommend that your developers follow.
+
+Each of the `.mk` files is name `NN-MODULE.mk` where NN is a number
+(to affect the order that the module files are evaluated in, in case
+of dependencies between them), and MODULE is the module name. Each
+module has "public" variables prefixed with `MODULE.`, and "private"
+variables prefixed with `_MODULE.` (again, "MODULE" being the module
+name). For example, the "groups" parameter of the "files" module is
+configured via the `files.groups` variable. Within this convention,
+Autothing presents itself as a pseudo-module named "at"; that is,
+public Autothing variables are prefixed with `at.`.
+
+If you follow these conventions, then the "mod" module distributed
+along with Autothing can display information about the modules that a
+project uses, and documentation on each module. Running the command
+`make at-modules` (implemented by the "mod" module) will produce a
+list of the modules present in a project, and short descriptions of
+them:
+
+ $ make at-modules
+ Autothing modules used in this project:
+ - dist `dist` target for distribution tarballs (more)
+ - files Keeping track of groups of files (more)
+ - gitfiles Automatically populate files.src.src from git (more)
+ - gnuconf GNU standard configuration variables (more)
+ - mod Display information about Autothing modules (more)
+ - nested Easy nested .PHONY targets (more)
+ - quote Macros to quote tricky strings (more)
+ - texinfo The GNU documentation system (more)
+ - var Depend on the values of variables (more)
+ - write-atomic `write-atomic` auxiliary build script (more)
+ - write-ifchanged `write-ifchanged` auxiliary build script (more)
+
+The "(more)" at the end of a line indicates that there is further
+documentation for that module, which can be produced by running the
+command `make at-modules/MODULE_NAME`. See the output of `make
+at-modules/mod` for instructions on how to produce this further
+documentation for modules you develop.
+
+Besides the "mod" module, the set modules distributed along with
+Autothing primarily exists to provide the bits of (sometimes somewhat
+tricky) functionality required of Makefiles by the GNU Coding
+Standards. Run the `at-modules` commands above for documentation on
+each of them.
+
+Formal interface
+----------------
+
+System requirements:
+ - A version of GNU Make that supports `undefine` (ie, version 3.82
+ and above).
+
+ If the user attempts to use your Autothing-using Makefile with an
+ older version of GNU Make, `Makefile.head.mk` will print an error
+ message and refuse to proceed:
+
+ $ make-3.81
+ build-aux/Makefile.head.mk:58: *** Autothing: We need a version of Make that supports 'undefine'. Stop.
+
+Inputs:
+ - In each `Makefile`:
+ - Before `Makefile.head.mk`:
+ - Variable (mandatory) : `topoutdir`
+ - Variable (mandatory) : `topsrcdir` (must not be a subdirectory of `$(topoutdir)`)
+ - Variable (optional) : `at.Makefile` (Default: `Makefile`)
+ - Between `Makefile.head.mk` and `Makefile.tail.mk`:
+ - Variable: `at.targets` (Default: empty)
+ - Variable: `at.subdirs` (Default: empty)
+ - Files:
+ - `${topsrcdir}/build-aux/Makefile.{each,once}.{head,tail}/*.mk`
+
+ Unfortunately, a limitation of Autothing is that it does require a
+ designated "top" directory; it can't be used to have a sub-project
+ that can also be totally separate and built alone. In your
+ Makefiles, before you include `Makefile.head.mk`, you must tell
+ Autothing what the top directory is by setting `topoutdir` and
+ `topsrcdir`.
+
+ If you wish for your per-directory Makefiles to have a name other
+ than `Makefile` (such as `GNUmakefile` or `makefile`, which GNU Make
+ also looks for by default; or another name for project-specific
+ reasons), Autothing supports this by setting the `at.Makefile`
+ variable. Unfortunately, Autothing does not support having a list
+ of filenames to try; so one must be consistent about the filename
+ throughout the project.
+
+ In the body of each Makefile, you may set the `at.targets` variable
+ to list which targets should have access to the variables defined in
+ the body of that Makefile.
+
+ In the body of each Makefile, you may set the `at.subdirs` variable
+ to list of directories which have their own Makefile which produces
+ targets that targets in this directory depend on. Directories
+ listed in `at.subdirs` may be relative or absolute; if relative,
+ they are interpreted as relative to `$(outdir)`.
+
+Outputs:
+ - Global:
+ - Variable (function): `$(call at.is_subdir, parent, child)`
+ - Variable (function): `$(call at.is_strict_subdir, parent, child)`
+ - Variable (function): `$(call at.relbase, parent, children...)`
+ - Variable (function): `$(call at.relto, parent, children...)`
+ - Variable (function): `$(call at.path, paths...)`
+ - Variable (function): `$(call at.out2src, paths...)`
+ - Variable (function): `$(call at.addprefix, prefix, paths...)`
+ - Variable : `$(at.nl)` # a single newline
+ - Per-directory:
+ - Variable: `$(outdir)`
+ - Variable: `$(srcdir)`
+
+ For dealing with path normalization problems that arise because of
+ the way Autothing inclusions work, several global functions are
+ provided for dealing with paths; see the above "Tools for dealing
+ with paths" section for documentation on each of these functions.
+
+ For convenience, it also provides `$(at.nl)` which is a single
+ newline, as newlines are very difficult to type in Make variable
+ values.
Tips, notes
-----------
-I like to have the first (non-comment) line in a Makefile be:
+If you use Autoconf (or similar), I recommend having a file at
+`$(topsrcdir)/config.mk.in` of the form
+
+ ifeq ($(origin topsrcdir),undefined)
+ topoutdir := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
+ topsrcdir := $(topoutdir)/@top_srcdir@
+
+ # Any other global variables you might want to set
+
+ endif
+
+Then have `./configure` generate `$(topoutdir)/config.mk` from it by
+placing `AC_CONFIG_FILES([config.mk])` in your `configure.ac`. I
+recommend that you have `config.mk` be the _only_ Makefile edited by
+`./configure`; which will require manual support to have `./configure`
+link/copy the Makefiles unedited into `$(topoutdir)`; you can do this
+by placing something like this in your `configure.ac`:
+
+ AC_OUTPUT([], [], [
+ if test "$srcdir" != .; then
+ find "$srcdir" -name Makefile -printf '%P\n' \
+ | while read -r filename; do
+ mkdir -p "\$(dirname "\$filename")"
+ ln -srfT "$srcdir/\$filename" "\$filename"
+ done
+ fi
+ ])
+
+This will allow you to write your Makefiles in the form:
+
+ include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+ include $(topsrcdir)/build-aux/Makefile.head.mk
+
+ # your Makefile here
+
+ include $(topsrcdir)/build-aux/Makefile.tail.mk
+
+Where you only need to adjust the number of `../` segments in the
+first line based on how deep that directory is.
+
+Further development
+-------------------
+
+Most of the modules distributed along with Autothing have the goal of
+combining to provide the things that the GNU Coding Standards require.
+Between `gnuconf`, `dist`, `files`, and `texinfo`; the GNU Coding
+Standards for Makefiles are nearly entirely satisfied. However, there
+are a few targets that are required, but aren't implemented by a
+module (yet!):
+
+ - `install-strip`
+ - `TAGS`
+ - `check`
+ - `installcheck` (optional, but recommended)
+
+TODO
+----
- include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+ - Write documentation on `srcdir`, `outdir`, and out-of-tree builds;
+ I don't think discussions involving the separate `srcdir` and
+ `outdir` make much sense without that context.
-(adjusting the number of `../` sequences as nescessary). Then, my
-(user-editable) `config.mk` is of the form:
+Bugs/Limitations
+----------------
- ifeq ($(topsrcdir),)
- topoutdir := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
- topsrcdir := $(topoutdir)
+ - This documentation file is almost three times as long as the code
+ that it documents.
- # your configuration
+ - The "parse time" for projects with hundreds of sub-directories
+ (each having a Makefile) can be slow (ex: a project with 166
+ directories has a parse time of around 12 seconds on my box). I
+ blame GNU Make's garbage collector; I don't think it was ever
+ designed to deal with as much "garbage" as Autothing's variable
+ namespacing throws at it.
- endif
+ - Requires a designated "top" directory; see discussion above.
-If the package has a `./configure` script, then I have it modifiy
-topsrcdir as necessary, as well as modifying whatever other parts of
-the configuration. All of the configuration lives in `config.mk`;
-`./configure` doesn't modify any `Makefile`s, it just generates
-`config.mk`, and copies (or (sym?)link?) every `$(srcdir)/Makefile` to
-`$(outdir)/Makefile`.
+ - Does not support varying per-directory Makefile names; see
+ discussion above.
----
-Copyright (C) 2016 Luke Shumaker
+Copyright (C) 2016-2017 Luke Shumaker
This documentation file is placed into the public domain. If that is
not possible in your legal system, I grant you permission to use it in
diff --git a/build-aux/Makefile.each.head/00-dist.mk b/build-aux/Makefile.each.head/00-dist.mk
index a094305..924d79a 100644
--- a/build-aux/Makefile.each.head/00-dist.mk
+++ b/build-aux/Makefile.each.head/00-dist.mk
@@ -13,8 +13,11 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+mod.dist.depends += files
+
ifeq ($(outdir),$(topoutdir))
-std.clean_files += $(addprefix $(dist.pkgname)-*,$(dist.exts) .tar /)
+files.out.int += $(addprefix $(dist.pkgname)-*,$(dist.exts) .tar /) .tmp.$(dist.pkgname)-*/
endif
$(outdir)/dist: $(addprefix $(topoutdir)/$(dist.pkgname)-$(dist.version),$(dist.exts))
+.PHONY: $(outdir)/dist
diff --git a/build-aux/Makefile.each.head/00-files.mk b/build-aux/Makefile.each.head/00-files.mk
new file mode 100644
index 0000000..c4820cf
--- /dev/null
+++ b/build-aux/Makefile.each.head/00-files.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2015-2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+files.src.src ?=
+files.src.int ?=
+files.src.cfg ?=
+files.src.gen ?=
+
+files.out.slow ?=
+files.out.int ?=
+files.out.cfg ?=
+
+# define files.out.$(group) files.sys.$(group) for every files.group
+$(eval $(foreach t,$(files.groups),files.out.$t ?=$(at.nl)files.sys.$t ?=$(at.nl)))
+
+# define files.src, files.out, and files.sys aggregates
+$(eval \
+ files.src = $$(sort $(foreach _files.v,$(filter files.src.%,$(.VARIABLES)),$$($(_files.v))))$(at.nl)\
+ files.out = $$(sort $(foreach _files.v,$(filter files.out.%,$(.VARIABLES)),$$($(_files.v))))$(at.nl)\
+ files.sys = $$(sort $(foreach _files.v,$(filter files.sys.%,$(.VARIABLES)),$$($(_files.v)))))
diff --git a/build-aux/Makefile.once.head/10-std.mk b/build-aux/Makefile.each.head/00-gitfiles.mk
index 3e058ec..b872912 100644
--- a/build-aux/Makefile.once.head/10-std.mk
+++ b/build-aux/Makefile.each.head/00-gitfiles.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2016 Luke Shumaker
+# Copyright (C) 2016 Luke Shumaker
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@@ -13,27 +13,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# Declare the default target
-all: build
-.PHONY: all noop
+ifeq ($(abspath $(topsrcdir)),$(abspath $(srcdir)))
+files.src.gen += $(gitfiles.file)
+endif
-# Standard creative PHONY targets
-at.phony += build install installdirs
-# Standard destructive PHONY targets
-at.phony += uninstall mostlyclean clean distclean maintainer-clean
-
-at.dirlocal += std.src_files
-at.dirlocal += std.gen_files
-at.dirlocal += std.cfg_files
-at.dirlocal += std.out_files
-at.dirlocal += std.sys_files
-at.dirlocal += std.clean_files
-at.dirlocal += std.slow_files
-
-# User configuration
-
-DESTDIR ?=
-
-RM ?= rm -f
-RMDIR_P ?= rmdir -p
-TRUE ?= true
+files.src.src += $(_gitfiles.dir.src)
diff --git a/build-aux/Makefile.each.head/00-nested.mk b/build-aux/Makefile.each.head/00-nested.mk
new file mode 100644
index 0000000..4325825
--- /dev/null
+++ b/build-aux/Makefile.each.head/00-nested.mk
@@ -0,0 +1,16 @@
+# Copyright (C) 2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+nested.subdirs ?=
diff --git a/build-aux/Makefile.each.head/00-texinfo.mk b/build-aux/Makefile.each.head/00-texinfo.mk
new file mode 100644
index 0000000..88aaeb5
--- /dev/null
+++ b/build-aux/Makefile.each.head/00-texinfo.mk
@@ -0,0 +1,16 @@
+# Copyright (C) 2016-2017 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+texinfo.docs ?=
diff --git a/build-aux/Makefile.each.tail/00-dist.mk b/build-aux/Makefile.each.tail/00-dist.mk
new file mode 100644
index 0000000..b023e80
--- /dev/null
+++ b/build-aux/Makefile.each.tail/00-dist.mk
@@ -0,0 +1 @@
+_dist.files := $(strip $(_dist.files) $(call at.addprefix,$(srcdir),$(filter-out $(files.src.int),$(files.src))))
diff --git a/build-aux/Makefile.each.tail/00-mod.mk b/build-aux/Makefile.each.tail/00-mod.mk
new file mode 100644
index 0000000..d6514dd
--- /dev/null
+++ b/build-aux/Makefile.each.tail/00-mod.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+$(outdir)/at-variables $(outdir)/at-variables-local: _mod.VARIABLES := $(filter-out $(call quote.pattern,$(_at.VARIABLES)),$(.VARIABLES))
+$(outdir)/at-variables-global:
+ @printf '%s\n' $(call quote.shell-each,$(sort $(.VARIABLES)))
+$(outdir)/at-variables-local:
+ @printf '%s\n' $(call quote.shell-each,$(sort $(_mod.VARIABLES)))
+$(outdir)/at-variables $(outdir)/at-values:
+ @printf '%s\n' $(call quote.shell-each,$(sort $(.VARIABLES),$(_mod.VARIABLES)))
+$(outdir)/at-variables/%:
+ @printf '%s\n' $(call quote.shell,$($*))
+$(outdir)/at-values/%:
+ @printf '%s\n' $(call quote.shell,$(value $*))
+.PHONY: $(addprefix $(outdir)/, at-variables-global at-variables-local at-variables at-values)
+at.targets += $(addprefix $(outdir)/, at-variables-global at-variables-local at-variables at-values at-variables/% at-values/%)
+
+$(outdir)/at-modules:
+ @printf 'Autothing modules used in this project:\n'
+ @printf ' - %s\n' $(foreach _mod.tmp,$(_mod.modules),$(call quote.shell,$(_mod.tmp) $(mod.$(_mod.tmp).description) $(if $(mod.$(_mod.tmp).doc),(more))))|column -t -s $$'\t'
+$(addprefix $(outdir)/at-modules/,$(_mod.modules)): $(outdir)/at-modules/%:
+ @printf 'Name : %s\n' $(call quote.shell,$*)
+ @printf 'Description : %s\n' $(call quote.shell,$(mod.$*.description))
+ @echo 'Depends on :' $(sort $(mod.$*.depends))
+ @echo 'Files :'
+ @printf ' %s\n' $(call quote.shell-each,$(call at.relto,$(topsrcdir),$(sort $(mod.$*.files) $(wildcard $(topsrcdir)/build-aux/Makefile.*/??-$*.mk))))
+ @echo 'Documentation :'
+ @printf '%s\n' $(call quote.shell,$(mod.$*.doc)) | sed -e 's/^# / /' -e 's/^#//'
+
+$(outdir)/at-noop:
+.PHONY: $(outdir)/at-noop
+at.targets += $(outdir)/at-noop
diff --git a/build-aux/Makefile.each.tail/00-var.mk b/build-aux/Makefile.each.tail/00-var.mk
new file mode 100644
index 0000000..c2fd9d7
--- /dev/null
+++ b/build-aux/Makefile.each.tail/00-var.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+mod.var.depends += quote write-ifchanged
+
+$(outdir)/.var.%: _var.FORCE
+ @printf '%s' $(call quote.shell,$($*)) | sed 's/^/#/' | $(WRITE_IFCHANGED) $@
+-include $(wildcard $(outdir)/.var.*)
+
+at.targets += $(addprefix $(outdir)/,.var.%)
diff --git a/build-aux/Makefile.each.tail/10-files.mk b/build-aux/Makefile.each.tail/10-files.mk
new file mode 100644
index 0000000..3bb3bc2
--- /dev/null
+++ b/build-aux/Makefile.each.tail/10-files.mk
@@ -0,0 +1,57 @@
+# Copyright (C) 2015-2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# Add some more defaults to the *_files variables
+
+$(eval \
+ $(foreach _files.var,$(filter files.src files.src.%,$(.VARIABLES)),\
+ _$(_files.var) = $$(call at.addprefix,$$(srcdir),$$($(_files.var)))$(at.nl))\
+ $(foreach _files.var,$(filter files.out files.out.%,$(.VARIABLES)),\
+ _$(_files.var) = $$(call at.addprefix,$$(outdir),$$($(_files.var)))$(at.nl))\
+ $(foreach _files.var,$(filter files.sys files.sys.%,$(.VARIABLES)),\
+ _$(_files.var) = $$(addprefix $$(DESTDIR),$$($(_files.var)))$(at.nl)))
+
+_files.all = $(_files.src) $(_files.out) $(_files.sys)
+
+at.targets += $(subst *,%,$(_files.all))
+
+# Creative targets
+$(outdir)/$(files.generate): $(_files.src.gen) $(_files.src.cfg)
+$(outdir)/install: $(_files.sys.$(files.default))
+$(outdir)/installdirs: $(sort $(dir $(_files.sys)))
+$(eval \
+ $(foreach _files.g,$(files.groups),\
+ $$(outdir)/$(_files.g): $$(_files.out.$(_files.g))$(at.nl))\
+ $(foreach _files.g,$(filter-out $(files.default),$(files.groups)),\
+ $$(outdir)/install-$(_files.g): $$(_files.sys.$(_files.g))$(at.nl)))
+
+# Destructive targets
+_files.uninstall = $(_files.sys)
+_files.mostlyclean = $(filter-out $(_files.out.slow) $(_files.out.cfg),$(_files.out))
+_files.clean = $(filter-out $(_files.out.cfg),$(_files.out))
+_files.distclean = $(_files.out)
+$(addprefix $(outdir)/,uninstall mostlyclean clean distclean): %: %-hook
+ $(RM) -- $(sort $(filter-out %/,$(_files.$(@F))))
+ $(RM) -r -- $(sort $(filter %/,$(_files.$(@F))))
+ $(RMDIR_P) -- $(sort $(dir $(_files.$(@F))))
+_files.maintainer-clean = $(filter-out $(_files.src.cfg) $(_files.src.src),$(_files.src))
+_files.$(files.vcsclean) = $(filter-out $(_files.src.src),$(_files.src))
+$(addprefix $(outdir)/,maintainer-clean $(files.vcsclean)): $(outdir)/%: $(outdir)/distclean $(outdir)/%-hook
+ @echo 'This command is intended for maintainers to use; it'
+ @echo 'deletes files that may need special tools to rebuild.'
+ $(RM) -- $(sort $(filter-out %/,$(_files.$(@F))))
+ $(RM) -r -- $(sort $(filter %/,$(_files.$(@F))))
+ $(RMDIR_P) -- $(sort $(dir $(_files.$(@F))))
+$(foreach t,uninstall mostlyclean clean distclean maintainer-clean $(files.vcsclean), $(outdir)/$t-hook)::
+.PHONY: $(foreach t,uninstall mostlyclean clean distclean maintainer-clean $(files.vcsclean), $(outdir)/$t-hook)
diff --git a/build-aux/Makefile.each.tail/10-nested.mk b/build-aux/Makefile.each.tail/10-nested.mk
new file mode 100644
index 0000000..5e5a40b
--- /dev/null
+++ b/build-aux/Makefile.each.tail/10-nested.mk
@@ -0,0 +1,20 @@
+# Copyright (C) 2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+$(eval $(foreach _tmp.nested,$(nested.targets),\
+ $$(outdir)/$(_tmp.nested): $$(addsuffix /$(_tmp.nested),$$(call at.addprefix,$$(outdir),$$(nested.subdirs)))$(at.nl)))
+.PHONY: $(addprefix $(outdir)/,$(nested.targets))
+
+at.subdirs += $(nested.subdirs)
diff --git a/build-aux/Makefile.each.tail/10-std.mk b/build-aux/Makefile.each.tail/10-std.mk
deleted file mode 100644
index 693f39d..0000000
--- a/build-aux/Makefile.each.tail/10-std.mk
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (C) 2015-2016 Luke Shumaker
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-# Add some more defaults to the *_files variables
-std.clean_files += $(std.gen_files) $(std.cfg_files) $(std.out_files)
-
-# Fix each variable at its current value to avoid any weirdness
-$(foreach c,src gen cfg out sys clean slow,$(eval std.$c_files := $$(std.$c_files)))
-
-# Make each of the standard variables relative to the correct directory
-std.src_files := $(patsubst ./%,%,$(addprefix $(srcdir)/,$(std.src_files)))
-std.gen_files := $(patsubst ./%,%,$(addprefix $(srcdir)/,$(std.gen_files)))
-std.cfg_files := $(patsubst ./%,%,$(addprefix $(outdir)/,$(std.cfg_files)))
-std.out_files := $(patsubst ./%,%,$(addprefix $(outdir)/,$(std.out_files)))
-std.sys_files := $(addprefix $(DESTDIR),$(std.sys_files))
-std.clean_files := $(patsubst ./%,%,$(addprefix $(outdir)/,$(std.clean_files)))
-std.slow_files := $(patsubst ./%,%,$(addprefix $(outdir)/,$(std.slow_files)))
-
-# Creative targets
-$(outdir)/build : $(std.out_files)
-$(outdir)/install : $(std.sys_files)
-$(outdir)/installdirs: $(sort $(dir $(std.sys_files)))
-
-# Destructive targets
-_std.uninstall/$(outdir) := $(std.sys_files)
-_std.mostlyclean/$(outdir) := $(filter-out $(std.slow_files) $(std.cfg_files) $(std.gen_files) $(std.src_files),$(std.clean_files))
-_std.clean/$(outdir) := $(filter-out $(std.cfg_files) $(std.gen_files) $(std.src_files),$(std.clean_files))
-_std.distclean/$(outdir) := $(filter-out $(std.gen_files) $(std.src_files),$(std.clean_files))
-_std.maintainer-clean/$(outdir) := $(filter-out $(std.src_files),$(std.clean_files))
-$(addprefix $(outdir)/,mostlyclean clean distclean maintainer-clean): %: %-hook
- $(RM) -- $(sort $(filter-out %/,$(_std.$(@F)/$(@D))))
- $(RM) -r -- $(sort $(filter %/,$(_std.$(@F)/$(@D))))
- $(RMDIR_P) $(sort $(dir $(_std.$(@F)/$(@D)))) 2>/dev/null || $(TRUE)
-# separate uninstall to support GNU Coding Standards' NORMAL_UNINSTALL
-$(addprefix $(outdir)/,uninstall): %: %-hook
- $(NORMAL_UNINSTALL)
- $(RM) -- $(sort $(filter-out %/,$(_std.$(@F)/$(@D))))
- $(RM) -r -- $(sort $(filter %/,$(_std.$(@F)/$(@D))))
- $(RMDIR_P) $(sort $(dir $(_std.$(@F)/$(@D)))) 2>/dev/null || $(TRUE)
-$(foreach t,uninstall mostlyclean clean distclean maintainer-clean, $(outdir)/$t-hook)::
-.PHONY: $(foreach t,uninstall mostlyclean clean distclean maintainer-clean, $(outdir)/$t-hook)
diff --git a/build-aux/Makefile.each.tail/11-texinfo.mk b/build-aux/Makefile.each.tail/11-texinfo.mk
new file mode 100644
index 0000000..9491820
--- /dev/null
+++ b/build-aux/Makefile.each.tail/11-texinfo.mk
@@ -0,0 +1,42 @@
+# Copyright (C) 2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+$(outdir)/info : $(addsuffix .info,$(texinfo.docs))
+files.src.gen += $(addsuffix .info,$(texinfo.docs))
+files.out.dvi += $(addsuffix .dvi ,$(texinfo.docs))
+files.out.html += $(addsuffix .html,$(texinfo.docs))
+files.out.pdf += $(addsuffix .pdf ,$(texinfo.docs))
+files.out.ps += $(addsuffix .ps ,$(texinfo.docs))
+
+files.sys.all += $(foreach f,$(texinfo.docs), $(infodir)/$f.info )
+files.sys.dvi += $(foreach f,$(texinfo.docs), $(dvidir)/$f.dvi )
+files.sys.html += $(foreach f,$(texinfo.docs), $(htmldir)/$f.html )
+files.sys.pdf += $(foreach f,$(texinfo.docs), $(pdfdir)/$f.pdf )
+files.sys.ps += $(foreach f,$(texinfo.docs), $(psdir)/$f.ps )
+
+$(outdir)/install:
+ $(POST_INSTALL)
+ $(foreach f,$(texinfo.docs),$(INSTALL_INFO) $(DESTDIR)$(infodir)/$f.info $(DESTDIR)$(infodir)/dir$(at.nl))
+
+$(outdir)/%.info: $(srcdir)/%.texi; $(MAKEINFO) -o $(@D) $<
+$(outdir)/%.info: $(outdir)/%.texi; $(MAKEINFO) -o $(@D) $<
+$(outdir)/%.dvi : $(srcdir)/%.texi; $(TEXI2DVI) -o $(@D) $<
+$(outdir)/%.dvi : $(outdir)/%.texi; $(TEXI2DVI) -o $(@D) $<
+$(outdir)/%.html: $(srcdir)/%.texi; $(TEXI2HTML) -o $(@D) $<
+$(outdir)/%.html: $(outdir)/%.texi; $(TEXI2HTML) -o $(@D) $<
+$(outdir)/%.pdf : $(srcdir)/%.texi; $(TEXI2PDF) -o $(@D) $<
+$(outdir)/%.pdf : $(outdir)/%.texi; $(TEXI2PDF) -o $(@D) $<
+$(outdir)/%.ps : $(srcdir)/%.texi; $(TEXI2PS) -o $(@D) $<
+$(outdir)/%.ps : $(outdir)/%.texi; $(TEXI2PS) -o $(@D) $<
diff --git a/build-aux/Makefile.head.mk b/build-aux/Makefile.head.mk
index 63a3462..f4eb51d 100644
--- a/build-aux/Makefile.head.mk
+++ b/build-aux/Makefile.head.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2016 Luke Shumaker
+# Copyright (C) 2015-2017 Luke Shumaker
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@@ -12,52 +12,105 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+at.Makefile ?= Makefile
+_at.MAKEFILE_LIST ?=
+_at.MAKEFILE_LIST := $(strip $(_at.MAKEFILE_LIST) $(lastword $(filter %/$(at.Makefile),$(abspath $(MAKEFILE_LIST)))))
# This bit only gets evaluated once, at the very beginning
-ifeq ($(_at.NO_ONCE),)
+ifeq ($(origin _at.NO_ONCE),undefined)
+
+# Internal functions ###################################################
+
+# These 4 functions are all $(call _at.func,parent,child)
+_at.is_strict_subdir = $(filter $(abspath $1)/%,$(abspath $2))
+_at.is_subdir = $(filter $(abspath $1)/%,$(abspath $2)/.)
+_at.relbase = $(strip \
+ $(if $(call _at.is_subdir,$1,$2), \
+ $(patsubst %/.,%,$(patsubst $(abspath $1)/%,%,$(abspath $2)/.)), \
+ $(abspath $2)))
+_at.relto = $(strip \
+ $(if $(call _at.is_subdir,$1,$2), \
+ $(patsubst %/.,%,$(patsubst $(abspath $1)/%,%,$(abspath $2)/.)), \
+ ../$(call _at.relto,$(dir $1),$2)))
+
+# These 3 functions only take one operand; we define public multi-operand
+# versions below.
+_at.path = $(strip \
+ $(if $(call _at.is_subdir,$(topoutdir),$1), \
+ $(patsubst %/.,%,$(topoutdir)/$(call _at.relto,.,$1)), \
+ $(if $(call _at.is_subdir,$(topsrcdir),$1), \
+ $(patsubst %/.,%,$(topsrcdir)/$(call _at.relto,$(topsrcdir),$1)), \
+ $(abspath $1))))
+_at.out2src = $(call _at.path,$(if $(call _at.is_subdir,$(topoutdir),$1),$(topsrcdir)/$(call _at.path,$1),$1))
+_at.addprefix = $(call _at.path,$(if $(filter-out /%,$2),$1/$2,$2))
+
+_at.rest = $(wordlist 2,$(words $1),$1)
+_at.reverse = $(if $1,$(call _at.reverse,$(_at.rest))) $(firstword $1)
+
+_at.target_variable = $(_at.target_variable.$(flavor $2))
+_at.target_variable.recursive = $1: private $2 = $(subst $(at.nl),$$(at.nl),$(value $2))
+_at.target_variable.simple = $1: private $2 := $$($2)
+_at.quote-pattern = $(subst %,\%,$(subst \,\\,$1))
+
+# Sanity checking ######################################################
+ifeq ($(filter undefine,$(.FEATURES)),)
+$(error Autothing: We need a version of Make that supports 'undefine')
+endif
ifeq ($(topsrcdir),)
-$(error topsrcdir must be set before including Makefile.head.mk)
+$(error Autothing: topsrcdir must be set (and non-empty) before including Makefile.head.mk)
endif
ifeq ($(topoutdir),)
-$(error topoutdir must be set before including Makefile.head.mk)
+$(error Autothing: topoutdir must be set (and non-empty) before including Makefile.head.mk)
+endif
+ifneq ($(call _at.is_strict_subdir,$(topoutdir),$(topsrcdir)),)
+$(error Autothing: topsrcdir=$(topsrcdir) must not be a subdirectory of topoutdir=$(topoutdir))
endif
-_at.noslash = $(patsubst %/.,%,$(patsubst %/,%,$1))
-# These are all $(call _at.func,parent,child)
-#at.relto = $(if $2,$(shell realpath -sm --relative-to='$1' $2))
-_at.is_subdir = $(filter $(abspath $1)/%,$(abspath $2)/.)
-_at.relto_helper = $(if $(call _at.is_subdir,$1,$2),$(patsubst $1/%,%,$(addsuffix /.,$2)),$(addprefix ../,$(call _at.relto_helper,$(patsubst %/,%,$(dir $1)),$2)))
-_at.relto = $(call _at.noslash,$(call _at.relto_helper,$(call _at.noslash,$(abspath $1)),$(call _at.noslash,$(abspath $2))))
-at.relto = $(foreach p,$2,$(call _at.relto,$1,$p))
-# Note that _at.is_subdir says that a directory is a subdirectory of
-# itself.
-at.path = $(call at.relto,.,$1)
+# External provisions ##################################################
-define at.nl
+# These 4 functions are all $(call _at.func,parent,child)
+at.is_subdir = $(_at.is_subdir)
+at.is_strict_subdir = $(_at.is_strict_subdir)
+#at.relbase = $(if $2,$(shell realpath -sm --relative-base=$1 -- $2))
+at.relbase = $(foreach _at.tmp,$2,$(call _at.relbase,$1,$(_at.tmp)))
+#at.relto = $(if $2,$(shell realpath -sm --relative-to=$1 -- $2))
+at.relto = $(foreach _at.tmp,$2,$(call _at.relto,$1,$(_at.tmp)))
+at.path = $(foreach _at.tmp,$1,$(call _at.path,$(_at.tmp)))
+at.out2src = $(foreach _at.tmp,$1,$(call _at.out2src,$(_at.tmp)))
+at.addprefix = $(foreach _at.tmp,$2,$(call _at.addprefix,$1,$(_at.tmp)))
-endef
+define at.nl
-_at.rest = $(wordlist 2,$(words $1),$1)
-_at.reverse = $(if $1,$(call _at.reverse,$(_at.rest))) $(firstword $1)
-at.dirlocal += at.subdirs
-at.dirlocal += at.depdirs
+endef
+# Include modules ######################################################
include $(sort $(wildcard $(topsrcdir)/build-aux/Makefile.once.head/*.mk))
+_at.tmp_targets =
+_at.tmp_subdirs =
+_at.VARIABLES =
+_at.VARIABLES := $(.VARIABLES)
endif # _at.NO_ONCE
# This bit gets evaluated for each Makefile
-## Set outdir and srcdir (assumes that topoutdir and topsrcdir are
-## already set)
-outdir := $(call at.path,$(dir $(lastword $(filter-out %.mk,$(MAKEFILE_LIST)))))
-srcdir := $(call at.path,$(topsrcdir)/$(call _at.relto,$(topoutdir),$(outdir)))
+outdir := $(call _at.path,$(dir $(lastword $(_at.MAKEFILE_LIST))))
+ifeq ($(call _at.is_subdir,$(topoutdir),$(outdir)),)
+$(error Autothing: not a subdirectory of topoutdir=$(topoutdir): $(outdir))
+endif
-_at.included_makefiles := $(_at.included_makefiles) $(call at.path,$(outdir)/Makefile)
+# Don't use at.out2src because we *know* that $(outdir) is inside $(topoutdir),
+# and has already had $(_at.path) called on it.
+srcdir := $(call _at.path,$(topsrcdir)/$(outdir))
+ifeq ($(call _at.is_subdir,$(topsrcdir),$(srcdir)),)
+$(error Autothing: not a subdirectory of topsrcdir=$(topsrcdir): $(srcdir))
+endif
-$(foreach v,$(at.dirlocal),$(eval $v=))
+at.subdirs =
+at.targets =
include $(sort $(wildcard $(topsrcdir)/build-aux/Makefile.each.head/*.mk))
diff --git a/build-aux/Makefile.once.head/00-gitfiles.mk b/build-aux/Makefile.once.head/00-gitfiles.mk
new file mode 100644
index 0000000..8566a7f
--- /dev/null
+++ b/build-aux/Makefile.once.head/00-gitfiles.mk
@@ -0,0 +1,75 @@
+# Copyright (C) 2016-2017 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+mod.gitfiles.description = Automatically populate files.src.src from git
+mod.gitfiles.depends += files nested write-ifchanged quote
+mod.gitfiles.files += $(topsrcdir)/$(gitfiles.file)
+define mod.gitfiles.doc
+# Inputs:
+# - Global variable : `gitfiles.file` (Default: gitfiles.mk)
+# - Directory variable : `nested.subdirs`
+# - External : git
+# Outputs:
+# - File : `$(topsrcdir)/$(gitfiles.file)`
+# - Directory variable : `files.src.src`
+# - Directory variable : `files.src.gen` (only in top dir)
+#
+# The `files` module has a variable (`files.src.src`) that you (the
+# developer) set to list "pure" source files; the type of files that you
+# would check into a version control system. Since you are a
+# responsible developer, you use a version control system. Since the
+# computer is already maintaining a list of these files *in the VCS*,
+# why should you--a filthy human--need to also maintain the list? Enter
+# gitfiles, which will talk to git to maintain `files.src.src`, but
+# won't require that the git repository be distributed to
+# installing-users.
+#
+# If `$(topsrcdir)/.git` exists, then it will generate
+# `$(topsrcdir)/$(gitfiles.file)`. Otherwise, it will assume that
+# `$(topsrcdir)/$(gitfiles.file)` already exists.
+#
+# It will use the information in `$(topsrcdir)/$(gitfiles.file)` to
+# append to `files.src.src` in each directory
+#
+# Finally, since the generated `$(topsrcdir)/$(gitfiles.file)` must be
+# distributed to users, it is added to $(topsrcdirs)'s `files.src.gen`.
+#
+# When setting `files.src.src`, it needs to know which files "belong" to
+# the current directory directly, and which "belong" to a further
+# subdirectory. To do this, it uses an expression involving
+# `$(nested.subdirs)`.
+#
+# While gitfiles sets `files.src.src` very early in `each.head`, because
+# `nested.subdirs` might not be set yet, it may or may not be safe to
+# use the value of `$(files.src.src)` in your Makefile, depending on how
+# you set `nested.subdirs`.
+endef
+mod.gitfiles.doc := $(value mod.gitfiles.doc)
+
+gitfiles.file ?= gitfiles.mk
+
+_gitfiles.all =
+-include $(topsrcdir)/$(gitfiles.file)
+
+ifneq ($(wildcard $(topsrcdir)/.git),)
+$(topsrcdir)/$(gitfiles.file): _gitfiles.FORCE
+ @(cd $(@D) && git ls-files --recurse-submodules -z) | sed -z -e 's/\$$/\$$$$/g' -e 's/\n/$$(at.nl)/g' | xargs -r0 printf '_gitfiles.all+=%s\n' | $(WRITE_IFCHANGED) $@
+.PHONY: _gitfiles.FORCE
+endif
+
+_gitfiles.dir = $(call at.relto,$(topsrcdir),$(srcdir))
+_gitfiles.pat = $(patsubst ./%,%,$(_gitfiles.dir)/%)
+_gitfiles.dir.all = $(patsubst $(_gitfiles.pat),%,$(filter $(_gitfiles.pat),$(_gitfiles.all)))
+_gitfiles.dir.src = $(filter-out $(addsuffix /%,$(nested.subdirs)),$(_gitfiles.dir.all))
diff --git a/build-aux/Makefile.once.head/00-quote.mk b/build-aux/Makefile.once.head/00-quote.mk
new file mode 100644
index 0000000..94bc943
--- /dev/null
+++ b/build-aux/Makefile.once.head/00-quote.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+mod.quote.description = Macros to quote tricky strings
+define mod.quote.doc
+# Inputs:
+# (none)
+# Outputs:
+# - Global variable: `quote.var` : GNU Make variables
+# - Global variable: `quote.pattern` : GNU Make patterns
+# - Global variable: `quote.ere` : POSIX Extended Regular Expressions
+# - Global variable: `quote.bre` : POSIX Basic Regular Expressions
+# - Global variable: `quote.shell` : POSIX sh(1) strings
+# - Global variable: `quote.shell-each`: POSIX sh(1) strings
+#
+# Escaping/quoting things is hard! This module provides a number of
+# functions to escape/quote strings for various contexts.
+#
+# `quote.shell-each` quotes each list-item separately (munging
+# whitespace), while `quote.shell` keeps them as one string (preserving
+# whitespace).
+endef
+mod.quote.doc := $(value mod.quote.doc)
+
+_quote.backslash = $(if $1,$(call _quote.backslash,$(wordlist 2,$(words $1),$1),$(subst $(firstword $1),\$(firstword $1),$2)),$2)
+
+quote.var = $(subst $(at.nl),\$(at.nl),$(subst $$,$$$$,$1))
+quote.pattern = $(call _quote.backslash, \ % ,$1)
+quote.ere = $(call _quote.backslash, \ ^ . [ $$ ( ) | * + ? { ,$1)
+quote.bre = $(call _quote.backslash, \ ^ . [ $$ * ,$1)
+
+quote.shell-each = $(foreach _quote.tmp,$1,$(call quote.shell,$(_quote.tmp)))
+
+# I put this as the last line in the file because it confuses Emacs
+# syntax highlighting and makes the remainder of the file difficult to
+# edit.
+quote.shell = $(subst $(at.nl),'$$'\n'','$(subst ','\'',$1)')
diff --git a/build-aux/Makefile.once.head/00-var.mk b/build-aux/Makefile.once.head/00-var.mk
new file mode 100644
index 0000000..1f50f21
--- /dev/null
+++ b/build-aux/Makefile.once.head/00-var.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+mod.var.description = Depend on the values of variables
+define mod.var.doc
+# Inputs:
+# (user-defined)
+# Outputs:
+# Target : `$(outdir)/.var.%`
+# Directory variable: `at.targets`
+#
+# It's a well-known secret that many files generated by a Makefile vary with
+# the values of particular variables, but that GNU Make can't track these
+# dependencies. Well, with some cleverness, it actually can!
+#
+# With this module, to depend on the value of a variable, depend on
+# `$(outdir)/.var.VARNAME`.
+endef
+mod.var.doc := $(value mod.var.doc)
+
+.PHONY: _var.FORCE
diff --git a/build-aux/Makefile.once.head/00-write-ifchanged.mk b/build-aux/Makefile.once.head/00-write-ifchanged.mk
deleted file mode 100644
index 79ef1c4..0000000
--- a/build-aux/Makefile.once.head/00-write-ifchanged.mk
+++ /dev/null
@@ -1 +0,0 @@
-WRITE_IFCHANGED = $(topsrcdir)/build-aux/write-ifchanged
diff --git a/build-aux/Makefile.once.head/10-dist.mk b/build-aux/Makefile.once.head/10-dist.mk
new file mode 100644
index 0000000..27b39ec
--- /dev/null
+++ b/build-aux/Makefile.once.head/10-dist.mk
@@ -0,0 +1,79 @@
+# Copyright (C) 2015-2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+mod.dist.description = `dist` target to create distribution tarballs
+define mod.dist.doc
+# User variables:
+# - `CP ?= cp`
+# - `GZIP ?= gzip`
+# - `MKDIR ?= mkdir`
+# - `MKDIR_P ?= mkdir -p`
+# - `MV ?= mv`
+# - `RM ?= rm -f`
+# - `TAR ?= tar`
+# - `GZIPFLAGS ?= $(GZIP_ENV)`
+# - `GZIP_ENV ?= --best` (only used via `GZIPFLAGS`, not directly)
+# Inputs:
+# - Global variable : `dist.exts` (Default: `.tar.gz`)
+# - Global variable : `dist.pkgname` (Default: first of PACKAGE_TARNAME PACKAGE PACKAGE_NAME)
+# - Global variable : `dist.version` (Default: first of PACKAGE_VERSION VERSION)
+# - Directory variable : `files.src`
+# Outputs:
+# - Directory variable : `files.out.int` (only in top dir)
+# - .PHONY Target : `$(outdir)/dist`
+# - Target : `$(topoutdir)/$(dist.pkgname)-$(dist.version)`
+# - Target : `$(topoutdir)/$(dist.pkgname)-$(dist.version).tar`
+# - Target : `$(topoutdir)/$(dist.pkgname)-$(dist.version).tar.gz`
+#
+# Provide the standard `dist` .PHONY target, based on the `files` module
+# information.
+#
+# You may change the default compression target easily via the
+# `dist.exts` variable, but you must define the rule for it manually.
+#
+# Bugs:
+#
+# The tarball isn't reproducible. It uses file-system ordering of
+# files, and includes timestamps.
+endef
+mod.dist.doc := $(value mod.dist.doc)
+
+# Developer configuration
+
+dist.exts ?= .tar.gz
+dist.pkgname ?= $(firstword $(PACKAGE_TARNAME) $(PACKAGE) $(PACKAGE_NAME))
+dist.version ?= $(firstword $(PACKAGE_VERSION) $(VERSION))
+
+ifeq ($(dist.pkgname),)
+$(error Autothing module: dist: dist.pkgname must be set)
+endif
+ifeq ($(dist.version),)
+$(error Autothing module: dist: dist.version must be set)
+endif
+
+_dist.files =
+
+# User configuration
+
+CP ?= cp
+GZIP ?= gzip
+MKDIR ?= mkdir
+MKDIR_P ?= mkdir -p
+MV ?= mv
+RM ?= rm -f
+TAR ?= tar
+
+GZIPFLAGS ?= $(GZIP_ENV)
+GZIP_ENV ?= --best
diff --git a/build-aux/Makefile.once.head/10-files.mk b/build-aux/Makefile.once.head/10-files.mk
new file mode 100644
index 0000000..f6fcf30
--- /dev/null
+++ b/build-aux/Makefile.once.head/10-files.mk
@@ -0,0 +1,87 @@
+# Copyright (C) 2015-2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+mod.files.description = Keeping track of groups of files
+mod.files.depends += nested
+define mod.files.doc
+# User variables:
+# - `DESTDIR ?=`
+# - `RM ?= rm -f`
+# - `RMDIR_P ?= rmdir -p --ignore-fail-on-non-empty`
+# - `TRUE ?= true`
+# Inputs:
+# - Global variable : `files.groups ?= all`
+# - Global variable : `files.default ?= all`
+# - Global variable : `files.vcsclean ?= files.vcsclean`
+# - Global variable : `files.generate ?= files.generate`
+# - Directory variable : `files.src.src`
+# - Directory variable : `files.src.int`
+# - Directory variable : `files.src.cfg`
+# - Directory variable : `files.src.gen`
+# - Directory variable : `files.out.slow`
+# - Directory variable : `files.out.int`
+# - Directory variable : `files.out.cfg`
+# - Directory variable : `files.out.$(files.groups)` (well, $(addprefix...))
+# - Directory variable : `files.sys.$(files.groups)` (well, $(addprefix...))
+# Outputs:
+# - Global variable : `nested.targets`
+# - Global variable : `at.targets`
+# - Global variable : `.DEFAULT_GOAL = $(files.default)`
+# - Directory variable : `files.src`
+# - Directory variable : `files.out`
+# - Directory variable : `files.sys`
+# - Creative .PHONY targets:
+# - `$(outdir)/$(files.generate))`
+# - `$(outdir)/$(group)` for `group` in `$(files.groups)`
+# - `$(outdir)/install`
+# - `$(outdir)/install-$(group)` for `group` in `$(filter-out $(files.default),$(files.groups))`
+# - `$(outdir)/installdirs`
+# - Destructive .PHONY targets:
+# - `$(outdir)/uninstall`
+# - `$(outdir)/mostlyclean`
+# - `$(outdir)/clean`
+# - `$(outdir)/distclean`
+# - `$(outdir)/maintainer-clean`
+# - `$(outdir)/$(files.vcsclean)`
+#
+# Basic `*` wildcards are supported. Use `*`, not `%`; it will automatically
+# substitute `*`->`%` where appropriate.
+#
+# TODO: prose documentation
+endef
+mod.files.doc := $(value mod.files.doc)
+
+files.groups ?= all
+files.default ?= all
+files.vcsclean ?= files.vcsclean
+files.generate ?= files.generate
+
+.DEFAULT_GOAL = $(files.default)
+
+# Standard creative PHONY targets
+nested.targets += $(files.generate)
+nested.targets += install installdirs
+nested.targets += $(foreach g,$(files.groups),$g)
+nested.targets += $(foreach g,$(filter-out $(files.default),$(files.groups)),install-$g install-$gdirs)
+# Standard destructive PHONY targets
+nested.targets += uninstall mostlyclean clean distclean maintainer-clean
+
+# User configuration
+
+DESTDIR ?=
+
+RM ?= rm -f
+RMDIR_P ?= rmdir -p --ignore-fail-on-non-empty
+TRUE ?= true
diff --git a/build-aux/Makefile.once.head/00-gnuconf.mk b/build-aux/Makefile.once.head/10-gnuconf.mk
index 83cb110..e24dfa1 100644
--- a/build-aux/Makefile.once.head/00-gnuconf.mk
+++ b/build-aux/Makefile.once.head/10-gnuconf.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2016 Luke Shumaker
+# Copyright (C) 2016-2017 Luke Shumaker
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@@ -13,12 +13,26 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# This file is based on §7.2 "Makefile Conventions" of the release of
-# the GNU Coding Standards dated April 13, 2016.
+mod.gnuconf.description = GNU standard configuration variables
+define mod.gnuconf.doc
+# Inputs:
+# - Global variable: `gnuconf.pkgname`
+# (Default: `$(firstword $(PACKAGE_TARNAME) $(PACKAGE) $(PACKAGE_NAME))`)
+# Outputs:
+# (see below)
+#
+# This module defines default values (using `?=`) a huge list of
+# variables specified in the GNU Coding Standards that installing-users
+# expect to be able to set.
+#
+# This is based on §7.2 "Makefile Conventions" of the July 25, 2016
+# release of the GNU Coding Standards.
+endef
+mod.gnuconf.doc := $(value mod.gnuconf.doc)
gnuconf.pkgname ?= $(firstword $(PACKAGE_TARNAME) $(PACKAGE) $(PACKAGE_NAME))
ifeq ($(gnuconf.pkgname),)
-$(error gnuconf.pkgname must be set)
+$(error Autothing module: gnuconf: gnuconf.pkgname must be set)
endif
# 7.2.2: Utilities in Makefiles
@@ -54,43 +68,71 @@ TOUCH ?= touch
TR ?= tr
TRUE ?= true
-# These must be user-configurable
+# 7.2.2: Utilities in Makefiles/7.2.3: Variables for Specifying Commands
+# ----------------------------------------------------------------------
+
+# Standard user-configurable programs.
+#
+# The list of programs here is specified in §7.2.2, but the associated FLAGS
+# variables are specified in §7.2.3. I found it cleaner to list them together.
AR ?= ar
ARFLAGS ?=
BISON ?= bison
BISONFLAGS ?=
CC ?= cc
-CCFLAGS ?= $(CFLAGS)
+CFLAGS ?= # CFLAGS instead of CCFLAGS
FLEX ?= flex
FLEXFLAGS ?=
INSTALL ?= install
-#INSTALLFLAGS ?=
+# There is no INSTALLFLAGS[0]
LD ?= ld
LDFLAGS ?=
-LDCONFIG ?= ldconfig #TODO
+LDCONFIG ?= ldconfig # TODO[1]
LDCONFIGFLAGS ?=
LEX ?= lex
-LEXFLAGS ?= $(LFLAGS)
+LFLAGS ?= # LFLAGS instead of LEXFLAGS
#MAKE
MAKEINFO ?= makeinfo
MAKEINFOFLAGS ?=
-RANLIB ?= ranlib #TODO
+RANLIB ?= ranlib # TODO[1]
RANLIBFLAGS ?=
TEXI2DVI ?= texi2dvi
TEXI2DVIFLAGS ?=
YACC ?= yacc
-YACCFLAGS ?= $(YFLAGS)
+YFLAGS ?= # YFLAGS instead of YACCFLAGS
-CFLAGS ?=
-LFLAGS ?=
-YFLAGS ?=
+CPPFLAGS ?=
-LN_S ?= ln -s #TODO
+LN_S ?= ln -s # TODO[2]
CHGRP ?= chgrp
+CHGRPFLAGS ?=
CHMOD ?= chmod
+CHMODFLAGS ?=
CHOWN ?= chown
+CHOWNFLAGS ?=
MKNOD ?= mknod
+MKNODFLAGS ?=
+
+# [0]: There is no INSTALLFLAGS because it would be inconsistent with how the
+# standards otherwise recommend using $(INSTALL); with INSTALL_PROGRAM and
+# INSTALL_DATA; which are specified in a way precluding the use of
+# INSTALLFLAGS. To have the variable, but to ignore it in the common case
+# would be confusing.
+#
+# [1]: The RANLIB and LDCONFIG variables need some extra smarts; §7.2.2 says:
+#
+# > When you use ranlib or ldconfig, you should make sure nothing bad
+# > happens if the system does not have the program in question. Arrange
+# > to ignore an error from that command, and print a message before the
+# > command to tell the user that failure of this command does not mean a
+# > problem. (The Autoconf ‘AC_PROG_RANLIB’ macro can help with this.)
+#
+# [2]: The LN_S variable isn't standard, but we have it here as an (incomplete)
+# stub to help support this bit of §7.2.2:
+#
+# > If you use symbolic links, you should implement a fallback for
+# > systems that don’t have symbolic links.
# 7.2.3: Variables for Specifying Commands
# ----------------------------------------
diff --git a/build-aux/Makefile.once.head/10-nested.mk b/build-aux/Makefile.once.head/10-nested.mk
new file mode 100644
index 0000000..4f181a9
--- /dev/null
+++ b/build-aux/Makefile.once.head/10-nested.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+mod.nested.description = Easy nested .PHONY targets
+define mod.nested.doc
+# Inputs:
+# - Global variable : `nested.targets`
+# - Directory variable : `nested.subdirs`
+# Outputs:
+# - .PHONY Targets : `$(addprefix $(outdir)/,$(nested.targets))`
+# - Variable : `at.subdirs`
+#
+# The Autothing `at.subdirs` slates a subdirectory's Makefile for inclusion,
+# but doesn't help with recursive targets like `all`, `install`, or `clean`,
+# which one would expect to descend into subdirectories. Enter `nested`:
+# Define a global list of targets that are recursive/nested, and then in each
+# directory define a list of subdirectries that one would expect them to
+# recurse into.
+#
+# Directories added to `nested.subdirs` are automatically added to `at.subdirs`
+# during the each.tail phase.
+#
+# It may help to think of at.subdirs and nested.subdirs in terms of their
+# Automake conterparts:
+#
+# | Autothing | GNU Automake |
+# +----------------+--------------+
+# | at.subdirs | DIST_SUBDIRS |
+# | nested.subdirs | SUBDIRS |
+endef
+mod.nested.doc := $(value mod.nested.doc)
+
+nested.targets ?=
diff --git a/build-aux/Makefile.once.head/10-texinfo.mk b/build-aux/Makefile.once.head/10-texinfo.mk
new file mode 100644
index 0000000..aac2c28
--- /dev/null
+++ b/build-aux/Makefile.once.head/10-texinfo.mk
@@ -0,0 +1,51 @@
+# Copyright (C) 2016-2017 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+mod.texinfo.description = The GNU documentation system
+mod.texinfo.depends += files nested gnuconf
+define mod.texinfo.doc
+# User variables (in addition to gnuconf):
+# - `TEXI2HTML ?= makeinfo --html`
+# - `TEXI2PDF ?= texi2pdf`
+# - `TEXI2PS ?= texi2dvi --ps`
+# Inputs:
+# - Directory variable : `texinfo.docs ?=`
+# Outputs:
+# - Global variable : `files.groups += html dvi pdf ps`
+# - Global variable : `nested.targets += info`
+# - Directory variable : `files.src.gen`
+# - Directory variable : `files.out.{dvi,html,pdf,ps}`
+# - Directory variable : `files.sys.{dvi,html,pdf,ps,all}`
+# - .PHONY target : `$(outdir)/info`
+# - .PHONY target : `$(outdir)/install` (see below)
+# - Target : `$(outdir)/%.info`
+# - Target : `$(outdir)/%.dvi`
+# - target : `$(outdir)/%.html`
+# - target : `$(outdir)/%.pdf`
+# - Target : `$(outdir)/%.ps`
+#
+# The module counts on the `$(outdir)/install` target being defined by
+# `files`, but not having a rule that executes once the dependencies
+# have been taken care of; it adds a "post-install" rule to add the
+# info files to the index.
+endef
+mod.texinfo.doc := $(value mod.texinfo.doc)
+
+TEXI2HTML ?= makeinfo --html
+TEXI2PDF ?= texi2pdf
+TEXI2PS ?= texi2dvi --ps
+
+files.groups += html dvi pdf ps
+nested.targets += info
diff --git a/build-aux/Makefile.once.head/10-write-atomic.mk b/build-aux/Makefile.once.head/10-write-atomic.mk
new file mode 100644
index 0000000..c4aa808
--- /dev/null
+++ b/build-aux/Makefile.once.head/10-write-atomic.mk
@@ -0,0 +1,23 @@
+mod.write-atomic.description = `write-atomic` auxiliary build script
+mod.write-atomic.files += $(topsrcdir)/build-aux/write-atomic
+define mod.write-atomic.doc
+# User variables:
+# - `WRITE_ATOMIC ?= $(topsrcdir)/build-aux/write-atomic`
+# Inputs:
+# (none)
+# Outputs:
+# (none)
+#
+# The $(WRITE_ATOMIC) program reads a file from stdin, and writes it to
+# the file named in argv[1], but does so atomically.
+#
+# That is, the following lines are almost equivalient:
+#
+# ... > $@
+# ... | $(WRITE_ATOMIC) $@
+#
+# The are only different in that one is atomic, while the other is not.
+endef
+mod.write-atomic.doc := $(value mod.write-atomic.doc)
+
+WRITE_ATOMIC ?= $(topsrcdir)/build-aux/write-atomic
diff --git a/build-aux/Makefile.once.head/10-write-ifchanged.mk b/build-aux/Makefile.once.head/10-write-ifchanged.mk
new file mode 100644
index 0000000..649aab9
--- /dev/null
+++ b/build-aux/Makefile.once.head/10-write-ifchanged.mk
@@ -0,0 +1,23 @@
+mod.write-ifchanged.description = `write-ifchanged` auxiliary build script
+mod.write-ifchanged.files += $(topsrcdir)/build-aux/write-ifchanged
+define mod.write-ifchanged.doc
+# User variables:
+# - `WRITE_IFCHANGED ?= $(topsrcdir)/build-aux/write-ifchanged`
+# Inputs:
+# (none)
+# Outputs:
+# (none)
+#
+# The $(WRITE_IFCHANGED) program reads a file from stdin, and writes it to the
+# file named in argv[1], but does so atomically, but more importantly, does so
+# in a way that does not bump the file's ctime if the new content is the same
+# as the old content.
+#
+# That is, the following lines are almost equivalient:
+#
+# ... > $@
+# ... | $(WRITE_ATOMIC) $@
+endef
+mod.write-ifchanged.doc := $(value mod.write-ifchanged.doc)
+
+WRITE_IFCHANGED ?= $(topsrcdir)/build-aux/write-ifchanged
diff --git a/build-aux/Makefile.once.head/zz-mod.mk b/build-aux/Makefile.once.head/zz-mod.mk
new file mode 100644
index 0000000..95d251d
--- /dev/null
+++ b/build-aux/Makefile.once.head/zz-mod.mk
@@ -0,0 +1,59 @@
+# Copyright (C) 2016 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+mod.mod.description = Display information about Autothing modules
+mod.mod.depends += quote
+define mod.mod.doc
+# Inputs:
+# - Files : `$(topsrcdir)/build-aux/Makefile.*/??-*.mk`
+# - Global variable : `mod.*.description`
+# - Global variable : `mod.*.depends`
+# - Global variable : `mod.*.files`
+# - Global variable : `mod.*.doc`
+# Outputs:
+# - Directory variable : `at.targets`
+# - .PHONY Target : `$(outdir)/at-variables-local`
+# - .PHONY Target : `$(outdir)/at-variables-global`
+# - .PHONY Target : `$(outdir)/at-variables`
+# - .PHONY Target : `$(outdir)/at-variables/%`
+# - .PHONY Target : `$(outdir)/at-values`
+# - .PHONY Target : `$(outdir)/at-values/%`
+# - .PHONY Target : `$(outdir)/at-modules`
+# - .PHONY Target : `$(outdir)/at-modules/%`
+# - .PHONY Target : `$(outdir)/at-noop`
+#
+# TODO: prose documentation
+endef
+mod.mod.doc := $(value mod.mod.doc)
+
+# The trickery that is _mod.empty/_mod.space is from §6.2 of the GNU Make
+# manual, "The Two Flavors of Variables".
+_mod.empty :=
+_mod.space := $(_mod.empty) #
+undefine _mod.empty
+# _mod.rest is equivalent to GMSL rest.
+_mod.rest = $(wordlist 2,$(words $1),$1)
+
+_mod.file2mod = $(foreach _mod.tmp,$(patsubst %.mk,%,$(notdir $1)),$(subst $(_mod.space),-,$(call _mod.rest,$(subst -, ,$(_mod.tmp)))))
+
+_mod.modules := $(sort $(call _mod.file2mod,$(wildcard $(topsrcdir)/build-aux/Makefile.*/??-*.mk)))
+undefine _mod.rest
+undefine _mod.file2mod
+
+$(eval $(foreach _mod.tmp,$(_mod.modules),\
+ mod.$(_mod.tmp).description ?=$(at.nl)\
+ mod.$(_mod.tmp).depends ?=$(at.nl)\
+ mod.$(_mod.tmp).files ?=$(at.nl)\
+ mod.$(_mod.tmp).doc ?=$(at.nl)))
diff --git a/build-aux/Makefile.once.tail/00-dist.mk b/build-aux/Makefile.once.tail/00-dist.mk
index b8b7733..59a7dc3 100644
--- a/build-aux/Makefile.once.tail/00-dist.mk
+++ b/build-aux/Makefile.once.tail/00-dist.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2016 Luke Shumaker
+# Copyright (C) 2015-2017 Luke Shumaker
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
@@ -13,15 +13,19 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# NB: intentionally resolve symlinks (tack on the -P flag to `cp` to
+# not resolve them). The GNU Coding Standards say to avoid symlinks
+# in tarballs.
_dist.copyfile = $(MKDIR_P) $(dir $2) && $(CP) -T $1 $2
_dist.addfile = $(call _dist.copyfile,$3,$2/$(call at.relto,$1,$3))
-$(topoutdir)/$(dist.pkgname)-$(dist.version): $(foreach v,$(filter std.src_files/% std.gen_files/%,$(.VARIABLES)),$($v))
- $(RM) -r $@ $(@D)/tmp.$(@F)
- $(MKDIR) $(@D)/tmp.$(@F)
- $(foreach f,$^,$(call _dist.addfile,$(topsrcdir),$(@D)/tmp.$(@F),$f)$(at.nl))
- $(MV) $(@D)/tmp.$(@F) $@ || $(RM) -r $(@D)/tmp.$(@F)
+$(topoutdir)/$(dist.pkgname)-$(dist.version): $(_dist.files)
+ $(RM) -r $@ $(@D)/.tmp.$(@F)
+ $(MKDIR) $(@D)/.tmp.$(@F)
+ $(foreach f,$^,$(call _dist.addfile,$(topsrcdir),$(@D)/.tmp.$(@F),$f)$(at.nl))
+ $(MV) $(@D)/.tmp.$(@F) $@ || $(RM) -r $(@D)/.tmp.$(@F)
-$(topoutdir)/$(dist.pkgname)-$(dist.version).tar: $(topoutdir)/$(dist.pkgname)-$(dist.version)
+# TODO: The tar rule isn't reproducible, it should be.
+$(topoutdir)/$(dist.pkgname)-$(dist.version).tar: %.tar: %
$(TAR) cf $@ -C $(<D) $(<F)
-$(topoutdir)/$(dist.pkgname)-$(dist.version).tar.gz: $(topoutdir)/$(dist.pkgname)-$(dist.version).tar
+$(topoutdir)/$(dist.pkgname)-$(dist.version).tar.gz: %.gz: %
$(GZIP) $(GZIPFLAGS) < $< > $@
diff --git a/build-aux/Makefile.tail.mk b/build-aux/Makefile.tail.mk
index dfbad5a..2e4adc6 100644
--- a/build-aux/Makefile.tail.mk
+++ b/build-aux/Makefile.tail.mk
@@ -17,34 +17,23 @@
include $(call _at.reverse,$(sort $(wildcard $(topsrcdir)/build-aux/Makefile.each.tail/*.mk)))
-at.subdirs := $(patsubst ./%,%,$(addprefix $(outdir)/,$(at.subdirs)))
-at.depdirs := $(patsubst ./%,%,$(addprefix $(outdir)/,$(at.depdirs)))
+_at.tmp_targets := $(at.targets)
+_at.tmp_subdirs := $(call at.addprefix,$(outdir),$(at.subdirs))
-# Move all of the dirlocal variables to their namespaced version
-$(foreach v,$(at.dirlocal),$(eval $v/$(outdir) := $$($v)))
-$(foreach v,$(at.dirlocal),$(eval undefine $v))
+# Clean the environment
+$(eval \
+ $(foreach _at.tmp_variable,$(filter-out $(call _at.quote-pattern,_at.tmp_variable $(_at.VARIABLES)),$(.VARIABLES)),\
+ $(call _at.target_variable,$(_at.tmp_targets),$(_at.tmp_variable))$(at.nl)\
+ undefine $(_at.tmp_variable)$(at.nl)))
-# Remember that this is a directory that we've visited
-_at.outdirs := $(_at.outdirs) $(outdir)
-
-# Generic phony target declarations:
-# mark them phony
-.PHONY: $(addprefix $(outdir)/,$(at.phony))
-# have them depend on subdirs
-$(foreach t,$(at.phony),$(eval $(outdir)/$t: $(addsuffix /$t,$(at.subdirs/$(outdir)))))
-
-# Include Makefiles from other directories
+# Recurse
$(foreach _at.NO_ONCE,y,\
- $(foreach makefile,$(call at.path,$(addsuffix /Makefile,$(at.subdirs/$(outdir)) $(at.depdirs/$(outdir)))),\
- $(eval include $(filter-out $(_at.included_makefiles),$(makefile)))))
+ $(foreach _at.tmp,$(call at.path,$(addsuffix /$(at.Makefile),$(_at.tmp_subdirs))),\
+ $(if $(filter-out $(_at.MAKEFILE_LIST),$(abspath $(_at.tmp))),\
+ $(eval include $(_at.tmp)))))
# This bit only gets evaluated once, after all of the other Makefiles are read
-ifeq ($(_at.NO_ONCE),)
-
-outdir = /bogus
-srcdir = /bogus
-
-$(foreach v,$(at.dirlocal),$(eval $v=))
+ifeq ($(origin _at.NO_ONCE),undefined)
include $(call _at.reverse,$(sort $(wildcard $(topsrcdir)/build-aux/Makefile.once.tail/*.mk)))
diff --git a/build-aux/Makefile.once.head/00-dist.mk b/build-aux/write-atomic
index d5bfcd3..ab2a417 100644..100755
--- a/build-aux/Makefile.once.head/00-dist.mk
+++ b/build-aux/write-atomic
@@ -1,3 +1,4 @@
+#!/usr/bin/env bash
# Copyright (C) 2015-2016 Luke Shumaker
#
# This program is free software: you can redistribute it and/or modify
@@ -13,32 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# Developer configuration
+outfile=$1
+tmpfile="$(dirname "$outfile")/.tmp.${outfile##*/}.tmp"
-dist.exts ?= .tar.gz
-dist.pkgname ?= $(PACKAGE)
-dist.version ?= $(VERSION)
-
-ifeq ($(dist.pkgname),)
-$(error dist.pkgname must be set)
-endif
-ifeq ($(dist.version),)
-$(error dist.version must be set)
-endif
-
-# User configuration
-
-CP ?= cp
-GZIP ?= gzip
-MKDIR ?= mkdir
-MKDIR_P ?= mkdir -p
-MV ?= mv
-RM ?= rm -f
-TAR ?= tar
-
-GZIPFLAGS ?= $(GZIP_ENV)
-GZIP_ENV ?= --best
-
-# Implementation
-
-at.phony += dist
+cat > "$tmpfile" || { r=$?; rm -f "$tmpfile"; exit $r; }
+mv -f "$tmpfile" "$outfile"
diff --git a/build-aux/write-ifchanged b/build-aux/write-ifchanged
index 185ceb0..84dfd6e 100755
--- a/build-aux/write-ifchanged
+++ b/build-aux/write-ifchanged
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
outfile=$1
-tmpfile="$(dirname "$outfile")/.tmp${outfile##*/}"
+tmpfile="$(dirname "$outfile")/.tmp.${outfile##*/}.tmp"
cat > "$tmpfile" || exit $?
if cmp -s "$tmpfile" "$outfile"; then
diff --git a/config.mk.in b/config.mk.in
new file mode 100644
index 0000000..be95ab4
--- /dev/null
+++ b/config.mk.in
@@ -0,0 +1,198 @@
+# Copyright (C) 2016-2017 Luke Shumaker
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This is based on §7.2 "Makefile Conventions" of the July 25, 2016
+# release of the GNU Coding Standards.
+#
+# Grep for '^##' in this file to see which Autoconf macros it depends
+# on.
+
+dist.pkgname = @PACKAGE_TARNAME@
+gnuconf.pkgname = @PACKAGE_NAME@
+
+# 7.2.2: Utilities in Makefiles
+# -----------------------------
+
+# It's ok to hard-code these commands in rules, but who wants to
+# memorize the list of what's ok?
+
+## AC_PROG_AWK
+## AC_PROG_GREP
+## AC_PROG_EGREP
+## AC_PROG_SED
+
+AWK = @AWK@
+CAT = cat
+CMP = cmp
+CP = cp
+DIFF = diff
+ECHO = echo
+EGREP = @EGREP@
+EXPR = expr
+FALSE = false
+GREP = @GREP@
+INSTALL_INFO = install-info
+LN = ln
+LS = ls
+MKDIR = mkdir
+MV = mv
+PRINTF = printf
+PWD = pwd
+RM = rm
+RMDIR = rmdir
+SED = @SED@
+SLEEP = sleep
+SORT = sort
+TAR = tar
+TEST = test
+TOUCH = touch
+TR = tr
+TRUE = true
+
+# 7.2.2: Utilities in Makefiles/7.2.3: Variables for Specifying Commands
+# ----------------------------------------------------------------------
+
+# Standard user-configurable programs.
+#
+# The list of programs here is specified in §7.2.2, but the associated FLAGS
+# variables are specified in §7.2.3. I found it cleaner to list them together.
+
+## AC_PROG_INSTALL # @INSTALL@ @INSTALL_PROGRAM@ @INSTALL_SCRIPT@ @INSTALL_DATA@
+## AC_PROG_LEX # @LEX@ @LEXLIB@
+## AC_PROG_RANLIB
+## AC_PROG_YACC
+## AC_PROG_CC
+#
+# TODO: What causes Autoconf to define @AR@?
+
+AR = @AR@
+ARFLAGS =
+BISON = bison
+BISONFLAGS =
+CC = @CC@
+CFLAGS = @CFLAGS@ # CFLAGS instead of CCFLAGS
+FLEX = flex
+FLEXFLAGS =
+INSTALL = @INSTALL@
+# There is no INSTALLFLAGS[0]
+LD = ld
+LDFLAGS = @LDFLAGS@
+LDCONFIG = ldconfig # TODO[1]
+LDCONFIGFLAGS =
+LEX = @LEX@
+LFLAGS = #LFLAGS instead of LEXFLAGS
+#MAKE
+MAKEINFO = makeinfo
+MAKEINFOFLAGS =
+RANLIB = @RANLIB@
+RANLIBFLAGS =
+TEXI2DVI = texi2dvi
+TEXI2DVIFLAGS =
+YACC = @YACC@
+YFLAGS = # YFLAGS instead of YACCFLAGS
+
+CPPFLAGS = @CPPFLAGS@
+
+LN_S = @LN_S@
+
+CHGRP = chgrp
+CHGRPFLAGS =
+CHMOD = chmod
+CHMODFLAGS =
+CHOWN = chown
+CHOWNFLAGS =
+MKNOD = mknod
+MKNODFLAGS =
+
+# [0]: There is no INSTALLFLAGS because it would be inconsistent with how the
+# standards otherwise recommend using $(INSTALL); with INSTALL_PROGRAM and
+# INSTALL_DATA; which are specified in a way precluding the use of
+# INSTALLFLAGS. To have the variable, but to ignore it in the common case
+# would be confusing.
+#
+# [1]: The RANLIB and LDCONFIG variables need some extra smarts; §7.2.2 says:
+#
+# > When you use ranlib or ldconfig, you should make sure nothing bad
+# > happens if the system does not have the program in question. Arrange
+# > to ignore an error from that command, and print a message before the
+# > command to tell the user that failure of this command does not mean a
+# > problem. (The Autoconf ‘AC_PROG_RANLIB’ macro can help with this.)
+
+# 7.2.3: Variables for Specifying Commands
+# ----------------------------------------
+
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_DATA = @INSTALL_DATA@
+
+# 7.2.5: Variables for Installation Directories
+# ---------------------------------------------
+
+# Root for the installation
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+# Executable programs
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+# Data files (Autoconf won't support runstatedir until version 2.70)
+datarootdir = @datarootdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+runstatedir = $(localstatedir)/run
+# Specific types of files
+includedir = @includedir@
+oldincludedir = @oldincludedir@
+docdir = @docdir@
+infodir = @infodir@
+htmldir = @htmldir@
+dvidir = @dvidir@
+pdfdir = @pdfdir@
+psdir = @psdir@
+libdir = @libdir@
+lispdir = $(datarootdir)/emacs/site-lisp
+localedir = @localedir@
+
+mandir = @mandir@
+man1dir = $(mandir)/man1
+man2dir = $(mandir)/man2
+man3dir = $(mandir)/man3
+man4dir = $(mandir)/man4
+man5dir = $(mandir)/man5
+man6dir = $(mandir)/man6
+man7dir = $(mandir)/man7
+man8dir = $(mandir)/man8
+
+manext = .1
+man1ext = .1
+man2ext = .2
+man3ext = .3
+man4ext = .4
+man5ext = .5
+man6ext = .6
+man7ext = .7
+man8ext = .8
+
+# 7.2.7: Install Command Categories
+# ---------------------------------
+
+PRE_INSTALL =
+POST_INSTALL =
+NORMAL_INSTALL =
+
+PRE_UNINSTALL =
+POST_UNINSTALL =
+NORMAL_UNINSTALL =