• Building and processing the dependency graph to update the requested target is what make is all about.
  • There are a number of different kinds of rules:
    • Explicit rules indicate a specific target to be updated if it is out of date with respect to any of its prerequisites.
    • Implicit rules are either pattern rules or suffix rules found in the rules database built-in to make.
    • Pattern rules use wildcards instead of explicit filenames. This allows make to apply the rule any time a target file matching the pattern needs to updated.
    • Static pattern rules are like regular pattern rules except they apply only to a specific list of target files.
2.1 Explicit Rules
  • A rule does not have to be defined "all at once." Each time make sees a target file it adds the target and prerequisites to the dependency graph. If a target has already been seen and exists in the graph, any additional prerequisites are appended to the target file entry in make's dependency graph.
  • make supports wildcards (also known as globbing).
  • make's wildcards are identical to the Bourne shell's: ~, *, ?, [...], and [^...].Wildcard expansion is performed by make when the pattern appears as a target or prerequisite, when the pattern appears in a command, the expansion is performed by the subshell.
  • In other contexts, wildcards can be expanded explicitly by calling a function.
  • In more controlled environments using wildcards to select the files in a program is considered bad practice because a rogue source file might be accidentally linked into a program.
  • GNU make includes a special target, .PHONY, to tell make that a target is not a real file. Any target can be declared phony by including it as a prerequisite of .PHONY:
.PHONY: clean
clean:
           rm -f *.o lexer.c
  • Normally, phony targets will always be executed because the commands associated with the rule do not create the target name.
  • It is often useful to give phony targets prerequisites. Phony targets can also be thought of as shell scripts embedded in a makefile. Making a phony target a prerequisite of another target will invoke the phony target script before making the actual target.
.PHONY: make-documentation
make-documentation: df
           javadoc ...
.PHONY: df
df:
           df -k . | awk 'NR == 2 { printf( "%d available\n", $$4 ) }'
  • Example:
$(Program): build_msg $(OBJECTS) $(BUILTINS_DEP) $(LIBDEP)
           $(RM) $@
           $(CC) $(LDFLAGS) -o $(Program) $(OBJECTS) $(LIBS)
           ls -l $(Program)
           size $(Program)
.PHONY: build_msg
build_msg:
           @printf "#\n# Building $(Program)\n#\n"
  • Table 2-1. Standard phony targets

Target

Function

All

Perform all tasks to build the application

install

Create an installation of the application from the compiled binaries

clean

Delete the binary files generated from sources

distclean

Delete all the generated files that were not in the original source distribution

TAGS

Create a tags table for use by editors. TAGS is not really a phony since the output of the ctags and etags programs is a file named TAGS.

info

Create GNU info files from their Texinfo sources

check

Run any tests associated with this application

  • Empty targets, we have some command, with no output file, that needs to be performed only occasionally and we don't want our dependents updated, so there’s a rule whose target is an empty file(cookie):
prog: size prog.o
           $(CC) $(LDFLAGS) -o $@ $^
size: prog.o
           size $^
           touch size
  • The size rule uses touch to create an empty file named size after it completes. This empty file is used for its timestamp so that make will execute the size rule only when prog.o has been updated.
  • Example. print all the files that have changed since the last time make print was executed:
print: *.[hc]
           lpr $?
           touch $@
empty files can be used to mark the last time a particular event has taken place.
2.2 Variables
  • $(variable-name) indicates that we want to expand the variable whose name is variable-name.
  • In general, a variable name must be surrounded by $( ) or ${ } to be recognized by make. As a special case, a single character variable name does not require the parentheses.
  • Automatic variables are set by make after a rule is matched. They provide access to elements from the target and prerequisite lists so you don't have to explicitly specify any filenames.

$@

The filename representing the target.

$%

The filename element of an archive member specification. Individual members in an archive file could be a target or a prerequisite. Syntax: archive (member). Ex: foo.a(bar.o), $% means bar.o, $@means foo.a.

$<

The filename of the first prerequisite.

$?

(all new) The names of all prerequisites that are newer than the target, separated by spaces.

$^

