summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-05-17 18:34:43 -0400
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-05-17 18:34:43 -0400
commit733636464ea990d96d38edefff9ca326e8195cf4 (patch)
tree127eb63979415d3d98659132efd9970ba11a3cdb
parente7dd6b39d699d9326874cd5f99dcaa6929c4d6d7 (diff)
parent37ef1295bf8885876df8de989569a2233f97583f (diff)
Merge remote-tracking branch 'automake/master'
-rw-r--r--automake.head.mk56
-rw-r--r--automake.tail.mk139
-rw-r--r--automake.txt164
3 files changed, 359 insertions, 0 deletions
diff --git a/automake.head.mk b/automake.head.mk
new file mode 100644
index 0000000..37d3b07
--- /dev/null
+++ b/automake.head.mk
@@ -0,0 +1,56 @@
+# 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/>.
+
+_am = am_
+
+_am_noslash = $(patsubst %/.,%,$(patsubst %/,%,$1))
+# These are all $(call _am_func,parent,child)
+#_am_relto = $(if $2,$(shell realpath -sm --relative-to='$1' $2))
+_am_is_subdir = $(filter $(abspath $1)/%,$(abspath $2)/.)
+_am_relto_helper = $(if $(call _am_is_subdir,$1,$2),$(patsubst $1/%,%,$(addsuffix /.,$2)),$(addprefix ../,$(call _am_relto_helper,$(patsubst %/,%,$(dir $1)),$2)))
+_am_relto = $(call _am_noslash,$(call _am_relto_helper,$(call _am_noslash,$(abspath $1)),$(call _am_noslash,$(abspath $2))))
+# Note that _am_is_subdir says that a directory is a subdirectory of
+# itself.
+_am_path = $(call _am_relto,.,$1)
+am_path = $(foreach p,$1,$(call _am_relto,$p))
+
+## Declare the default target
+all: build
+.PHONY: all
+
+## Set outdir and srcdir (assumes that topoutdir and topsrcdir are
+## already set)
+outdir := $(call _am_path,$(dir $(lastword $(filter-out %.mk,$(MAKEFILE_LIST)))))
+srcdir := $(call _am_path,$(topsrcdir)/$(call _am_relto,$(topoutdir),$(outdir)))
+
+_am_included_makefiles := $(_am_included_makefiles) $(call _am_path,$(outdir)/Makefile)
+
+## Empty variables for use by each Makefile
+$(_am)subdirs =
+$(_am)depdirs =
+
+$(_am)src_files =
+$(_am)gen_files =
+$(_am)cfg_files =
+$(_am)out_files =
+$(_am)sys_files =
+
+$(_am)clean_files =
+$(_am)slow_files =
+
+ifeq ($(_am_NO_ONCE),)
+include $(topsrcdir)/common.once.head.mk
+endif
+include $(topsrcdir)/common.each.head.mk
diff --git a/automake.tail.mk b/automake.tail.mk
new file mode 100644
index 0000000..a24820b
--- /dev/null
+++ b/automake.tail.mk
@@ -0,0 +1,139 @@
+# 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/>.
+
+include $(topsrcdir)/common.each.tail.mk
+
+
+# Aggregate variables
+
+# Add some more defaults to the *_files variables
+$(_am)clean_files += $($(_am)gen_files) $($(_am)cfg_files) $($(_am)out_files)
+
+# Now namespace the *_files variables
+define _am_save_variables
+_am_src_files/$(outdir) = $(addprefix $(srcdir)/,$($(_am)src_files))
+_am_gen_files/$(outdir) = $(addprefix $(srcdir)/,$($(_am)gen_files))
+_am_cfg_files/$(outdir) = $(addprefix $(outdir)/,$($(_am)cfg_files))
+_am_out_files/$(outdir) = $(addprefix $(outdir)/,$($(_am)out_files))
+_am_sys_files/$(outdir) = $(addprefix $(DESTDIR),$($(_am)sys_files))
+_am_clean_files/$(outdir) = $(addprefix $(outdir)/,$($(_am)clean_files))
+_am_slow_files/$(outdir) = $(addprefix $(outdir)/,$($(_am)slow_files))
+_am_subdirs/$(outdir) = $($(_am)subdirs)
+endef
+$(eval $(_am_save_variables))
+
+# And add them to the $(parent)_*_files variables (if applicable)
+define _am_add_subdir
+_am_src_files/%(outdir) += $(_am_src_files/%(subdir))
+_am_gen_files/%(outdir) += $(_am_gen_files/%(subdir))
+_am_cfg_files/%(outdir) += $(_am_cfg_files/%(subdir))
+_am_out_files/%(outdir) += $(_am_out_files/%(subdir))
+_am_sys_files/%(outdir) += $(_am_sys_files/%(subdir))
+_am_clean_files/%(outdir) += $(_am_clean_files/%(subdir))
+_am_slow_files/%(outdir) += $(_am_slow_files/%(subdir))
+endef
+$(foreach subdir,$(call _am_path,$(addprefix $(outdir)/,$($(_am)subdirs))),$(eval $(subst %(outdir),$(outdir),$(subst %(subdir),$(subdir),$(value _am_add_subdir)))))
+
+_am_outdirs := $(_am_outdirs) $(outdir)
+
+
+# Do some per-directory magic
+
+_am_phony = build install uninstall mostlyclean clean distclean maintainer-clean check
+
+.PHONY: $(addprefix $(outdir)/,$(_am_phony))
+
+$(addprefix $(outdir)/,uninstall mostlyclean clean distclean maintainer-clean)::
+ $(RM) -- $(sort $(_am_$(@F)/$(@D)))
+ $(RMDIRS) $(sort $(dir $(_am_$(@F)/$(@D)))) 2>/dev/null || $(TRUE)
+
+# 'build' and 'install' must be defined later, because the
+# am_*_files/* variables might not be complete yet.
+
+
+# Include Makefiles from other directories
+
+define _am_nl
+
+
+endef
+
+$(foreach _am_NO_ONCE,y,\
+ $(foreach makefile,$(foreach dir,$($(_am)subdirs) $($(_am)depdirs),$(call _am_path,$(outdir)/$(dir)/Makefile)),\
+ $(eval include $(filter-out $(_am_included_makefiles),$(makefile)))))
+
+
+# This only gets evaluated once, after all of the other Makefiles are read
+ifeq ($(_am_NO_ONCE),)
+# Empty directory-level variables
+outdir = /bogus
+srcdir = /bogus
+
+$(_am)subdirs =
+$(_am)depdirs =
+
+$(_am)src_files =
+$(_am)gen_files =
+$(_am)cfg_files =
+$(_am)out_files =
+$(_am)sys_files =
+$(_am)clean_files =
+$(_am)slow_files =
+
+_am_clean_files/$(topoutdir) += $(topoutdir)/$(PACKAGE)-$(VERSION).tar.gz
+$(addprefix $(topoutdir)/,mostlyclean clean distclean maintainer-clean) ::
+ $(RM) -r -- $(topoutdir)/$(PACKAGE)-$(VERSION)
+
+define _am_directory_rules
+# Constructive phony targets
+$(outdir)/build : $(_am_out_files/%(outdir))
+$(outdir)/install: $(_am_sys_files/%(outdir))
+# Destructive phony targets
+_am_uninstall/%(outdir) = $(_am_sys_files/%(outdir))
+_am_mostlyclean/%(outdir) = $(filter-out $(_am_slow_files/%(outdir)) $(_am_cfg_files/%(outdir)) $(_am_gen_files/%(outdir)) $(_am_src_files/%(outdir)),$(_am_clean_files/%(outdir)))
+_am_clean/%(outdir) = $(filter-out $(_am_cfg_files/%(outdir)) $(_am_gen_files/%(outdir)) $(_am_src_files/%(outdir)),$(_am_clean_files/%(outdir)))
+_am_distclean/%(outdir) = $(filter-out $(_am_gen_files/%(outdir)) $(_am_src_files/%(outdir)),$(_am_clean_files/%(outdir)))
+_am_maintainer-clean/%(outdir) = $(filter-out $(_am_src_files/%(outdir)),$(_am_clean_files/%(outdir)))
+endef
+$(foreach outdir,$(_am_outdirs),$(eval $(subst %(outdir),$(outdir),$(value _am_directory_rules))))
+
+# Add the `dist` target
+.PHONY: dist
+dist: $(topoutdir)/$(PACKAGE)-$(VERSION).tar.gz
+$(topoutdir)/$(PACKAGE)-$(VERSION).tar.gz: $(topoutdir)/$(PACKAGE)-$(VERSION)
+ $(TAR) czf $@ -C $(<D) $(<F)
+_am_copyfile = $(MKDIRS) $(dir $2) && $(CP) -T $1 $2
+_am_addfile = $(call _am_copyfile,$3,$2/$(call _am_relto,$1,$3))
+$(topoutdir)/$(PACKAGE)-$(VERSION): $(_am_src_files/$(topoutdir)) $(_am_gen_files/$(topoutdir))
+ $(RM) -r $@
+ @PS4='' && set -x && \
+ $(MKDIR) $(@D)/tmp.$(@F).$$$$ && \
+ $(foreach f,$^,$(call _am_addfile,$(topsrcdir),$(@D)/tmp.$(@F).$$$$,$f) &&) \
+ $(MV) $(@D)/tmp.$(@F).$$$$ $@ || $(RM) -r $(@D)/tmp.$(@F).$$$$
+
+include $(topsrcdir)/common.once.tail.mk
+
+# For some reason I can't explain, RM doesn't really get set with ?=
+CP ?= cp
+MKDIR ?= mkdir
+MKDIRS ?= mkdir -p
+MV ?= mv
+RM = rm -f
+RMDIRS ?= rmdir -p
+TAR ?= tar
+TRUE ?= true
+
+.PHONY: noop
+endif
diff --git a/automake.txt b/automake.txt
new file mode 100644
index 0000000..935af5f
--- /dev/null
+++ b/automake.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)/automake.head.mk
+
+ # your makefile
+
+ include $(topsrcdir)/automake.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.