הלינקייה: מגזין חודשי למפתחים

רוצה לשמוע על כל האירועים, המדריכים, הקורסים והמאמרים שנכתבו החודש ?
הלינקייה הינו מגזין חופשי בעברית שמשאיר אותך בעניינים.
בלי ספאם. בלי שטויות. פעם בחודש אצלך בתיבה.

Pattern Rules

A make rule is the basic building block of a makefile. The rule tells the make utility how to act. The syntax for a make rule is:

     targets : prerequisites
             recipe
             ...

A target is out of date if no file named target exists, or if it is older than any of its prerequisites. When make finds a target that is out of date, it runs the recipe in a shell to update it.

So far, we were able to reduce repetition using make automatic variables, but there was always one place we needed to duplicate our code - every target had to specify its rules explicitly. Many times, we have multiple targets with almost the same rules.

Pattern rules allow us to avoid repetition when writing these rules. In make, there are two kinds of pattern rules, the builtin rules and the custom rules.

#########################################
# GNU make comes with a huge number of builtin
# rules that save us the trouble of writing
# our own. 
# One simple example is the rule to transform
# a .c file to an .h file
# Using this rule, we can code only the
# dependency tree, and let make do all the work
 
objects = source1.o source2.o source3.o
OUTPUT = runme
RM = rm -f
 
$(OUTPUT): $(objects)
        $(CC) $(objects) -o $@
 
source1.o: source1.c header1.h
 
source2.o: source2.c header2.h
 
source3.o: source3.c header1.h header2.h
 
clean:
        @echo "Cleaning uP"
        rm -f $(OUTPUT) $(objects)
 

Luckily, this is not the end of it. GNU make also provides us with the ability to write our own custom rules for our own needs.

The patterns are called static rules, and the syntax is as follows:

objects = foo.o bar.o
 
all: $(objects)
 
$(objects): %.o: %.c
        $(CC) -c $(CFLAGS) $< -o $@

In every static rule, a new special variable exists - the $*. This is the stem of the file, which is the part that was replaced by the % character in the template.

We can also delete the target file list from the begining of the rule, and we'll get a new custom implicit rule, like the example below.

############################################
# This makefile uses a prepare_source perl script
# to create a .c source file from a .template file
# prepare_source.pl is a code generator, and using
# this makefile, we make sure our code will get
# re-created from the templates each build
#
# Note how touching the template file causes
# the hello app to re-build.
 
PERL=/usr/bin/perl
 
hello: main.o
        $(CC) $^ -o $@
 
main.o: main.c
 
%.c:%.template
        $(PERL) prepare_source.pl $< > $@
 
clean:
        rm hello *.o
#############################
# An example for using the --print-data-base 
# make option to print all the database
# We're using a shell script to format
# the output and filter only relevant stuff
 
PERL=/usr/bin/perl
AWK=/usr/bin/awk
SORT=/usr/bin/sort
 
.PHONY: help clean
 
hello: main.o
	$(CC) $^ -o $@
 
main.o: main.c
 
%.c:%.template
	$(PERL) prepare_source.pl $< > $@
 
clean:
	rm hello *.o
 
help:
	$(MAKE) --print-data-base --question |                \
	$(AWK) '/^[^.%][-A-Za-z0-9_]*:/                       \
		{ print substr($$1, 1, length($$1)-1) }' |     \
	$(SORT) 
 

Implit rule also provides dependencies for its targets. So, if we're using the 'cc -c' rule, we don't need to specify the .c file explicitly. Let's examine another example:

################
# Note the use of implicit rules for the .c -> .o conversion
# We don't need to specify the .c file, because the implicit rule
# provides us with one.
#
 
objects = main.o kbd.o command.o display.o \
         insert.o search.o files.o utils.o
 
edit : $(objects)
       cc -o edit $(objects)
 
main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
 
.PHONY : clean
clean :
        rm edit $(objects)

Grouping Entries By Prerequisites

Taking into account implicit rules, we can group the .o entries by their prerequisites and get shorter makefiles. When several targets are written in the same line, make regards them as if they were in separate lines, each taking the same set of prerequisites. This one saves repeating the same prerequisites over and over.

Have a look at this same makefile when entries are grouped:

objects = main.o kbd.o command.o display.o \
          insert.o search.o files.o utils.o
 
edit : $(objects)
        cc -o edit $(objects)
 
$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h
course: