Monday, October 16, 2006

Development Tool : MAKE

make is a utility for automatically building large applications. This utility determines which pieces of a large program need to be recompiled and issues commands to recompile them. To prepare to use 'make' we must write a file called "makefile" that describe the relationships among files and provide commands for updating each file.make is most commonly used in C/C++ projects, but in principle it can be used with almost any compiled language.

The basic tool for building an application from source code is the compiler. make is a separate, higher-level utility which tells the compiler which source code files to process. It tracks which ones have changed since the last time the project was built and invokes the compiler on only the components that depend on those files. Although in principle one could always just write a simple shell script to recompile everything at every build, in large projects this would consume a prohibitive amount of time. Thus, a makefile can be seen as a kind of advanced shell script which tracks dependencies instead of following a fixed sequence of steps.

Today, programmers increasingly rely on Integrated Development Environments and language-specific compiler features to manage the build process for them instead of manually specifying dependencies in makefiles. However, make remains widely used, especially in Unix-based platforms.


Modern versions

Make has gone through a number of rewrites, and a number of from-scratch variants which used the same file format and basic algorithmic principles, and also provided a number of their own non-standard enhancements, in the time that followed. Some of them are:

  • BSD make, which is derived from Adam de Boor's work on a version of make capable of building targets in parallel, and survives with varying degrees of modification in FreeBSD, NetBSD and OpenBSD. Most notably, it has conditionals and iterative loops which are applied at the parsing stage and may be used to conditionally, and programmatically, construct the makefile, including generation of targets at runtime.
  • GNU make, which is part of most GNU/Linux installations and is frequently used in conjunction with the GNU build system. Its notable departures from traditional make are most noticeable in pattern-matching in dependency graphs and build targets, as well as a number of functions which may be invoked to have the make utility do things like collect a list of all files in the current directory. Where BSD make has a rich set of internal macros at parse time, GNU make typically encourages the use of an external macro package like m4.
  • Microsoft nmake, commonly available on Windows. It is fairly basic, offering only a subset of the features of the two above makes. Note that there exists another, incompatible program also called nmake from AT&T for Unix.

POSIX includes standardization of the basic features and operation of the make utility, and is implemented with varying degrees of completeness in Unix-based versions of make. In general, simple makefiles may be used between various versions of make with reasonable success. Some versions of GNU make and BSD make will look first for files named "GNUmakefile" and "BSDmakefile" respectively, which allows one to put makefiles which use implementation-defined behaviour in separate locations.

Advantages

GNU Make has many powerful features for use in makefiles, beyond what other Make versions have. It can also regenerate, use, and then delete intermediate files which need not be saved.

GNU Make also has a few simple features that are very convenient. For example, the -o file option which says ``pretend that source file file has not changed, even though it has changed.'' This is extremely useful when you add a new macro to a header file. Most versions of Make will assume they must therefore recompile all the source files that use the header file; but GNU Make gives you a way to avoid the recompilation, in the case where you know your change to the header file does not require it.

However, the most important difference between GNU Make and most versions of Make is that GNU Make is free software.


Makefile structure

A makefile consists of lines of text which define a file (or set of files) or a rule name as depending on a set of files. Output files are marked as depending on their source files, for example, and source files are marked as depending on files which they include internally. After each dependency is listed, a series of lines of tab-indented text may follow which define how to transform the input into the output, if the former has been modified more recently than the latter. In the case where such definitions are present, they are referred to as "build scripts" and are passed to the shell to generate the target file. The basic structure is:

# Comments use the pound sign (aka hash)
target: dependencies
command 1
command 2
.
.
.
command n

A makefile also can contain definitions of variables and inclusion of other makefiles. Variables in makefiles may be overridden in the command line arguments passed to the make utility. This allows users to specify different behaviour for the build scripts and how to invoke programs, among other things. For example, the variable "CC" is frequently used in makefiles to refer to a C compiler, and the user may wish to provide an alternate compiler to use.


Example makefile

Below is a very simple makefile that would compile a source called "helloworld.c" using cc, a C compiler. The PHONY tag is a technicality that tells make that a particular target name does not produce an actual file. The $@ and $<>

Note that in the "clean" target, a minus prefixes the command, which tells make to ignore errors in running the command; make will normally exit if execution of a command fails at any point. In the case of a target to cleanup, typically called "clean", one wants to remove any files generated by the build process, without exiting if they don't exist. By tagging the clean target PHONY, we prevent make expecting a file to be produced by that target. Note that in this particular case, the minus prefixing the command is redundant in the common case, the -f or "force" flag to rm will prevent rm exiting due to files not existing. It may exit with an error on other, unintended errors which may be worth stopping the build for.

helloworld: helloworld.o
        cc -o $@ $<

helloworld.o: helloworld.c
cc -c -o $@ $<

.PHONY: clean
clean:
-rm -f helloworld helloworld.o
Link
----------------------------------------------------------------------------------------------------------------

No comments: