I am basically looking into a buggy makefile, which confused me with the following scenario.
default:
echo "Build started with $@ at $(ENV_VAR)";\
ENV_VAR=$(NEW_DIR); \
cd $(ENV_VAR); \
$(MAKE);
Objective of the programmer:
Basically the programmer has intended to override the environment variable ENV_VAR with a new value (path to a directory) NEW_DIR and then execute make recursively within that directory.
Bug in this makefile:
The bug that I see is the coder has used “cd $(ENV_VAR)” instead of “cd $$(ENV_VAR)”. So because of this, we haven’t actually used the overriden make variable for “cd” command instead still using the value of environment variable before executing $(MAKE). Also the cd command failed because of non-existing path.Finally we end up doing a recursive make.
Questions I have
As I mentioned in the above paragraph, we are in the scenario of recursive make. Interestingly What I see is when the same target is called for the second time I see the $(ENV_VAR) to reflect the overridden value of NEW_DIR.That’s making me puzzled.
Sample Output:
Assume:
NEW_DIR=/new-dir OLD-DIR=/old-dir
Output:
Build started with default at /old-dir
bin/sh /old-dir: no such file (or) directory
Build started with default at /new-dir
Any suggestions on this please.
You’re right that
$(ENV_VAR)is incorrect. But$$(ENV_VAR)is incorrect too.Make expands the variables in the command, then passes the command to the shell, which does its own expansion. So
becomes a shell command
This command has not yet been executed; the assignment has not yet taken place, and it’s already too late.
The proposed solution won’t work either. This:
becomes this:
This looks good, but notice that
cd $(ENV_VAR);is Make syntax, not shell syntax (at least not bash). The correct shell syntax iscd $ENV_VAR, so the rule should be:According to the manual, “by default, only variables that came from the environment or the command line are passed to recursive invocations” (but you can override this behavior if you choose). Since
ENV_VARis an environmental variable, it will be passed to the sub-make, and if you modify it in the command shell, the sub-make will receive the modified value.Finally, I suggest you try it this way: