Skip to main content Skip to navigation

4. Implicit rules

1. Internal macros

Make has a number of pre-defined macros. It's a sensible idea to make use of as many of these macros as you can. Most of these macros are assigned values, but some of them must be assigned by the user. You can always find out what the internal macro definitions are for your machine by using the -p command with make (without providing any arguments), e.g.

make -p

This will probably result in a whole bunch of text just whizzing by your eyes...not too helpful. Try piping this command to less instead. If you want to write the output of make -p to a file, you'll have to type

make -p >& output

Take a look at this output - you'll see it has comment statements (they start with a #) and lots of definitions, such as

LINK.F = $(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)

So, lots of these internal macros contain recursive definitions. However, some of these definitions are nowhere to be seen in the output of make -p.

Example:

In your output of make -p
Can you find a definition of OUTPUT_OPTION ?
Can you find a definition of CFLAGS?
Can you find a definition of FC?

You should find a definition of OUTPUT_OPTION that you can understand. However, you will find no definition of CFLAGS or FC, even though these are used in other definitions - eg, in the definition of COMPILE.c - so where are these objects defined?

Definitions such as CFLAGS, FFLAGS, CC, FC etc must be defined by you in your Makefile. CC and FC are just the C and/or Fortran compilers you wish to use. So far in our examples, we have denoted CC = /usr/bin/gcc similarly you could denote FC as FC=/opt/warwick/packages/intel_fc_80/ifort

The macros CFLAGS/FFLAGS are compiler options (compiler options is just another way of saying compiler flags). For example, if you wish to debug your code, you need to compile with the -g option. Therefore you could set CFLAGS = -g. If you don't wish to define any compiler options, you can use CFLAGS = (i.e., without anything on the right hand side of the equals sign).

Putting it into Practice:

Take your existing makefile and change such that it contains a definition for CFLAGS with the -g option, e.g.
# here are the macro definitions
CC = /usr/bin/gcc
INC = -I.
OBJ = hellomain.o hellofunc.o
DIR = /tmp/
CFLAGS = -g

myexecutable: ${objs}
	${CC} -o $@ ${objs}
	mv $@ ${DIR}

hellomain.o: hellomain.c ${CC} -c ${INC} $?
hellofunc.o: hellofunc.c ${CC} -c ${INC} $?
clean: rm -f core myexecutable ${objs}


Back to top

2. Implicit rules

Implicit rules are sometimes also referred to as suffix rules.

So far, you have included definitions of CC and CFLAGS. Actually, you haven't made any use of CFLAGS so far. You can make use of defintions like CFLAGS while at the same time reducing the amount of repetition in your Makefile by making use of Implicit Rules.

In make, implicit rules are a set of generalised instructions for doing certain tasks, where the instructions are provided as default. For example, an implicit rule can tell make how to construct a .o file (eg an object file) from a .c file (ie a source file). The following example may make this clear.

Example:

Take a look at your output of make -p again. Search for the string .c.o: The .c.o: statement marks the start of an implicit rule.
This implicit rule .c.o tells make how to construct a .o file from a .c file in a general way.
Underneath the .c.o: statement you will find (beneath the comment statements) the line
 $(COMPILE.c) $(OUTPUT_OPTION) $< 
This is the general instruction for making a .o file from a .c file. You'll need to understand what each entry in this instruction means.
Look up the definition of COMPILE.c - it will be found in your make -p output. You should find it is given by
 COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c  
All of these variables on the right hand side of the equals sign would need to be assigned by you in your Makefile. Any of those not assigned by you will just be ignored. You have already met CC and CFLAGS. The other variables are defined as follows:
CPPFLAGS are the options passed to the pre-processor.
TARGET_ARCH defines the machine architecture you are trying to build on.
Now look up the definition of OUTPUT_OPTION - if you didn't do that before! You should understand what the entries mean for this definition.
The final entry is $<, which is reminiscent of the macros you saw in Section 3.2. This macro can only be used in implicit rules and user-defined rules, and it equates to the name of the prerequisite that is being used to make the target. This is very similar to the $? macro that you saw in Section 3.2. It's good practice to ensure you only use $< in user-defined rules, and leave $? for explicit compile instructions.

Making use of these implicit rules obviates the need for placing the following entries in your existing Makefile

hellomain.o: hellomain.c 
	${CC} -c $(INC) $?
hellofunc.o: hellofunc.c
	${CC} -c ${INC} $?

These will now be built automatically according to the implicit rule given by instructions in the .c.o: entry. Acknowledging these implicit rules means you have a much more compact looking Makefile - see below:

Putting it into Practice:

Take your existing makefile and change such that it takes advantage of the implicit rule .c.o:
Notice that according to the implicit rule instruction, you will need to put your -I. option in the CFLAGS definition.
# here are the macro definitions
CC = /usr/bin/gcc
OBJ = hellomain.o hellofunc.o
DIR = /tmp/
CFLAGS = -g -I.

myexecutable: ${objs}
	${CC} -o $@ ${OBJ}
	mv $@ ${DIR}
clean:
	rm -f core ${OBJ}
To running make with this Makefile (do a make clean first!). Have a look at what is written to standard output. It should have expanded the implicit rule for building hellomain.o and hellofunc.o, eg for hellomain.o you should see
 /usr/bin/gcc -g -I. -c -o hellomain.o hellomain.c 
now that you've run make once, alter the timestamp of hellofunc.c (use touch), and re-run make. It should only re-build hellofunc.o and myexecutable. In other words, this new, briefer Makefile behaves exactly like your old, longer, Makefile (that contained explicit commands)!

You should take note that while using implicit rules here hasn't shortened the Makefile that much, but if you had many source files, this would make a huge difference.


Back to top

3. Declaring dependencies

While the above Makefile will work, it's a good idea to explicitly declare any header dependencies you have in your source files. This is because Make can't actually open your files and look inside them to check! It's very easy to declare dependencies - for each .c file that contains special headers (other than standard C headers such as stdio.h and math.h), you should include an entry that has the .o name followed by a colon followed by the name of the header file. Following our example Makefile, you could include the line below as a final entry in your Makefile.

hellofunc.o: hellofunc.h

If you use the gcc compiler, you can use the -M option on your .c source files to get a listing of all header file dependencies.


Back to top

4. Linking in libraries

Suppose you change hellomake.c so that it now includes a maths operation such as sin(x). In this case, C programmers will know that they must link in the standard C maths library at linking time. For those who aren't C programmers, this means including the option -lm at the linking phase. Instead of adding -lm to the CFLAGS we should therefore add it to the LDFLAGS (ld is the GNU linker program). So in this case you might alter your Makefile as shown in the example below:

Example:

Change your Makefile to include the -lm option at linking time, eg
# here are the macro definitions
CC = /usr/bin/gcc
OBJ = hellomain.o hellofunc.o
DIR = /tmp/
CFLAGS = -g -I.
LDFLAGS = -lm

myexecutable: ${objs}
	${CC} -o $@ ${OBJ} ${LDFLAGS}
	mv $@ ${DIR}
clean:
	rm -f core ${OBJ}


Back to top