Vous êtes sur la page 1sur 4

PROGRAMMING C Tutorial: Part 12

C: Part 12

Language of the ‘C’


Following on from last month’s article, Steven Goodwin, looks at how the make utility can be used to improve the

development process. BY STEVEN GOODWIN

I
n preparing this section, I asked you would want to rebuild those source Line 1 describes a target. The text to the
twelve different programmers for files to reflect the changes. A makefile left of the colon dictates what we want to
the best way to write a make file. I (by default called Makefile – the capital produce (an executable file called
got twelve different answers! Writing M is important!) describes each com- convunit in this case), whilst the right
makefiles, like code, novels or music is a ponent of the project, how they should hand side lists the dependant files we
uniquely individual experience. There is be built, and what constitutes them have to use in order to build it. Our
no right or wrong way – whatever works being ‘out of date’. The watchword here makefile is effectively saying that should
(and is readable!) can considered a is dependency. converter.c or converter.h change, then
‘good makefile’! The method we’re using If we have a two file project where ‘convunit’ will be out of date and needs
here is fairly ‘traditional’ and shall be converter.c includes converter.h, then we to be rebuilt.
developed from first principles, so you can say converter.c is dependent on Each subsequent line after a target
can see each step in the process. converter.h. If converter.h changes, it that begins with a tab (and only a
stands to reasons that converter.c must tab!) holds the command, or commands,
Make also have changed in some way, and so that we must execute in order to produce
So first off; what is a makefile? And what needs to be re-built. We can build a make- the target file. It stands to reason, there-
is make? Well, make is a utility that file to describe this. We can then build fore, that those commands must produce
helps reduce development time by this program by typing: the target file in some manner. You can
allowing us to only rebuild parts of the include as many commands as you need;
project (using gcc) that need it; if you make
have not changed ‘converter.c’ or Listing 1: Makefile
included header files, why would you If you had not named your file ‘Makefile’
1 convunit: converter.c U
want to spend time compiling it when but listing1make, for example, then you
converter.h
the result will be the same as it was last will need to use the -f flag.
2 gcc converter.c -o U
week?! Conversely, if you’ve changed a
convunit
header file that is used in four places, make -f listing1make

62 November 2002 www.linux-magazine.com


C Tutorial: Part 12 PROGRAMMING

