From bce7ec06c4234cb5713bb20350b3aff9010e3703 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 8 Feb 2016 16:36:45 -0500 Subject: (libretools) Refactor the build system. Avoid recursive make. This looks like a lot, but more things should "just work". We have `make dist` now! --- automake.head.mk | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 automake.head.mk (limited to 'automake.head.mk') diff --git a/automake.head.mk b/automake.head.mk new file mode 100644 index 0000000000..a3c90fd21f --- /dev/null +++ b/automake.head.mk @@ -0,0 +1,58 @@ +# 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 . + +_am = am_ + +_am_noslash = $(patsubst %/.,%,$(patsubst %/,%,$1)) +# These are all $(call _am_func,parent,child) +#_am_relto = $(if $2,$(shell realpath -s --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) + +## Declare the default target +all: build +.PHONY: all + +## Set topoutdir, outdir, and srcdir (assumes that topsrcdir is +## already set, and that $(topoutdir)/config.mk has been included) +ifeq ($(topoutdir),) +topoutdir := $(call _am_path,$(dir $(lastword $(filter %/config.mk config.mk,$(MAKEFILE_LIST))))) +endif + 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 the module +$(_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 -- cgit v1.2.3-54-g00ecf From a2718bfbd5dee3b5a69fcdb4361748ef84cbd06c Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Tue, 1 Mar 2016 19:15:29 -0500 Subject: (libretools) improve HACKING documentation --- automake.head.mk | 2 +- automake.txt | 131 +++++++++++++++++++++++++++++++------------------------ 2 files changed, 75 insertions(+), 58 deletions(-) (limited to 'automake.head.mk') diff --git a/automake.head.mk b/automake.head.mk index a3c90fd21f..ad7154c769 100644 --- a/automake.head.mk +++ b/automake.head.mk @@ -39,7 +39,7 @@ endif _am_included_makefiles := $(_am_included_makefiles) $(call _am_path,$(outdir)/Makefile) -## Empty variables for use by the module +## Empty variables for use by each Makefile $(_am)subdirs = $(_am)depdirs = diff --git a/automake.txt b/automake.txt index 307b321f6d..22a0b84f63 100644 --- a/automake.txt +++ b/automake.txt @@ -7,14 +7,13 @@ 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. -Anyway, how to use it: +High-level overview +------------------- In each source directory, you write a `Makefile`, very similarly to if you were writing for plain GNU Make, with -_am_phony = build install uninstall mostlyclean clean distclean maintainer-clean check - - + # adjust the number of `../` segments as appropriate include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk include $(topsrcdir)/automake.head.mk @@ -22,63 +21,81 @@ _am_phony = build install uninstall mostlyclean clean distclean maintainer-clean include $(topsrcdir)/automake.tail.mk -Write your own `common.each.mk` that gets included after the body of -your Makefile for each Makefile. - -Write your own `common.once.mk` that gets included once after -everything else has been parsed. - -There are several commands that generate files; simply record what -they the list of files in their output to the following variables: - -| Variable | Command | Description | Relative to | -|-----------+--------------+-----------------------------------+-------------| -| src_files | emacs | Files that the developer writes | srcdir | -| gen_files | ??? | Files the developer compiles | srcdir | -| cfg_files | ./configure | Users' compile-time configuration | outdir | -| out_files | make all | Files the user compiles | outdir | -| sys_files | make install | Files the user installs | DESTDIR | - -In addition, there are - - subdirs : 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. - - clean_files : A list of things to `rm` in addition to - `$(out_files)` when you run `make clean`. (Example: - `*.o`). - - slow_files : A list of things in `$(out_files)` that (as an - exception) should _not_ be deleted when you run `make - mostlyclean`. - -Each directory containing a Makefile is a "module". The module name -is one of 4 things (with / replaced with _ in all cases): - - `all` - - $(realpath --relative-to=. $dir_name) - - dep-top - - dep-$(realpath --relative-to=$(topoutdir) $dir_name) - -The dep-* options are only used if that directory is not a child of -the current directory. +Write your own `common.{each,once}.{head,tail}.mk` files 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 Here is a table of all of the .PHONY targets that automake takes care of for you: -| this | and this | are aliases for this | which is just a case of this | -|------+------------------+----------------------+------------------------------| -| all | build | build-all | build-$(module) | -| | install | install-all | install-$(module) | -| | uninstall | uninstall-all | uninstall-$(module) | -| | mostlyclean | mostlyclean-all | mostlyclean-$(module) | -| | clean | clean-all | clean-$(module) | -| | distclean | distclean-all | distclean-$(module) | -| | maintainer-clean | maintainer-clean-all | maintainer-clean-$(module) | -| | check | check-all | check-$(module) | -| | | | dist | +| 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 responsible for implementing the `$(outdir)/check` target in +each of your Makefiles. + +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`. + +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. | ---- Copyright (C) 2016 Luke Shumaker -- cgit v1.2.3-54-g00ecf From 8b4ed19938c1314ea15eb0b507dcc5aadeb3d9e6 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Thu, 3 Mar 2016 17:55:39 -0500 Subject: make a public `am_path`, more docs --- automake.head.mk | 3 +- automake.txt | 96 +++++++++++++++++++++++++++++++++++++++++++++----------- config.mk.in | 47 --------------------------- 3 files changed, 80 insertions(+), 66 deletions(-) delete mode 100644 config.mk.in (limited to 'automake.head.mk') diff --git a/automake.head.mk b/automake.head.mk index ad7154c769..c79da83bd8 100644 --- a/automake.head.mk +++ b/automake.head.mk @@ -17,13 +17,14 @@ _am = am_ _am_noslash = $(patsubst %/.,%,$(patsubst %/,%,$1)) # These are all $(call _am_func,parent,child) -#_am_relto = $(if $2,$(shell realpath -s --relative-to='$1' $2)) +#_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 diff --git a/automake.txt b/automake.txt index 22a0b84f63..b6b9184779 100644 --- a/automake.txt +++ b/automake.txt @@ -10,6 +10,34 @@ 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 @@ -21,30 +49,40 @@ you were writing for plain GNU Make, with include $(topsrcdir)/automake.tail.mk -Write your own `common.{each,once}.{head,tail}.mk` files that get -included: +And in the top-level output directory, you write a `config.mk` with: + + ifeq ($(topsrcdir),) + # have your ./configure script adjust topsrcdir if doing an + # out-of-tree build + topsrcdir := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) + + # your configuration + + endif + +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 -Here is a table of all of the .PHONY targets that automake takes care -of for you: +The `common.*.mk` makefiles are nice for including generic pattern +rules and variables that aren't specific to a directory. -| 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're probably thinking that this sounds too good to be true! +Unfortunately, there are two major deviations from writing a plain +recursive Makefile: -You are responsible for implementing the `$(outdir)/check` target in -each of your Makefiles. + 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)`. Telling automake about your program ----------------------------------- @@ -53,6 +91,11 @@ 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: @@ -97,9 +140,26 @@ between directories: | | files in this directory, things in the dependency | | | directory will not be built. | +Tips, notes +----------- + +If you have a `./configure` script, don't have it modify the +`Makefile`s; have everything you need modified be in +`$(topoutdir)/config.mk` and have it generate that; then have it copy +(or (sym?)link?) every `$(srcdir)/Makefile` into `$(outdir)/Makefile`. + +If you're wondering, `am_path` is defined equivalently to: + + am_path = $(if $1,$(shell realpath -sm -- $1))` + +though it is implemented purely in Make, instead of calling out to +another program. Besides that older versions of coreutils don't have +`realpath`, calling to an external program like that can have a +_substantial_ slowdown on the parse time. + ---- 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 do so. +absolutely every way that I can legally grant to you. diff --git a/config.mk.in b/config.mk.in deleted file mode 100644 index ca8fa7fefc..0000000000 --- a/config.mk.in +++ /dev/null @@ -1,47 +0,0 @@ -ifeq ($(topsrcdir),) -topsrcdir := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) - -PACKAGE = rvs -VERSION = 0.10 -pkgtextdomain = $(PACKAGE) - -DESTDIR = -prefix = /usr/local -exec_prefix = $(prefix) - -bindir = $(exec_prefix)/bin -sbindir = $(exec_prefix)/sbin -libexecdir = $(exec_prefix)/libexec -datarootdir = $(prefix)/share -datadir = $(datarootdir) -sysconfdir = $(prefix)/etc -sharedstatedir = $(prefix)/com -localstatedir = $(prefix)/var -runstatedir = $(localstatedir)/run -localedir = $(datarootdir)/locale - -pkgdatadir = $(datadir)/$(PACKAGE) -pkglibexecdir = $(libexecdir)/$(PACKAGE) - -CFLAGS = -std=c99 -Werror -Wall -Wextra -pedantic -O2 -CPPFLAGS = -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -D_GNU_SOURCE - -CC = cc -M4 = m4 -MKDIR = mkdir -MKDIRS = mkdir -p -RMDIRS = rmdir -p -INSTALL_DATA = install -m644 -INSTALL_PROGRAM = install -m755 -CP = cp -MV = mv -RM = rm -f -SED = sed -SORT = sort -TAR = tar -TRUE = true -PRINTF = printf - -AUTODEPS = t - -endif -- cgit v1.2.3-54-g00ecf From 37ef1295bf8885876df8de989569a2233f97583f Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 4 Mar 2016 13:20:55 -0500 Subject: Have the caller be responsible for setting topoutdir --- automake.head.mk | 11 ++++------- automake.txt | 40 +++++++++++++++++++++++----------------- 2 files changed, 27 insertions(+), 24 deletions(-) (limited to 'automake.head.mk') diff --git a/automake.head.mk b/automake.head.mk index c79da83bd8..37d3b07997 100644 --- a/automake.head.mk +++ b/automake.head.mk @@ -30,13 +30,10 @@ am_path = $(foreach p,$1,$(call _am_relto,$p)) all: build .PHONY: all -## Set topoutdir, outdir, and srcdir (assumes that topsrcdir is -## already set, and that $(topoutdir)/config.mk has been included) -ifeq ($(topoutdir),) -topoutdir := $(call _am_path,$(dir $(lastword $(filter %/config.mk config.mk,$(MAKEFILE_LIST))))) -endif - outdir := $(call _am_path,$(dir $(lastword $(filter-out %.mk,$(MAKEFILE_LIST))))) - srcdir := $(call _am_path,$(topsrcdir)/$(call _am_relto,$(topoutdir),$(outdir))) +## 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) diff --git a/automake.txt b/automake.txt index c9834d3f27..935af5f5a9 100644 --- a/automake.txt +++ b/automake.txt @@ -41,25 +41,14 @@ 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 - # adjust the number of `../` segments as appropriate - include $(dir $(lastword $(MAKEFILE_LIST)))/../../config.mk + topoutdir ?= ... + topsrcdir ?= ... include $(topsrcdir)/automake.head.mk # your makefile include $(topsrcdir)/automake.tail.mk -And in the top-level output directory, you write a `config.mk` with: - - ifeq ($(topsrcdir),) - # have your ./configure script adjust topsrcdir if doing an - # out-of-tree build - topsrcdir := $(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST)))) - - # your configuration - - endif - 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 @@ -145,10 +134,27 @@ between directories: Tips, notes ----------- -If you have a `./configure` script, don't have it modify the -`Makefile`s; have everything you need modified be in -`$(topoutdir)/config.mk` and have it generate that; then have it copy -(or (sym?)link?) every `$(srcdir)/Makefile` into `$(outdir)/Makefile`. +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 -- cgit v1.2.3-54-g00ecf