Systems, Tools, and Terminal Science

Main menu

Post navigation

Unix as IDE: Building

Because compiling projects can be such a complicated and repetitive process, a
good IDE provides a means to abstract, simplify, and even automate software
builds. Unix and its descendents accomplish this process with a Makefile, a
prescribed recipe in a standard format for generating executable files from
source and object files, taking account of changes to only rebuild what’s
necessary to prevent costly recompilation.

One interesting thing to note about make is that while it’s generally used
for compiled software build automation and has many shortcuts to that effect,
it can actually effectively be used for any situation in which it’s required to
generate one set of files from another. One possible use is to generate
web-friendly optimised graphics from source files for deployment for a website;
another use is for generating static HTML pages from code, rather than
generating pages on the fly. It’s on the basis of this more flexible
understanding of software “building” that modern takes on the tool like Ruby’s
rake have become popular, automating the general tasks for producing and
installing code and files of all kinds.

Anatomy of a Makefile

The general pattern of a Makefile is a list of variables and a list of
targets, and the sources and/or objects used to provide them. Targets may not
necessarily be linked binaries; they could also constitute actions to perform
using the generated files, such as install to instate built files into the
system, and clean to remove built files from the source tree.

It’s this flexibility of targets that enables make to automate any sort of
task relevant to assembling a production build of software; not just the
typical parsing, preprocessing, compiling proper and linking steps performed by
the compiler, but also running tests (make test), compiling documentation
source files into one or more appropriate formats, or automating deployment of
code into production systems, for example, uploading to a website via a git
push or similar content-tracking method.

An example Makefile for a simple software project might look something like
the below:

The above isn’t the most optimal Makefile possible for this project, but it
provides a means to build and install a linked binary simply by typing make.
Each target definition contains a list of the dependencies required for the
command that follows; this means that the definitions can appear in any order,
and the call to make will call the relevant commands in the appropriate
order.

Much of the above is needlessly verbose or repetitive; for example, if an
object file is built directly from a single C file of the same name, then we
don’t need to include the target at all, and make will sort things out for
us. Similarly, it would make sense to put some of the more repeated calls into
variables so that we would not have to change them individually if our choice
of compiler or flags changed. A more concise version might look like the
following:

More general uses of make

In the interests of automation, however, it’s instructive to think of this a
bit more generally than just code compilation and linking. An example could be
for a simple web project involving deploying PHP to a live webserver. This is
not normally a task people associate with the use of make, but the principles
are the same; with the source in place and ready to go, we have certain targets
to meet for the build.

PHP files don’t require compilation, of course, but web assets often do. An
example that will be familiar to web developers is the generation of scaled and
optimised raster images from vector source files, for deployment to the web.
You keep and version your original source file, and when it comes time to
deploy, you generate a web-friendly version of it.

Let’s assume for this particular project that there’s a set of four icons used
throughout the site, sized to 64 by 64 pixels. We have the source files to hand
in SVG vector format, safely tucked away in version control, and now need to
generate the smaller bitmaps for the site, ready for deployment. We could
therefore define a target icons, set the dependencies, and type out the
commands to perform. This is where command line tools in Unix really begin to
shine in use with Makefile syntax:

With the above done, typing make icons will go through each of the source
icons files in a Bash loop, convert them from SVG to PNG using ImageMagick’s
convert, and optimise them with pngcrush, to produce images ready for
upload.

A similar approach can be used for generating help files in various forms, for
example, generating HTML files from Markdown source:

And perhaps finally deploying a website with git push web, but only after
the icons are rasterized and the documents converted:

deploy: icons docs
git push web

For a more compact and abstract formula for turning a file of one suffix into
another, you can use the .SUFFIXES pragma to define these using special
symbols. The code for converting icons could look like this; in this case, $<
refers to the source file, $* to the filename with no extension, and $@ to
the target.

Tools for building a Makefile

A variety of tools exist in the GNU Autotools toolchain for the construction of
configure scripts and make files for larger software projects at a higher
level, in particular autoconf and automake. The use of these
tools allows generating configure scripts and make files covering very
large source bases, reducing the necessity of building otherwise extensive
makefiles manually, and automating steps taken to ensure the source remains
compatible and compilable on a variety of operating systems.

Covering this complex process would be a series of posts in its own right, and
is out of scope of this survey.

I know that people have made entire books discussing ‘make’, but I’m surprised that you didn’t at least talk about “old-fashioned” suffix rules. This would allow your penultimate examples to be written like this: