Self-Documenting Makefiles

[article]

In this article, Ask Mr. Make explores self-documenting Makefiles.

to load the Makefile. This last part is subtle.

If the Makefile that included help-system.mak was simply called Makefile (or makefile or GNUmakefile) then GNU Make will look for it automatically and it's enough to type make help to get help. If it was not then the actual Makefile name needs to be specified with the -f parameter.

This rule uses a variable called dash-f to output the right command line. dash-f contains nothing if one of the default Makefile names was used or -f where is the correct Makefile name.

dash-f := $(if $(filter-out Makefile makefile GNUmakefile,$(parent-makefile)), -f $(parent-makefile))

dash-f looks at the value of a variable called parent-makefile which contains the name of the Makefile that included help-system.mak. If it's not a standard name then it returns the name of the parent makefile with the -f option.

parent-makefile is determined by looking at the MAKEFILE_LIST. MAKEFILE_LIST is a list of all the Makefiles read so far in order. help-system.mak first determines its own name by doing:

this-makefile := $(call last-element,$(MAKEFILE_LIST))

and then it gets the list of all the other Makefiles included by removing this-makefile (i.e. help-system.mak) from the MAKEFILE_LIST:

other-makefiles := $(filter-out $(this-makefile),$(MAKEFILE_LIST))

The final element of other-makefiles will be the parent of help-system.mak:

parent-makefile := $(call last-element,$(other-makefiles))

The last-element function is used to get the last element of a space-separated list. last-element returns the last word in a list by getting the word count using $(words ) and then returning the word referenced by it. Since GNU Make's lists are counted from position 1 $(words LIST) is the index of the last element.

define last-element
$(word $(words $1),$1)
endef

Documenting Makefiles with the print-help function is easy.

Just add the relevant $(call print-help,target,description) to the prerequisite list for each target you want to document. If you add the call right next to the commands that are used for the target than the help system not only prints help, but automatically points the user to the place in the Makefile to look for more information.

It's also easy to keep the documentation up to date because the description of a target is actually part of the definition of the target and not in a separate help list.

Appendix – help-system.mak

help:
@echo $(if $(need-help),,Type \'$(MAKE)$(dash-f) help\' to get help)

need-help := $(filter help,$(MAKECMDGOALS))

define print-help
$(if $(need-help),$(warning $1 -- $2))
endef

define last-element
$(word $(words $1),$1)
endef

this-makefile := $(call last-element,$(MAKEFILE_LIST))
other-makefiles := $(filter-out $(this-makefile),$(MAKEFILE_LIST))
parent-makefile := $(call last-element,$(other-makefiles))

dash-f := $(if $(filter-out Makefile makefile GNUmakefile,\
$(parent-makefile)), -f $(parent-makefile))

About the author

John Graham-Cumming's picture John Graham-Cumming

John Graham-Cumming is Co-Founder at Electric Cloud, Inc . Prior to joining Electric Cloud, John was a Venture Consultant with Accel Partners, VP of Internet Technology at Interwoven, Inc. (IWOV), VP of Engineering at Scriptics Corporation (acquired by Interwoven), and Chief Architect at Optimal Networks, Inc. John holds BA and MA degrees in Mathematics and Computation and a Doctorate in Computer Security from Oxford University. John is the creator of the highly acclaimed open source POPFile project. He also holds two patents in network analysis and has others pending.

AgileConnection is one of the growing communities of the TechWell network.

Featuring fresh, insightful stories, TechWell.com is the place to go for what is happening in software development and delivery.  Join the conversation now!