projects with several source files, this and config.o process.o output.o debug.o)
Listing 2: Makefile also means that updates can be built and report that it is “up to date”.
1 convunit: converter.o with just one compile and one link, Whenever a file changes, only the
2 gcc converter.o -o U which is much more efficient than necessary dependencies will be rebuilt.
convunit several compiles, and one link. Object This is determined by looking at the date
3 files (by convention) use the .o exten- stamp of the files in question. You can
4 converter.o: converter.c U sion, which is usually pronounced “dot test this by typing:
converter.h oh!”.
5 gcc -c converter.c -o U Here, we are nesting targets. In touch output.h
converter.o this example, convunit is dependant on make
converter.o (an object file), which in turn
is dependant on the two files converter.c This will then build core.o and output.o
semi-colons let you put two or more and converter.h. (since they are the only targets that
commands a line, while the backslash is Should any of these files change, depend on output.h) and re-link a new
available for line continuation if convunit will be re-built. We can place executable with the 3 old, and 2 new,
required. This is sometimes necessary, the targets in any order we choose, object files. It is very rare to include
since each line executes in its own shell, however, the first target given (convunit) header files like stdio.h and stdlib.h in
and you might need to include several is the one built by default and so should the dependencies list.
commands. The following would fail be the main executable. This is because they are standard
if each instruction was placed on a Looking to the bigger picture, we have headers, and changing the function
different line. already split our project into modules prototypes or macros here would require
(see last month’s Linux Magazine issue a change in the glibc libraries also. That
main: source/converter.c U 24) and have five ready-made targets last happened many years ago with the
cd source; gcc converter.c (core, config, process, output and debug) switch from version 5 to 6, and required
that map nicely onto five object files. a complete recompile of all system and
make will execute each command in From this we can build a complete user software.
sequence until none is found (i.e. the line makefile for the project.
does not begin with a tab) or an error These last two examples make uses of Showroom Dummies
occurs. At this point it will stop trying to the ‘-c’ option of GCC, which indicates To ease the task of maintenance, make
build that target and exit. To suppress we want to only build an object file, and supports macro substitutions which
these errors, start each command with a not a complete executable. you can use to save re-typing repe-
minus sign and it will continue with the Our first invocation of make will build titive command line switches. This is -
next instruction (we’ll see where that is five object files (the .o files from lines especially useful for changing compiler
used later). Also, as each command is 4,7,10,13 and 16) and one executable and linker options as one macro can
echoed to the screen you may wish to (line 1); our second will build none! It replace everything in one go.
stop this by using the @ prefix. will spot that the file convunit is newer Macros are, by convention, always
than all its dependencies (converter.o upper case and defined as a ‘name=
convunit: converter.c U
converter.h Listing 3: Makefile
@echo "Now compiling U
1 convunit: converter.o config.o process.o output.o debug.o
converter.c ..."
2 gcc converter.o config.o process.o output.o debug.o -o convunit
gcc converter.c -o convunit
3
4 converter.o: converter.c converter.h config.h output.h process.h U
Temporary Like Achilles debug.h
This makefile can be improved however
5 gcc -c converter.c -o converter.o
by building object files, and not
6
executables. Object files are compiled
7 config.o: config.c converter.h config.h process.h
versions of source code (it can consist of
8 gcc -c config.c -o config.o
one or more ‘C’ files), which lack the
9
essential ingredients that make them
10 process.o: process.c converter.h process.h
executable (like access to glibc, and a
11 gcc -c process.c -o process.o
place to start, for instance!). This not
12
only makes them smaller, but also does
13 output.o: output.c converter.h output.h process.h
not tie them in to any particular
14 gcc -c output.c -o output.o
executable.
15
They can be built individually, and
16 debug.o: debug.c converter.h debug.h process.h
then linked together with other object
17 gcc -c debug.c -o debug.o
modules to make one executable. For

www.linux-magazine.com November 2002 63


PROGRAMMING C Tutorial: Part 12

substitution’ pair. They are used with the Notice that the equals sign is used debug.o
$(NAME) syntax and are substituted without spaces as it helps distinguish
automatically before executing any build between a macro definition and target The implied dependencies of an exe-
command. This way, any errors are name. For other examples of CFLAGS, cutable (converter, in the case above) is
explained with real commands and see the BOXOUT: Useful compiler flags. its equivalent .o file, and anything else
parameters, instead of macro names that given on the right hand side of the colon.
may be quite complex and obtuse. College Girls Are Easy That is – its usual dependencies.
Another one of make‘s many features to For advanced work, it is possible to
CC = gcc improve the quality of life are implied create your own implied dependencies;
CFLAGS = -Wall dependencies. Make knows that a C file they are called suffix rules.
generates a .o object file, and that it must
converter.o: converter.c U use gcc to do so; the dependency of Time After Time
converter.h the .o on the .c is implied and so make In addition to macros, there are a
$(CC) $(CFLAGS) converter.c can perform the compile operation number of special variables with a
automatically! This allows you to reduce similar appearance to macros, as both
A number of macros exist by default a typical line to: start with a ‘$‘ symbol. When building
(type “make -p” in a shell to find out make files they can be used to enhance
which) but these can still be changed if config.o: config.c converter.h U error messages, or to provide parameters
necessary. There are also a number of config.h process.h to other programs. They also work inside
standard macros that you will see, so quoted strings.
you should become at least comfortable On the surface, it might appear that we
with them (see tables 1 & 2). have lost the means to use macros and converter.o: converter.c $$
Macros can also be set from the shell, apply special compile flags to gcc. Not converter.h
by giving the ‘name=substitution’ pair so! By using the CFLAGS macro (which
as an argument to make. is common) we can add warnings, Box 2: Useful compiler flags
compiler optimisations, or any number
-D_DEBUG_FLAGS Automatically defines
make CFLAGS=-Wall of switches we want, and they will get the macro
used within the implied dependency. ‘_DEBUG_FLAGS’to the
Table 1: Conventional Macros Notice, however, that line 3 provides source code.
an explicit build instruction because -g Include GNU debugging
Macro Description Example information into the
make doesn’t understand that a col-
CC Name of the C compiler GCC executable.This allows
MAKE The make utility make
lection of .o files need to be built into an you to use gdb to step
AS Assembler as executable. This is because it can not through the program
LD Linker ld make the connection between the one line at a time.

FC Name of the Fortran compiler (really!) f77 executable (convunit) and the object -c Compile and assemble,
files. By changing line 2 and calling our but don’t link. i.e. create
the object file
ELF ‘converter’ instead, we can do
-o converter Specify the output file
Table 2: Common Macros without line 3.
-Wall Specifies the warning
Macro Description level.‘All’is best.
TARGETS The names of the targets being 2 converter: converter.o U -O3 Specify the optimisation
compiled config.o process.o output.o U level.0 is off (debug),3 is
SOURCES Those files to be compiled the highest. Using -Os
LIBS Directories for other libraries Box 1: Targets will optimize for space,
INC Directories for other headers instead of speed.
files .SILENT: Does not echo any command -fPIC Switch specific flag
executed. Equivalent to prefixing options. Here, PIC tells
CFLAGS Compiler flags
each command with an @ gcc to produce position
LFLAGS Linker flags
.IGNORE: Ignore any errors from the independent code (if
commands. Equivalent to - on possible).The option
each command. name is case insensitive.
Table 3: Special Variables .PRECIOUS Does not remove the target file Used to produce libraries
$@ Name of the current target being removed after an error. that would work in more
$$@ As $@, but only available on .DEFAULT Tries to build this if the given than one place.
dependency line target doesn’t exist. -I /usr/local/apache2/include Also search the named
$? Files that are newer than the .PHONY Indicates that these targets do not directory for header files.
target,and so need building really compile into programs. Same the INC common
$% ?Member files of library files? Used for cases like ‘clean’and macro.
$< $? for suffix rules ‘install’,in case there’s a file called
$* $@ for suffix rules.The files (say) ‘clean’in the current directory Note:There is no space between the flags switch and
suffix is omitted, however. that could confuse the situation. the parameter,except with ‘I’.

64 November 2002 www.linux-magazine.com


C Tutorial: Part 12 PROGRAMMING

@echo "Trying to build $@ U above sentences are true, then ‘install’ need to add more targets, change
(because $? are too new!)" must be the name of a target. Funnily dependencies, or remove old files. Doing
$(CC) $(CFLAGS) converter.c enough, it is! The ‘install’ target often this manually can become a bind,
includes commands to copy configuration so there are a number of tools to help
For a list of these special variables, and executable files to the appropriate you, such as mkdepend, mkmkf and
please refer to table 3. place. These targets, however, are phony makedepend. We shall look at this latter.
– they don’t really produce a file – and as As the name suggests, makedepend
Shoot That Poison Arrow such need to be indicated by adding a will build a list of dependencies for the
When make is run without arguments .PHONY line to the make file (see listing files specified on its command line. So,
it will look for the first target in the 3 and BOXOUT: Targets) assuming all our source files in the same
makefile and try to build it. If the We can use this knowledge to enhance directory (and it contains no rogue files
makefile contains more than one project, our makefile by adding clean and install. from other projects), we can type:
you should create an extra target named Notice that in the case of clean we ignore
all, which is dependent on each of the all errors, and with install we suppress makedepend *.c
other targets. This way, every project will the echo command; and will require
get built with a single call to make. You superuser privileges. In these cases no And a complete list of dependencies
can also build a specific target by dependencies are given, meaning the (including things like stdio.h, and
including it as an argument. instructions are executed every time that stdlib.h) will be built, stored in the
particular target is called. This produces makefile. And in the correct format!
make testbed a complete makefile, ready for use! Makedepend does a couple of clever
make config.o things here. First off, it makes a back-up
There’s a guy works down of your original makefile and calls it
Now, most Linux users who build from the chip shop? ‘Makefile.bak’. Then it appends the
sources are familiar with the trio of ./con- As time goes on, and projects change, dependency information to Makefile.
figure, make, make install. If both the the makefile will become outdated. We’ll What is clever here is that a second call
to makedepend will not re-append the
Listing 4: Makefile same data. Even in a small project such
as ours, makedepend can add 50 or more
1 # We're now using implied dependencies!
lines to the makefile. How does it know?
2 convunit: converter.o config.o process.o output.o debug.o
Well, it adds a comment marked ‘DO
3 gcc converter.o config.o process.o output.o debug.o -o convunit
NOT DELETE’ before the appended text.
4 converter.o: converter.c converter.h config.h output.h process.h U
If this already exists, makedepend
debug.h
removes the text below it, and adds the
5 config.o: config.c converter.h config.h process.h
new information.
6 process.o: process.c converter.h process.h
Naturally, calling makedepend without
7 output.o: output.c converter.h output.h process.h
arguments will not find any
8 debug.o: debug.c converter.h debug.h process.h
dependencies and thus produce an
empty block at the bottom of the file.
Listing 5: Makefile This is still useful, as it makes the
makefiles small enough to fit in a maga-
01 CFLAGS -Wall
zine! And as long as we add the
02
dependencies back to the makefile
03 converter: converter.o config.o process.o output.o debug.o
before trying to compile, all is well!
04 converter.o: converter.c converter.h config.h output.h process.h U
With the exception of .DEFAULT, each
debug.h
can affect specific targets by including its
05 config.o: config.c converter.h config.h process.h
name as a dependency. If no target is
06 process.o: process.c converter.h process.h
specified, then it will affect all targets
07 output.o: output.c converter.h output.h process.h
within the makefile. ■
08 debug.o: debug.c converter.h debug.h process.h
09
The language of ‘C’has been brought
10 clean:
to you today by Steven Goodwin and
THE AUTHOR

11 -rm *.o converter


12 the pages 62–65. Steven is a lead
13 install: programmer, currently finishing off a
14 @echo "Copying conf file to /etc" game for the Nintendo GameCube
15 cp convert.conf /etc console.When not working, he can
16 often be found relaxing at London
17 .PHONY: clean install LONIX meetings.

www.linux-magazine.com November 2002 65

Vous aimerez peut-être aussi