(all) The filenames of all the prerequisites, separated by spaces. This list has duplicate filenames removed since for most uses, such as compiling, copying, etc., duplicates are not wanted.

$+

(all+d) The filenames of all the prerequisites, separated by spaces. $+ includes duplicates, this variable was created for specific situations such as arguments to linkers where duplicate values have meaning.

$*

The stem of the target filename. A stem is typically a filename without its suffix.

D

Append a "D" to the symbol indicate return only the directory portion of the value. Ex: $(@D), $(<D)…

F

Append a "F" to the symbol indicate return only the file portion of the value. Ex: $(@F), $(<F)…

  • A makefle:
count_words: count_words.o lexer.o –lfl
           gcc $^ -lfl -o $@
count_words.o: count_words.c
           gcc -c $@
lexer.o: lexer.c
           gcc -c $@
lexer.c: lexer.l
           flex -t $< > $@
2.3 Finding Files with VPATH and vpath
  • The VPATH variable consists of a list of directories to search when make needs a file.
  • The list will be searched for targets as well as prerequisites, but not for files mentioned in command scripts.
  • The list of directories can be separated by spaces or colons on Unix and separated by spaces or semicolons on Windows.
  • If a file of the same name exists in multiple places in the VPATH list, make grabs the first one.
  • The vpath directive is a more precise way to achieve our goals.
vpath pattern directory-list
  • make will search .c files in the src directory and .h files in the include directory.
vpath %.c src
vpath %.h include
2.4 Pattern Rules
  • Compilation conventions allow make to simplify rule creation by recognizing common filename patterns and providing built-in rules for processing them.
VPATH    = src include
CPPFLAGS = -I include
count_words: counter.o lexer.o –lfl
count_words.o: counter.h
counter.o: counter.h lexer.h
lexer.o: lexer.h
  • The built-in rules are all instances of pattern rules. A pattern rule looks like the normal rules except the stem of the file (the portion before the suffix) is represented by a % character.
  • This makefile works because of three built-in rules.
    • %.o: %.c      /* how to compile a .o file from a .c file */
$(COMPILE.c) $(OUTPUT_OPTION) $<
    • %.c: %.l       /* how to make a .c file from a .l file */
@$(RM) $@
$(LEX.l) $< > $@
    • %: %.c         /* generate a file with no suffix (always an executable) from a .c file */
$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@
  • the output is:
gcc  -I include  -c -o count_words.o src/count_words.c
gcc  -I include  -c -o counter.o src/counter.c
flex  -t src/lexer.l > lexer.c
gcc  -I include  -c -o lexer.o lexer.c
gcc   count_words.o counter.o lexer.o /lib/libfl.a   -o count_words
rm lexer.c
  • make identifies four prerequisites: count_words.o (this prerequisite is missing from the makefile, but is provided by the implicit rule), counter.o, lexer.o, and -lfl. It then tries to update each prerequisite in turn.
  • When make examines the first prerequisite, count_words.o, make finds no explicit rule for it but discovers the implicit rule. Since src/count_words.c has no prerequisites, make is free to update count_words.o so it runs the commands for the implicit rule.
  • counter.o is similar.
  • When make considers lexer.o, it cannot find a corresponding source file (even in src) so it assumes this (nonexistent source) is an intermediate file and looks for a way to make lexer.c from some other source file. It discovers a rule to create a .c file from a .l file and notices that lexer.l exists, so it moves on to the command for updating lexer.c.
  • make examines the library specification –lfl and discovers /lib/libfl.a.
  • Lastly, make realizes it created an intermediate file (lexer.c) that is not necessary to keep so it cleans it up.
  • Using sequences of rules like this to update a target is called rule chaining.
  • The built-in rules can be customized by changing the values of the variables in the command scripts.
  • The percent character (%) in a pattern rule is roughly equivalent to * in a Unix shell. It represents any number of any characters.
  • The percent character can be placed anywhere within the pattern but can occur only once.
  • A pattern can contain a prefix or a suffix or both.
  • Some pattern rules:
%: %.mod
           $(COMPILE.mod) -o $@ -e $@ $^
