Suppose you were writing an ordinary rule:
$(DEST_DIR)/foo : $(SOURCE_DIR)/foo
cp $(SOURCE_DIR)/foo $(DEST_DIR)/foo
That works, but the redundancy is troublesome. Sooner or later you’ll change $(DEST_DIR)/foo
in the preq but forget to change it in the rule. And the rule is hard to read. So we put in an automatic variable:
$(DEST_DIR)/foo : $(SOURCE_DIR)/foo
cp $(SOURCE_DIR)/foo $@
When this rule runs, $@
will be expanded to the name of the target, $(DEST_DIR)/foo
. (We can do even better than that, but let’s stop there.)
Now we want to make sure that $(DEST_DIR)
exists before this rule runs, but we don’t want it to be a prerequisite exactly, because the absence of that directory shouldn’t be enough to cause this rule to run. So we make it an order-only prerequisite:
$(DEST_DIR)/foo : $(SOURCE_DIR)/foo | $(DEST_DIR)
cp $(SOURCE_DIR)/foo $@
Now we want many rules like this, for different targets, and instead of doing it the smart way, we’ll use a “canned recipe”, sort of a template for creating rules on the fly.
# This won't work
define KERNEL_RULE
$(SOURCE_DIR)/$(1) : kernel_modules
$(DEST_DIR)/$(1) : $(SOURCE_DIR)/$(1) | $(DEST_DIR)
cp $(SOURCE_DIR)/$(1) $@
endef
The problem is that when we evaluate this definition, $@
will be expanded, and since it isn’t a rule yet, it will expand to nothing. So we change it to $$@
:
# This will work
define KERNEL_RULE
$(SOURCE_DIR)/$(1) : kernel_modules
$(DEST_DIR)/$(1) : $(SOURCE_DIR)/$(1) | $(DEST_DIR)
cp $(SOURCE_DIR)/$(1) $$@
endef
When Make calls this definition, $$@
expands to $@
, then if/when it runs the rule, $@
will expand to the name of the target.