I've searched and Googled a lot, but can't find an answer. If this is not possible then just let me know, and if you can give me more context about "why" I'd appreciate it.
I inherited a makefile which is making the same check in many different make-tasks, but tries to DRY out the logic by moving the body of the check itself into a single upstream make-task, that the other tasks depend on. Basically this:
DEV_FILE_PRESENT:=false
setup:
@if [ -f ./dev_file.json ]; then \
DEV_FILE_PRESENT=true; \
echo "found dev json file"; \
fi;
check-overwrite: setup
@if [ DEV_FILE_PRESENT ]; then \
echo "Existing dev files- overwrite? [Y/n]" && read ans && [ $${ans:-Y} = Y ]; \
fi;
preview: setup
@if [ ! DEV_FILE_PRESENT ]; then \
echo "Can't preview- missing dev file"; \
else \
echo "placeholder for moar bash scripting here"; \
fi;
This doesn't work: The global variable DEV_FILE_PRESENT
seems to revert to "false"
as soon as the setup
task is complete and has been exited. I think that's because "every sub-command is run in its own shell" (From
this StackOverflow answer: https://stackoverflow.com/a/29085684/1358187)
Much googling and searching later I settled on this "fix" - removing any attempt to DRY the body of the if-clause itself, and instead duplicating it across tasks:
check-overwrite:
@if [ -f ./dev_file.json ]; then \
echo "Existing dev files- overwrite? [Y/n]" && read ans && [ $${ans:-Y} = Y ]; \
fi;
preview:
@if [ ! -f ./dev_file.json ]; then \
echo "Can't preview- missing dev file"; \
else \
echo "placeholder for moar bash scripting here"; \
fi;
Are there any other solutions here? Is there any way to set a variable in one make-task, and reference it in another make-task?
CodePudding user response:
Issues with the original code
There are several problems here:
- You are creating a bash variable, which, exactly as you say, lives only in the shell when you define it.
- When you use the variable, you just use
DEV_FILE_PRESENT
: that's a constant string, not a reference to that variable.
One possible solution
Your solution is fine, another solution would be to use a make variable instead of a bash variable.
Here's a working example:
DEV_FILE_NAME=./dev_file.json
ifeq ($(wildcard $(DEV_FILE_NAME)), $(DEV_FILE_NAME))
DEV_FILE_PRESENT=yes
$(info Found dev json file)
else
DEV_FILE_PRESENT=no
endif
$(info DEV_FILE_PRESENT $(DEV_FILE_PRESENT))
check-overwrite:
@if [ "$(DEV_FILE_PRESENT)" = yes ]; then \
echo "Existing dev files- overwrite? [Y/n]" && read ans && [ $${ans:-Y} = Y ]; \
fi;
preview:
@if [ "$(DEV_FILE_PRESENT)" = no ]; then \
echo "Can't preview- missing dev file"; \
else \
echo "placeholder for more bash scripting here"; \
fi;
Notes:
- Tested with GNU Make 3.81
- The builtin
$(wildcard <arg>)
function in make is a bit different from regular wildcard expansion: here I'm taking advantage of the fact that when a pattern does not match any existing file, the wildcard is "expanded" to the empty string, even if there are no wildcard characters in the expression. - Using a make variable requires the syntax
$(VAR_NAME)
- Unless you place
SHELL=/bin/bash
in your Makefile, that's allsh
scripting in there, not Bash scripting. It won't always behave like you expect! - The
$(info DEV_FILE_PRESENT ...)
line is just for debugging.
Now, because of where I'm defining DEV_FILE_PRESENT
, the test is always going to run when the Makefile is loaded, whether you are asking for those targets to be run or not.
My personal style preference
Frankly, I actually like your own solution better: it's much simpler, and much easier to read. If you were using an expensive test, factoring it out might be worth the code complexity, but for such a trivial test repeating yourself makes more sense to me.
I'd only change one thing: use a variable to hold the name of the dev file, so that if that name changes in the future, you'll only have to make the change in one place:
DEV_FILE_NAME=./dev_file.json
check-overwrite:
@if [ -f "$(DEV_FILE_NAME)" ]; then \
echo "Existing dev files- overwrite? [Y/n]" && read ans && [ $${ans:-Y} = Y ]; \
fi;
preview:
@if [ ! -f "$(DEV_FILE_NAME)" ]; then \
echo "Can't preview- missing dev file"; \
else \
echo "placeholder for moar bash scripting here"; \
fi;