1. Overview
The make utility automatically determines which pieces of a large program need to be recompiled, and issues commands to recompile them.
We need a file called a makefile to tell make how to compile and link a program.
A makefile is executed when we type "make" in command line, if we has more than one makefile in current directory, we can use "-f name" option to select the makefile to be executed.
A make file contains:
A makefile is executed when we type "make" in command line, if we has more than one makefile in current directory, we can use "-f name" option to select the makefile to be executed.
A make file contains:
- Rules
- Variables
- Directives
- Comments
2. Rules in makefile
2.1 Shape of a rule
Fig. 2.1. The rule shape
Fig. 2.1 show a rule that consists of:
- A target: is usually the name of a file that if generated by a program, or the name of an action to carry out (such as "clean")
- A prerequisite: is a file that is used as input to create the a\target. A target often depends on several files.
- A recipe is an action that make carries out. A recipe may have more than one command, the recipe lines start with a tab character (which can be modified by .RECIPEPREFIX variable)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| edit : main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o main.o : main.c defs.h cc -c main.c kbd.o : kbd.c defs.h command.h cc -c kbd.c command.o : command.c defs.h command.h cc -c command.c display.o : display.c defs.h buffer.h cc -c display.c insert.o : insert.c defs.h buffer.h cc -c insert.c search.o : search.c defs.h buffer.h cc -c search.c files.o : files.c defs.h buffer.h command.h cc -c files.c utils.o : utils.c defs.h cc -c utils.c clean : rm edit main.o kbd.o command.o display.o \ insert.o search.o files.o utils.o |
2.2. Phony target
A target is not always a filename, it can be a action, for example clean target:
1
2
| clean: rm *.o test |
To avoid this problem, we use .PHONY target:
1
2
3
| .PHONY: clean clean: rm *.o test |
2.3. Multiple targets in a Rule
A rule with multiple targets is equivalent to writing many rules, each with one target and all identical aside from that.
For example;
1
2
| test1.o test2.o : test.h @echo $@ depends on $< |
1
2
3
4
| test1.o : test.h @echo test1.o depends on test.h test2.o @echo test2.o depends on test.h |
2.4. Multiple rules for one target
One file can be the target of several rules. All the prerequisites mentioned in all the rules are merged into one list of prerequisites for the target. Of the target is older than any prerequisite from any rule, the recipe is executed.For example:
1
2
3
| foo.o : foo.c bar.o : bar.c foo.o bar.o : def.h |
2.5. Static pattern rules
Static pattern rules are rules which specify multiple targets and construct the prerequisite names for each target based on the target name.Here is the syntax of a static pattern rule:
- The targets list specifies the targets that the rule applies to.
- The target-pattern and preque-patterns say how to compute the prerequisites for each target.
1
2
3
4
| objects = foo.o bar.o all: $(objects) $(objects): %.o: %.c $(CC) -c $(CFLAGS) $< -o $@ |
2.6. Implicit rules
If the rule for recipe is not specified, make will figure out which implicit rule to use based on which kind of source file exists or can be made.For example:
1
2
| test : foo.o bar.o gcc -o test foo.o bar.o |
2.7. Type of prerequisites
There are two different types of prerequisites in GNU make:
- normal prerequisites
- order-only prerequisites
2.8 Double colon rule
If a target appears in different rule and they are double-colon, each of them is independent of the others. Each double-colon rule's recipe is executed if the target is older than any prerequisites of that rule.
For example:
If the foo.o and/or bar.o is updated, the first and/or the second recipe is also executed.
If we modify test.c then make will rebuild test.o, however if we modify file.c then test.o will not be rebuilt.
It is helpful in the following case.
A folder is consider to be modified if a file is added, removed or rename. Some time we dont want to update folder/test.o when folder is modified by adding or removing some other files. To do that we declare folder as a order-only prerequisite.
The output will be:
this is a make file
If the example is written without '@' at the beginning of the recipe, the output will be
echo this is a make file
this is a make file
A solution is using && operator or .ONESHELL target:
test.o :: foo.o @echo this is the first rule test.o :: bar.o @echo this is the second rule
If the foo.o and/or bar.o is updated, the first and/or the second recipe is also executed.
2.7.1. Normal prerequisites
A normal prerequisite makes to statements:
- Impose an order in which recipes will be invoked: the recipes for all prerequisites of a target will be completed before the recipe for the target is run.
- if any prerequisite newer than the target, the target is considered out-of-date and must be rebuilt.
2.7.2. Order-only prerequisites
A order-only prerequisite imposes that the target will not be updated because the timestamp of the order-only prerequisite is changed. We can add prerequisites by using '|' charater. The normal prerequisites are in the left of '|', and the order-only ones are in the right.
For example:
For example:
1
| test.o : test.c | file.c |
If we modify test.c then make will rebuild test.o, however if we modify file.c then test.o will not be rebuilt.
It is helpful in the following case.
1
2
3
4
5
6
7
8
| folder/test.o : test.c | folder touch folder/test.o folder: mkdir folder add: touch folder/addFile.c remove : rm folder/addFile.c |
3. Recipes in rules
Recipes are meant to be interpreted by the shell and so they are written using shell syntax.
3.1. Recipe echoing
Normally make prints each line of the recipe before it is executed (echoing). When a line starts with '@', the echoing of that line is suppressed.
For example:
1
2
| all: @echo this is a make file |
this is a make file
If the example is written without '@' at the beginning of the recipe, the output will be
echo this is a make file
this is a make file
3.2. Recipe execution
When it is time to execute recipes to update a target, they are executed by invoking a new sub-shell for each line of the recipe, unless the .ONESHELL special target is in effect (provided in version 3.82). So if we call ls after cd, the result may be not expected.A solution is using && operator or .ONESHELL target:
1
2
3
4
5
6
7
8
| all: cd folder && ls #or .ONESHELL: all: cd folder ls |
3.3. Errors in Recipes
After each shell invocation returns, make looks at its exit status. If the shell completed successfully (the exit status is zero), the next line in the recipe í executed in a new shell.If there is an error, make gives up on the current rule, and perhaps on all rules. Some time the failure of a certain recipe line does not indicate a problem. For example we may use mkdir command to ensure that a directory exists.
To ignore erors in a recipe line, write a '-' at the beginning of the recipes.
For example:
1
2
| clean: -rm -f *.o |
3.4. Interrupting or Killing make
If make gets a fatal signal while a shell is executing, it may delete the target file that the recipe was supposed to update. The purpose of deleting the target is to make sure that is remade from scratch when make is next run.
4. Variables in makefile
A variable is a name defined in a makefile to represent a string of text. A variable name may be any sequence of characters not containing ':', '#', '=', or white space.
4.1. Basic variable references
To get a variable's value, write a dollar sign followed by the name of the variable in parentheses or braces. Either $(foo) or ${foo} is valid reference to the variable foo.
Variable references can be used in any context: targets, prerequisites, recipes, directives, and new variable values.
Variable references work by strict textual substitution.
For example:
1
2
| foo = c prog.o : prog.$(foo) |
1
| $(foo)$(foo) - $(foo) prog.$(foo) |
4.2. Flavors of Variable
Two ways that a variable in GNU make can have a value.- Recursive expansion
- Simple expansion
4.2.1. Recursive expansion
It is defined by using '=', if it contains references to other variables, these references are expanded whenever this variable is substituted.For example:
1
2
3
4
| foo = $(goo) goo = $(bar) bar = something all:;echo $(foo) |
- Advantage: we can write
1
2
| CFLAGS = $(include_dirs) -O include_dirs = -Ifoo -Ibar |
1
| CFLAGS = $(CFLAGS) -O |
4.2.2. Simple expansion
Simply expanded variables are defined by lines using ':=' or '::=' (both are equivalent). Simply expanded variables do not contain any references to other variables, it contains their values as of the time this variable was defines.Therefore
1
2
3
| x := foo y := $(x) bar x := later |
1
2
| y := foo bar x := later |
4.3. How variables get their values
- Specify an overriding value when running make
- Specify a value in the make file with an assignment or with a verbatim definition
- Variables in the environment become make variables.
- Several automatic variables are given new values for each rule. Each of these has a single conventional use
- Several variable have constant initial values.
4.4. Setting variables.
Beside using '=', ':=' or ':=', if we would like a variable to beset to a value only if it's not already set, then we can use '?=' instead of '='.Following examples are equivalent.
1
| foo ?= bar |
1
2
3
| ifeq ($(origin foo), undefined) foo = bar endif |
We can append more text into variable by using '+=' operator.
For example:
variable := value
variable += more
is equivalent to
variable := value
variable := $(variable) more
4.5. Automatic variables
- $@: Name of whichever target caused the rule's command to be run
- $%: If target name is archive,
- $<: The name of the first prerequisite
- $?: The names of all the prerequisites that newer than the target.
- $^: The name of all the prerequisites.
- $+: This is like '$^', but prerequisites listed more than once are duplicated in order they were listed in makefile
- $*: The name of the dependency, but except extension (like .c, .o)
- $|: The names of all the order-only prerequisites.
5. Function call syntax
A function call resembles a variable reference. A function call looks like this:
1
| $(function argument) |
1
2
3
| foo = a.o b.o c.o bar := $(subst .o,.c,$(foo)) #bar is now a.c b.c c.c |
The subst function replace .o pattern by .c pattern in $(foo).
6. Practice
We have 3 files: hellomake.c, hellofunc.c, and hellomake.h as follows
1
2
3
4
5
6
7
8
9
| //hellomake.c #include "hellomake.h" int main() { // call a function in another file myPrintHelloMake(); return (0); } |
1
2
3
4
5
6
7
8
9
10
11
| //hellofunc.c #include <stdio .h=""> #include <hellomake .h=""> void myPrintHelloMake( void ) { printf ( "Hello makefiles!\n" ); return ; } </hellomake></stdio> |
1
2
3
4
5
6
| //hellomake.h /* example include file */ void myPrintHelloMake( void ); |
1
2
| hellomake: hellomake.c hellofunc.c gcc -o hellomake hellomake.c hellofunc.c -I. |
6.2. Makefile2
1
2
3
4
5
| CC=gcc CFLAGS=-I. hellomake: hellomake.o hellofunc.o $(CC) -o hellomake hellomake.o hellofunc.o $(CFLAGS) |
This is the most small scale projects. However, the is one thing missing: dependency on the include files, make would not recompile the .c file even though they needed to be.
6.3. Makefile 3
1
2
3
4
5
6
7
8
9
| CC=gcc CFLAGS=-I. DEPS = hellomake.h %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) hellomake: hellomake.o hellofunc.o gcc -o hellomake hellomake.o hellofunc.o $(CFLAGS) |
6.4. Makefile 4
If we want to use $^ and $@ macros, we define a variable OJB as in makefile 4.
1
2
3
4
5
6
7
8
9
10
| CC=gcc CFLAGS=-I. DEPS = hellomake.h OBJ = hellomake.o hellofunc.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) hellomake: $(OBJ) gcc -o $@ $^ $(CFLAGS) |
If we want our project is well-organized.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| CC=gcc CFLAGS=-I$(IDIR) IDIR=../include BINDIR=../bin ODIR=../obj SRCDIR=../src _DEPS=hellomake.h DEPS=$(patsubst %,$(IDIR)/%,$(_DEPS)) _OBJS=hellomake.o hellofunc.o OBJS=$(patsubst %,$(ODIR)/%,$(_OBJS)) $(BINDIR)/hellomake : $(OBJS) $(CC) -o $@ $^ $(CFLAGS) $(OBJS): | $(ODIR) $(BINDIR) $(ODIR): -mkdir $(ODIR) $(BINDIR): -mkdir $(BINDIR) $(ODIR)/%.o : $(SRCDIR)/%.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) .PHONY : clean clean: -rm -rf $(ODIR) $(BINDIR) |
References
http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/
GNU make
Không có nhận xét nào:
Đăng nhận xét