Noeud:Command Prefixes, Noeud « Up »:Shell Commands



Command Prefixes

This is what happens when the install-HEADERS target from example 5.16 is invoked:

     $ make install-HEADERS
     mkdir /usr/local/include
     mkdir: cannot make directory `/usr/local/include': File exists
     make: *** [install-HEADERS] Error 1
     

Not exactly what we wanted, yet we do need to keep the mkdir command line in the rule incase the person who installs the project really doesn't have a /usr/local/include directory. Discarding the error message is easy enough, we simply redirect it to /dev/null. The real problem is that when Make encounters an error in the command sequence, it stops processing and prints a message like the one above.

Make provides a facility to override this behaviour, and thus continue normal processing and ignore any error from the shell command. By prefixing the command with a - character as follows, we tell make that it doesn't matter if that command fails:

     install-HEADERS: $(include_HEADERS)
             -mkdir $(includedir) 2>/dev/null
             for p in $(include_HEADERS); do \
               cp $$p $(includedir)/$$p; \
             done
     
     Example 5.17: Ignoring errors from shell commands in Make rules
     

Things progress as expected now:

     $ make install-HEADERS
     mkdir /usr/local/include
     make: [install-HEADERS] Error 1 (ignored)
     for p in m4module.h error.h hash.h system.h; do \
       cp $p /usr/local/include/$p; \
     done
     

Notice that the $$p references from the Makefile commands have been passed to the shell as $p as explained in the last section.

As long as it doesn't encounter any errors in the commands as it processes them (except for - prefix ignored errors), the normal behaviour of Make is to echo each line of the command to your display just before passing it to the shell. As this happens, Make variables are replaced by their current value, as evidenced by the list of files in the for loop above where $(include_HEADERS) was written in the Makefile. Although double $$ sequences are replaced by a single $, the command itself along with any shell variable references are then simply copied to your display before the shell processes it.

There is another prefix that you can add to a command line to suppress echoing. By inserting a @ at the start of a command, you tell Make to pass that command directly to the shell for processing. We can take advantage of this feature to clarify what the shell is doing in the commands of our install-HEADERS rule:

     install-HEADERS: $(include_HEADERS)
             @test -d $(includedir) || \
               { echo mkdir $(includedir); mkdir $(includedir); }
             @for p in $(include_HEADERS); do \
               echo cp $$p $(includedir)/$$p; \
               cp $$p $(includedir)/$$p; \
             done
     
     Example 5.18: Suppressing echoing of shell commands in Make rules
     

There are still two commands in this rule, but now we have turned off echoing from Make with the @ prefix, and instead use the shell echo command to report what is being done:

     $ make install-HEADERS
     cp m4module.h /usr/local/include/m4module.h
     cp error.h /usr/local/include/error.h
     cp hash.h /usr/local/include/hash.h
     cp system.h /usr/local/include/system.h
     

This time, we tested for the existence of a $(includedir), and created it only if it was missing. In this example the directory was already present, so the second clause of the first command in example 5.18 didn't trigger.

Because of the @ prefix on the first command in the rule, no command is echoed by make. We control when something is displayed, and echo the command only when it is executed. Much better.

Also, rather than letting Make echo the for loop code directly, we have the shell individually report each file that it copies, which makes the output of the command reflect exactly what is going on, and makes it easier for your users to see what is happening when that rule is invoked.