Command script processing occurs in four steps: read the code, expand variables , evaluate make expressions, and execute commands. Let's see how these steps apply to a complex command script. Consider this (somewhat contrived) makefile . An application is linked, then optionally stripped of symbols and compressed using the upx executable packer: # $(call strip-program, file) define strip-program strip endef complex_script: $(CC) $^ -o $@ ifdef STRIP $(call strip-program, $@) endif $(if $(PACK), upx --best $@) $(warning Final size: $(shell ls -s $@)) The evaluation of command scripts is deferred until they are executed, but ifdef directives are processed immediately wherever they occur. Therefore, make reads the command script, ignoring the content and storing each line until it gets to the line ifdef STRIP . It evaluates the test and, if STRIP is not defined, make reads and discards all the text up to and including the closing endif . make then continues reading and storing the rest of the script. When a command script is to be executed, make first scans the script for make constructs that need to be expanded or evaluated. When macros are expanded, a leading tab is prepended to each line. Expanding and evaluating before any commands are executed can lead to an unexpected execution order if you aren't prepared for it. In our example, the last line of the script is wrong. The shell and warning commands are executed before linking the application. Therefore, the ls command will be executed before the file it is examining has been updated. This explains the "out of order" output seen earlier in Section 5.1. Also, notice that the ifdef STRIP line is evaluated while reading the file, but the $(if...) line is evaluated immediately before the commands for complex_script are executed. Using the if function is more flexible since there are more opportunities to control when the variable is defined, but it is not very well suited for managing large blocks of text. As this example shows, it is important to always attend to what program is evaluating an expression (e.g., make or the shell) and when the evaluation is performed: $(LINK.c) $(shell find $(if $(ALL),$(wildcard core ext*),core) -name '*.o') This convoluted command script attempts to link a set of object files. The sequence of evaluation and the program performing the operation (in parentheses) is: -
Expand $ALL ( make ). -
Evaluate if ( make ). -
Evaluate the wildcard , assuming ALL is not empty ( make ). -
Evaluate the shell ( make ). -
Execute the find ( sh ). -
After completing the expansion and evaluation of the make constructs, execute the link command ( sh ). |