%: %.cpp
 
          $(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@
%: %.sh
           cat $< >$@
           chmod a+x $@
  • A static pattern rule is one that applies only to a specific list of targets.
$(OBJECTS): %.o: %c
           $(CC) -c $(CFLAGS) $< -o $@
  • The only difference between this rule and an ordinary pattern rule is the initial $(OBJECTS): specification. This limits the rule to the files listed in the $(OBJECTS) variable.
  • Use static pattern rules whenever it is easier to list the target files explicitly than to identify them by a suffix or other pattern.
  • Suffix rules are the original (and obsolete) way of defining implicit rules.
  • Suffix rules consist of one (single-suffix rules) or two (double-suffix rule) suffixes concatenated and used as a target, the prerequisite suffix comes first and the target suffix second:
.c.o:   /* %.o: %.c */
           $(COMPILE.c) $(OUTPUT_OPTION) $<
.p:      /* %: %.p */
           $(LINK.p) $^ $(LOADLIBES) $(LDLIBS) -o $@
  • The suffix rule is recognized by make only if the two suffixes are in a list of known suffixes. A special target, .SUFFIXES, is used to set the list of known suffixes.
.SUFFIXES: .out .a .ln .o .c .cc .C .cpp .p .f .F .r .y .l
2.5 The Implicit Rules Database
  • GNU make 3.80 has about 90 built-in implicit rules. An implicit rule is either a pattern rule or a suffix rule.
  • The built-in implicit rules are applied whenever a target is being considered and there is no explicit rule to update it.
  • The files generated by chaining rules are called intermediate files and are treated specially by make, make creates intermediate files itself as a side effect of updating a target, make will delete the intermediates before exiting.
  • The built-in rules have a standard structure intended to make them easily customizable.
%.o: %.c
           $(COMPILE.c) $(OUTPUT_OPTION) $<
  • COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) –c
  • CC = gcc
  • OUTPUT_OPTION = -o $@
  • The C compiler itself can be changed by altering the value of the CC variable. The other variables are used for setting compilation options (CFLAGS), preprocessor options (CPPFLAGS), and architecture-specific options (TARGET_ARCH).
  • Instead of using simple assignment, consider redefining the compilation variable to include variables or using append-style assignment:
    • For instance, given this assignment in a makefile:
CPPFLAGS = -I project/include
    • If the user add a CPP define to the command line, variables set on the command line override all other assignments to the variable.
$ make CPPFLAGS=-DDEBUG
    • Redefine the compilation variable:
COMPILE.c = $(CC) $(CFLAGS) $(INCLUDES) $(CPPFLAGS) $(TARGET_ARCH) –c
INCLUDES = -I project/include
  • A Simple Help Command
# help - The default goal
.PHONY: help
help:
           $(MAKE) --print-data-base --question |                \
           $(AWK) '/^[^.%][-A-Za-z0-9_]*:/                               \
                     { print substr($$1, 1, length($$1)-1) }' |       \
           $(SORT) |                                                                    \
           $(PR) --omit-pagination --width=80 --columns=4
2.6 Special Targets
  • A special target is a built-in phony target used to change make's default behavior.
  • These special targets follow the syntax of normal targets, that is target: prerequisite, but the target is not a file or even a normal phony.They are really more like directives for modifying make's internal algorithms.
  • There are twelve special targets. They fall into three categories:
    • alter the behavior of make when updating a target
    • act simply as global flags to make and ignore their targets
    • the .SUFFIXES special target is used when specifying old-fashioned suffix rules
  • Some useful target modifiers:
    • .PHONY: declares that its prerequisite does not refer to an actual file and should always be considered out of date.
    • .INTERMEDIATE: Prerequisites of this special target are treated as intermediate files. If make creates the file while updating another target, the file will be deleted automatically when make exits. If the file already exists when make considers updating the file, the file will not be deleted.
    • .SECONDARY: Prerequisites of this special target are treated as intermediate files but are never automatically deleted. The most common use of .SECONDARY is to mark object files stored in libraries.
    • .PRECIOUS: When make is interrupted during execution, it may delete the target file it is updating if the file was modified since make started. If you mark the file as precious, make will never delete the file if interrupted.
    • .DELETE_ON_ERROR: This is sort of the opposite of .PRECIOUS. Marking a target as .DELETE_ON_ERROR says that make should delete the target if any of the commands associated with the rule generates an error. make normally only deletes the target if it is interrupted by a signal.
