vendredi 17 avril 2009

Makefile with chained pattern rules for dummies

This is a simple Makefile (for GNU make, see http://www.gnu.org/software/make/) which I am publishing as a reference to my poor little brain. Who know ? Maybe it can prove useful for someone else. I even inserted some comments.


My problem was easy : I am given some RRA files (http://oss.oetiker.ch/rrdtool/) that need to be rendered as PNG. I am willing to render to PNG using an XSL stylesheet although I am aware 'rrdtool graph' would handle this anyway in one shot.

I want fun.


What is the point in having rrdtool handling all the stuff for you without learning anything new in the process ? Does it feel boring ?

It does. I want fun.



Here is the fun stuff :

I am dumping the RRA to XML which I am transforming to SVG with an XSL and finally render it as PNG.

Do you feel puzzled ? I am not, if you are, here is a free tip : the point is not in the result but in the way to handle this. Let read some serious stuff.


I want a batch to handle this task and repeat it over and over with as little keystrokes as possible. All of this because developers are lazy. I need a simple batch to handle this and wanted to learn something new.

Perl is a long friend, so I decided to not use it at all.

Instead, I took a look at GNU make manual. This is not something non technical people want to read, you have been warned. Nonetheless, I ended my reading with three informations:
  1. make lets you chain rules easily
  2. check if a file need a rebuild
  3. let you build pattern rules and chain them.
Those render the task trivial. Have fun reading the Makefile because I did had fun writing it at 7am this morning.



% cat Makefile | myblog -s 'Makefile with chained pattern rules for dummies'


#######################################################################
# The four following lines are variables declaration. This let
# you easily switch to a different renderer as needed.

RRD=/usr/bin/rrdtool
XSLTPROC=/usr/bin/xsltproc
XSL_STYLESHEET=rrd.xsl
INKSCAPE=/usr/bin/inkscape

#######################################################################
# Dynamically build the file lists used to iterate over for rendering
# purposes.
#
# Note: "RRDs = *.rrd" does not work.

# You MUST use the 'wildcard' function. XMLs, SVGs and PNGs lists
# are then build using a suffix substitution.

RRDs = $(wildcard *.rrd)
XMLs = ${RRDs:.rrd=.xml}
SVGs = ${RRDs:.rrd=.svg}
PNGs = ${RRDs:.rrd=.png}



#######################################################################
# Targets
#
# Targets are the files you actually want to generate. But you can also
# use virtual targets which are not bound to an actual file. .PHONY
# is used to declare those targets and make sure they are build even if
# the file really exist and is up to date.

.PHONY: clean all

# 'all' is usually the default target. We make it depends on a list of
# the PNG file we actually want. By using 'make all' make will then
# look for a way to build PNG files.

all : $(PNGs)


# Next target means "PNG files depends on SVG files". So make will
# look for a way to build the SVGs ...

$(PNGs) : $(SVGs)


# ... which the next target explain pretty well. To build the SVG files
# we need an XSL stylesheet and XML files ...

$(SVGs) : $(XSL_STYLESHEET) $(XMLs)


# ... XML files are build from RRD files.

$(XMLs) : $(RRDs)


# But how the hell will make know which command to use to actually
# render an SVG ? This is what conversion rules are for. The syntax
# is easy :
#
# %.ext_we_want : %.ext_needed
# command
#
# Which read as:
# To build a file '*.ext_we_want' , we need '*.ext_neded' and then run
# the 'command'.
#
# The percent is acting as a wildcard to match the files. make then
# instantiate special variables that can be used to craft a complete
# and fully command :
#
# An incomplete cheatsheet of make special variables :
#
# $@ name of file to be made
# $? names of the changed dependents
# $<> $@

# Would give and execute :
#
# rrdtool dump file.rrd > file.xml
#
# Easy isn't it ?

# The rule that explain how to get SVG out of XML files :
%.svg : %.xml
$(XSLTPROC) $(XSL_STYLESHEET) $< > $@

# Finally explain how to render as PNG
%.png : %.svg
$(INKSCAPE) $< --export-png=$@

# Congratulations ;)
# We can know generate a ton of files with a single command :
#
# % make

# Finally, a virtual target used to remove the files and start the process
# from scratch.
clean :
# Remember to use tabulations for the first indentation!
#
-$(RM) $(XMLs)
-$(RM) $(SVGs)
-$(RM) $(PNGs)



A little explanation about make :

When a dependency change, targets that depend on it are flagged for an update and will be rebuild even if they already exist. If nothing changed, he will happily keep the task and proceed to the next one.

This is the usefulness of 'make' : it focus on changes, thus saving the rendering/rebuild time.

In my example, if I change the XSL_STYLESHEET file, 'make' will detect it and flag targets that depends on it as needing an update. 'make' will thus have to rebuild the outdated SVG. In turn, the PNG files are now flagged as obsolete and marked as needing a rebuild.


Edit, 'make', Focus on the result ...

... not on the building process.

Aucun commentaire: