Thursday, April 3, 2014

make tips

VPATH: Search Path for All Prerequisites

The value of the make variable VPATH specifies a list of directories that make should search. Most often, the directories are expected to contain prerequisite files that are not in the current directory; however, make uses VPATH as a search list for both prerequisites and targets of rules.

Thus, if a file that is listed as a target or prerequisite does not exist in the current directory, make searches the directories listed in VPATH for a file with that name. If a file is found in one of them, that file may become the prerequisite (see below). Rules may then specify the names of files in the prerequisite list as if they all existed in the current directory. See Writing Recipes with Directory Search.
In the VPATH variable, directory names are separated by colons or blanks. The order in which directories are listed is the order followed by make in its search. (On MS-DOS and MS-Windows, semi-colons are used as separators of directory names in VPATH, since the colon can be used in the pathname itself, after the drive letter.)
For example,
     VPATH = src:../headers
specifies a path containing two directories, src and ../headers, which make searches in that order.
With this value of VPATH, the following rule,
     foo.o : foo.c
is interpreted as if it were written like this:
     foo.o : src/foo.c
assuming the file foo.c does not exist in the current directory but is found in the directory src.

The shell Function

The shell function is unlike any other function other than the wildcard function (see The Function wildcard) in that it communicates with the world outside of make.
The shell function performs the same function that backquotes (‘`’) perform in most shells: it does command expansion. This means that it takes as an argument a shell command and evaluates to the output of the command. The only processing make does on the result is to convert each newline (or carriage-return / newline pair) to a single space. If there is a trailing (carriage-return and) newline it will simply be removed.
The commands run by calls to the shell function are run when the function calls are expanded (see How make Reads a Makefile). Because this function involves spawning a new shell, you should carefully consider the performance implications of using the shell function within recursively expanded variables vs. simply expanded variables (see The Two Flavors of Variables).
Here are some examples of the use of the shell function:
     contents := $(shell cat foo)
sets contents to the contents of the file foo, with a space (rather than a newline) separating each line.
     files := $(shell echo *.c)
sets files to the expansion of ‘*.c’. Unless make is using a very strange shell, this has the same result as ‘$(wildcard *.c)’ (as long as at least one ‘.c’ file exists).

Using One Shell

Sometimes you would prefer that all the lines in the recipe be passed to a single invocation of the shell. There are generally two situations where this is useful: first, it can improve performance in makefiles where recipes consist of many command lines, by avoiding extra processes. Second, you might want newlines to be included in your recipe command (for example perhaps you are using a very different interpreter as your SHELL). If the .ONESHELL special target appears anywhere in the makefile then allrecipe lines for each target will be provided to a single invocation of the shell. Newlines between recipe lines will be preserved. For example:
     .ONESHELL:
     foo : bar/lose
             cd $(@D)
             gobble $(@F) > ../$@
would now work as expected even though the commands are on different recipe lines.
If .ONESHELL is provided, then only the first line of the recipe will be checked for the special prefix characters (‘@’, ‘-’, and ‘+’). Subsequent lines will include the special characters in the recipe line when the SHELL is invoked. If you want your recipe to start with one of these special characters you'll need to arrange for them to not be the first characters on the first line, perhaps by adding a comment or similar. For example, this would be a syntax error in Perl because the first ‘@’ is removed by make:
     .ONESHELL:
     SHELL = /usr/bin/perl
     .SHELLFLAGS = -e
     show :
             @f = qw(a b c);
             print "@f\n";
However, either of these alternatives would work properly:
     .ONESHELL:
     SHELL = /usr/bin/perl
     .SHELLFLAGS = -e
     show :
             # Make sure "@" is not the first character on the first line
             @f = qw(a b c);
             print "@f\n";
or
     .ONESHELL:
     SHELL = /usr/bin/perl
     .SHELLFLAGS = -e
     show :
             my @f = qw(a b c);
             print "@f\n";
As a special feature, if SHELL is determined to be a POSIX-style shell, the special prefix characters in “internal” recipe lines will removed before the recipe is processed. This feature is intended to allow existing makefiles to add the .ONESHELL special target and still run properly without extensive modifications. Since the special prefix characters are not legal at the beginning of a line in a POSIX shell script this is not a loss in functionality. For example, this works as expected:
     .ONESHELL:
     foo : bar/lose
             @cd $(@D)
             @gobble $(@F) > ../$@
Even with this special feature, however, makefiles with .ONESHELL will behave differently in ways that could be noticeable. For example, normally if any line in the recipe fails, that causes the rule to fail and no more recipe lines are processed. Under .ONESHELL a failure of any but the final recipe line will not be noticed by make. You can modify .SHELLFLAGS to add the -e option to the shell which will cause any failure anywhere in the command line to cause the shell to fail, but this could itself cause your recipe to behave differently. Ultimately you may need to harden your recipe lines to allow them to work with .ONESHELL.

Overriding Variables

An argument that contains ‘=’ specifies the value of a variable: ‘v=x’ sets the value of the variable v to x. If you specify a value in this way, all ordinary assignments of the same variable in the makefile are ignored; we say they have been overridden by the command line argument.
The most common way to use this facility is to pass extra flags to compilers. For example, in a properly written makefile, the variable CFLAGS is included in each recipe that runs the C compiler, so a file foo.c would be compiled something like this:
     cc -c $(CFLAGS) foo.c
Thus, whatever value you set for CFLAGS affects each compilation that occurs. The makefile probably specifies the usual value for CFLAGS, like this:
     CFLAGS=-g
Each time you run make, you can override this value if you wish. For example, if you say ‘make CFLAGS='-g -O'’, each C compilation will be done with ‘cc -c -g -O’. (This also illustrates how you can use quoting in the shell to enclose spaces and other special characters in the value of a variable when you override it.)
The variable CFLAGS is only one of many standard variables that exist just so that you can change them this way. See Variables Used by Implicit Rules, for a complete list.
You can also program the makefile to look at additional variables of your own, giving the user the ability to control other aspects of how the makefile works by changing the variables.
When you override a variable with a command line argument, you can define either a recursively-expanded variable or a simply-expanded variable. The examples shown above make a recursively-expanded variable; to make a simply-expanded variable, write ‘:=’ or ‘::=’ instead of ‘=’. But, unless you want to include a variable reference or function call in the value that you specify, it makes no difference which kind of variable you create.
There is one way that the makefile can change a variable that you have overridden. This is to use the override directive, which is a line that looks like this: ‘overridevariable = value’ (see The override Directive).


No comments:

Post a Comment