summaryrefslogtreecommitdiff
path: root/build-aux
diff options
context:
space:
mode:
Diffstat (limited to 'build-aux')
-rw-r--r--build-aux/Makefile.README.txt164
-rw-r--r--build-aux/Makefile.each.head/.gitignore0
-rw-r--r--build-aux/Makefile.each.tail/.gitignore0
-rw-r--r--build-aux/Makefile.each.tail/09-generate.mk1
-rw-r--r--build-aux/Makefile.head.mk63
-rw-r--r--build-aux/Makefile.once.head/.gitignore0
-rw-r--r--build-aux/Makefile.once.head/00-gnuconf.mk160
-rw-r--r--build-aux/Makefile.once.head/00-write-ifchanged.mk1
-rw-r--r--build-aux/Makefile.once.head/09-generate.mk1
-rw-r--r--build-aux/Makefile.once.head/20-golang.mk29
-rw-r--r--build-aux/Makefile.once.head/20-var.mk20
-rw-r--r--build-aux/Makefile.once.tail/.gitignore0
-rw-r--r--build-aux/Makefile.tail.mk51
-rwxr-xr-xbuild-aux/write-ifchanged25
14 files changed, 515 insertions, 0 deletions
diff --git a/build-aux/Makefile.README.txt b/build-aux/Makefile.README.txt
new file mode 100644
index 0000000..e06ba52
--- /dev/null
+++ b/build-aux/Makefile.README.txt
@@ -0,0 +1,164 @@
+Luke's AutoMake
+===============
+
+Yo, this document is incomplete. It describes the magical
+automake.{head,tail}.mk Makefiles and how to use them, kinda.
+
+I wrote a "clone" of automake. I say clone, because it works
+differently. Yeah, I need a new name for it.
+
+High-level overview
+-------------------
+
+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. |
+
+Tips, notes
+-----------
+
+I like to have the first (non-comment) line in a Makefile be:
+
+ include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk
+
+(adjusting the number of `../` sequences as nescessary). Then, my
+(user-editable) `config.mk` is of the form:
+
+ ifeq ($(topsrcdir),)
+ topoutdir := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST))))
+ topsrcdir := $(topoutdir)
+
+ # your configuration
+
+ endif
+
+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`.
+
+----
+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.each.head/.gitignore b/build-aux/Makefile.each.head/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build-aux/Makefile.each.head/.gitignore
diff --git a/build-aux/Makefile.each.tail/.gitignore b/build-aux/Makefile.each.tail/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build-aux/Makefile.each.tail/.gitignore
diff --git a/build-aux/Makefile.each.tail/09-generate.mk b/build-aux/Makefile.each.tail/09-generate.mk
new file mode 100644
index 0000000..3a8566f
--- /dev/null
+++ b/build-aux/Makefile.each.tail/09-generate.mk
@@ -0,0 +1 @@
+$(outdir)/generate: $(std.gen_files)
diff --git a/build-aux/Makefile.head.mk b/build-aux/Makefile.head.mk
new file mode 100644
index 0000000..63a3462
--- /dev/null
+++ b/build-aux/Makefile.head.mk
@@ -0,0 +1,63 @@
+# 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/>.
+
+# This bit only gets evaluated once, at the very beginning
+ifeq ($(_at.NO_ONCE),)
+
+ifeq ($(topsrcdir),)
+$(error topsrcdir must be set before including Makefile.head.mk)
+endif
+ifeq ($(topoutdir),)
+$(error topoutdir must be set before including Makefile.head.mk)
+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)
+
+define at.nl
+
+
+endef
+
+_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
+
+include $(sort $(wildcard $(topsrcdir)/build-aux/Makefile.once.head/*.mk))
+
+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)))
+
+_at.included_makefiles := $(_at.included_makefiles) $(call at.path,$(outdir)/Makefile)
+
+$(foreach v,$(at.dirlocal),$(eval $v=))
+
+include $(sort $(wildcard $(topsrcdir)/build-aux/Makefile.each.head/*.mk))
diff --git a/build-aux/Makefile.once.head/.gitignore b/build-aux/Makefile.once.head/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build-aux/Makefile.once.head/.gitignore
diff --git a/build-aux/Makefile.once.head/00-gnuconf.mk b/build-aux/Makefile.once.head/00-gnuconf.mk
new file mode 100644
index 0000000..83cb110
--- /dev/null
+++ b/build-aux/Makefile.once.head/00-gnuconf.mk
@@ -0,0 +1,160 @@
+# 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/>.
+
+# This file is based on ยง7.2 "Makefile Conventions" of the release of
+# the GNU Coding Standards dated April 13, 2016.
+
+gnuconf.pkgname ?= $(firstword $(PACKAGE_TARNAME) $(PACKAGE) $(PACKAGE_NAME))
+ifeq ($(gnuconf.pkgname),)
+$(error gnuconf.pkgname must be set)
+endif
+
+# 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?
+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
+
+# These must be user-configurable
+AR ?= ar
+ARFLAGS ?=
+BISON ?= bison
+BISONFLAGS ?=
+CC ?= cc
+CCFLAGS ?= $(CFLAGS)
+FLEX ?= flex
+FLEXFLAGS ?=
+INSTALL ?= install
+#INSTALLFLAGS ?=
+LD ?= ld
+LDFLAGS ?=
+LDCONFIG ?= ldconfig #TODO
+LDCONFIGFLAGS ?=
+LEX ?= lex
+LEXFLAGS ?= $(LFLAGS)
+#MAKE
+MAKEINFO ?= makeinfo
+MAKEINFOFLAGS ?=
+RANLIB ?= ranlib #TODO
+RANLIBFLAGS ?=
+TEXI2DVI ?= texi2dvi
+TEXI2DVIFLAGS ?=
+YACC ?= yacc
+YACCFLAGS ?= $(YFLAGS)
+
+CFLAGS ?=
+LFLAGS ?=
+YFLAGS ?=
+
+LN_S ?= ln -s #TODO
+
+CHGRP ?= chgrp
+CHMOD ?= chmod
+CHOWN ?= chown
+MKNOD ?= mknod
+
+# 7.2.3: Variables for Specifying Commands
+# ----------------------------------------
+
+INSTALL_PROGRAM ?= $(INSTALL)
+INSTALL_DATA ?= ${INSTALL} -m 644
+
+# 7.2.5: Variables for Installation Directories
+# ---------------------------------------------
+
+# Root for the installation
+prefix ?= /usr/local
+exec_prefix ?= $(prefix)
+# Executable programs
+bindir ?= $(exec_prefix)/bin
+sbindir ?= $(exec_prefix)/sbin
+libexecdir ?= $(exec_prefix)/libexec
+# Data files
+datarootdir ?= $(prefix)/share
+datadir ?= $(datarootdir)
+sysconfdir ?= $(prefix)/etc
+sharedstatedir ?= $(prefix)/com
+localstatedir ?= $(prefix)/var
+runstatedir ?= $(localstatedir)/run
+# Specific types of files
+includedir ?= $(prefix)/include
+oldincludedir ?= /usr/include
+docdir ?= $(datarootdir)/doc/$(gnuconf.pkgname)
+infodir ?= $(datarootdir)/info
+htmldir ?= $(docdir)
+dvidir ?= $(docdir)
+pdfdir ?= $(docdir)
+psdir ?= $(docdir)
+libdir ?= $(exec_prefix)/lib
+lispdir ?= $(datarootdir)/emacs/site-lisp
+localedir ?= $(datarootdir)/locale
+
+mandir ?= $(datarootdir)/man
+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 ?=
diff --git a/build-aux/Makefile.once.head/00-write-ifchanged.mk b/build-aux/Makefile.once.head/00-write-ifchanged.mk
new file mode 100644
index 0000000..79ef1c4
--- /dev/null
+++ b/build-aux/Makefile.once.head/00-write-ifchanged.mk
@@ -0,0 +1 @@
+WRITE_IFCHANGED = $(topsrcdir)/build-aux/write-ifchanged
diff --git a/build-aux/Makefile.once.head/09-generate.mk b/build-aux/Makefile.once.head/09-generate.mk
new file mode 100644
index 0000000..b07bb3f
--- /dev/null
+++ b/build-aux/Makefile.once.head/09-generate.mk
@@ -0,0 +1 @@
+at.phony += generate
diff --git a/build-aux/Makefile.once.head/20-golang.mk b/build-aux/Makefile.once.head/20-golang.mk
new file mode 100644
index 0000000..8d10a47
--- /dev/null
+++ b/build-aux/Makefile.once.head/20-golang.mk
@@ -0,0 +1,29 @@
+# Copyright 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/>.
+
+golang.Q ?= @
+golang.FLAGS ?=
+
+_golang.cgo_variables = CGO_ENABLED CGO_CFLAGS CGO_CPPFLAGS CGO_CXXFLAGS CGO_LDFLAGS CC CXX
+$(foreach v,$(_golang.cgo_variables),$(eval $v ?=))
+export $(_golang.cgo_variables)
+
+_golang.src_cmd = find -L $1/src -name '.*' -prune -o \( -type f \( -false $(foreach e,go c s S cc cpp cxx h hh hpp hxx,-o -name '*.$e') \) -o -type d \) -print
+golang.src = $(shell $(_golang.src_cmd)) $(addprefix $(var),$(_golang.cgo_variables))
+
+define golang.install
+ GOPATH='$(abspath $1)' go install $(golang.FLAGS) $2
+ $(golang.Q)true $(foreach e,$(notdir $2), && test -f $1/bin/$e -a -x $1/bin/$e && touch $1/bin/$e)
+endef
diff --git a/build-aux/Makefile.once.head/20-var.mk b/build-aux/Makefile.once.head/20-var.mk
new file mode 100644
index 0000000..b1de987
--- /dev/null
+++ b/build-aux/Makefile.once.head/20-var.mk
@@ -0,0 +1,20 @@
+# Copyright 2015-2016 Luke Shumaker <lukeshu@sbcglobal.net>.
+#
+# 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/>.
+
+var = $(patsubst ./%,%,$(topoutdir)/.var.)
+
+$(var)%: FORCE
+ @printf '%s' '$(subst ','\'',$($*))' | sed 's|^|#|' | $(WRITE_IFCHANGED) $@
+-include $(wildcard $(var)*)
diff --git a/build-aux/Makefile.once.tail/.gitignore b/build-aux/Makefile.once.tail/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build-aux/Makefile.once.tail/.gitignore
diff --git a/build-aux/Makefile.tail.mk b/build-aux/Makefile.tail.mk
new file mode 100644
index 0000000..dfbad5a
--- /dev/null
+++ b/build-aux/Makefile.tail.mk
@@ -0,0 +1,51 @@
+# 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/>.
+
+# This bit gets evaluated for each Makefile processed
+
+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)))
+
+# 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))
+
+# 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
+$(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)))))
+
+# 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=))
+
+include $(call _at.reverse,$(sort $(wildcard $(topsrcdir)/build-aux/Makefile.once.tail/*.mk)))
+
+endif # _at.NO_ONCE
diff --git a/build-aux/write-ifchanged b/build-aux/write-ifchanged
new file mode 100755
index 0000000..185ceb0
--- /dev/null
+++ b/build-aux/write-ifchanged
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+# Copyright (C) 2015 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/>.
+
+outfile=$1
+tmpfile="$(dirname "$outfile")/.tmp${outfile##*/}"
+
+cat > "$tmpfile" || exit $?
+if cmp -s "$tmpfile" "$outfile"; then
+ rm -f "$tmpfile" || :
+else
+ mv -f "$tmpfile" "$outfile"
+fi