2.7 Automatic Dependency Generation
  • The trick is to write a makefile target whose action runs gcc over all your source with the -M option, saves the results in a dependency file, add an include directive to the make program, and then re-runs make including the generated dependency file in the makefile so it can trigger the updates we need. Something like:
depend: count_words.c lexer.c counter.c
           $(CC) -M $(CPPFLAGS) $^ > $@
include depend
  • If we generated each source file's dependencies into its own dependency file with, say, a .d suffix and added the .d file itself as a target to this dependency rule, then make could know that the .d needed to be updated (along with the object file) when the source file changed:
counter.o counter.d: src/counter.c include/counter.h include/lexer.h
  • Now, for the cool feature. make will treat any file named in an include directive as a target to be updated. So, when we mention the .d files we want to include, make will automatically try to create these files as it reads the makefile.
VPATH    = src include
CPPFLAGS = -I include
SOURCES  = count_words.c \
                                lexer.c       \
                                counter.c
count_words: counter.o lexer.o –lfl
count_words.o: counter.h
counter.o: counter.h lexer.h
lexer.o: lexer.h
include $(subst .c,.d,$(SOURCES))
%.d: %.c
           $(CC) -M $(CPPFLAGS) $< > $@.$$$$;                         \          /* In the shell, the variable $$ returns the process number of the currently running shell */
           sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \          /* target.o -> .o target.d */
           rm -f $@.$$$$
  • The include directive should always be placed after the hand-written dependencies so that the default goal is not hijacked by some dependency file.
2.8 Managing Libraries
  • An archive library, usually called simply a library or archive, is a special type of file containing other files called members.
  • Archives are created and modified with the ar program.
$ ar rv libcounter.a counter.o lexer.o           /* ar options archive-name obj-file-lists, r: replace, v: verbosely */
  • makefile:
VPATH                   = src include
CPPFLAGS           = -I include
count_words: libcounter.a /lib/libfl.a
libcounter.a: libcounter.a(lexer.o) libcounter.a(counter.o)
libcounter.a(lexer.o): lexer.o                         /* These four lines can be replaced with built-in rules */
           $(AR) $(ARFLAGS) $@ $<                /* %@: libcounter.a */
libcounter.a(counter.o): counter.o
           $(AR) $(ARFLAGS) $@ $<
count_words.o: counter.h
counter.o: counter.h lexer.h
lexer.o: lexer.h
  • When libraries appear as prerequisites, they can be referenced using either a standard filename or with the -l syntax.
  • When the -l form is used in a prerequisite, make will search for the library (preferring a shared library) and substitute its value, as an absolute path, into the $^ and $? variables.
  • The pattern for recognizing libraries from the -l format is stored in .LIBPATTERNS and can be customized for other library filename formats.
  • If a makefile specifies a library file target, it cannot use the -l option for that file in a prerequisite. make does not expand -l option and search for a target, but instead does a straight library search. So for libraries built within the makefile, the filename form must be used.
  • When the prerequisites of a target are saved in the $^ and $? variables, their order is preserved.
  • A closely related problem is mutual reference between libraries, often referred to as circular references or circularities (Ex: B: -lA –lB -lA), but automatic variables normally discard duplicates. So use $+ instead of $^ or $? to preserve the duplicate the prerequisites.
  • Double-colon rules are an obscure feature that allows the same target to be updated with different commands depending on which set of prerequisites are newer than the target.
file-list:: generate-list-script
           chmod +x $<
           generate-list-script $(files) > file-list
file-list:: $(files)
           generate-list-script $(files) > file-list
  • Normally, when a target appears more than once all the prerequisites are appended in a long list with only one command script to perform the update.
  • With double-colon rules, each occurrence of the target is considered a completely separate entity and is handled individually.
  • This means that for a particular target, all the rules must be of the same type, either they are all double-colon rules or all single-colon rules.
arrow
arrow
    全站熱搜

    nix 發表在 痞客邦 留言(0) 人氣()