Issue
Problem Statement
I have a makefile that I'm attempting to make a helper function for. I want to be able to call the defined helper function but when I'm looking at the text expansion for the targets they're coming out as an empty string.
I'm looking to figure out where I'm messing up my Makefile syntax to cause the empty expansion.
Makefile
# Helper function to instantiate templates.
define render_template
$(eval $@_HOST_URI = "[email protected]:company-name")
$(eval $@_TEMPLATE_NAME = $(1))
$(eval $@_TEMPLATE_DIR = $(1)s)
$(eval $@_NAME = $(2))
cd ${$@_TEMPLATE_DIR}; cookiecutter --no-input [email protected]:company-name/terraform-${$@_TEMPLATE_NAME}.git ${$@_TEMPLATE_NAME}_name=${$@_NAME};
endef
# TODO: Add steps to build a Python3 venv and use it to build the local template.
.PHONY: new-service
new-service: ## Accesses Docker Shell (advanced)
if [ -z "$(SERVICE_NAME)" ]; then \
echo "SERVICE_NAME is a required parameter. Example usage 'make new SERVICE_NAME=vpc-networking'"; \
else \
$(call render_template,"service",${SERVICE_NAME}) \
fi
Error Output
% make new-service SERVICE_NAME=example
if [ -z "example" ]; then \
echo "SERVICE_NAME is a required parameter. Example usage 'make new SERVICE_NAME=vpc-networking'"; \
else \
\
fi
/bin/bash: -c: line 0: syntax error near unexpected token `fi'
/bin/bash: -c: line 0: `if [ -z "example" ]; then echo "SERVICE_NAME is a required parameter. Example usage 'make new SERVICE_NAME=vpc-networking'"; else fi'
make: *** [new-service] Error 2
Solution
IMO it's a very bad idea to do things like this. But I will explain what's happening so you understand it.
When a define
is created, all the newlines are preserved inside the variable; that's what makes a define
different from a normal variable assignment where backslash-newlines are removed.
When a variable is expanded in a recipe context, all the newlines are preserved just as if you'd typed them by hand. That's what allows you to put a complete recipe with multiple commands inside a define
variable then use it in a recipe and it expands to multiple commands.
In your situation, the eval
functions expand to the empty string after assigning the variables, so the expanded value of the call
function will be something like:
define render_template
cd "service"s; cookiecutter --no-input [email protected]:company-name/terraform-"service".git "service"_name=;
endef
The newlines here put the cd
on its own line, not inside the same if-statement. So make will run it AFTER the if-statement, but since the if-statement fails it never gets the chance.
If you add backslashes to ensure the entire define
is on line line, like this:
define render_template
$(eval $@_HOST_URI = "[email protected]:company-name") \
$(eval $@_TEMPLATE_NAME = $(1)) \
$(eval $@_TEMPLATE_DIR = $(1)s) \
$(eval $@_NAME = $(2)) \
cd ${$@_TEMPLATE_DIR}; cookiecutter --no-input [email protected]:company-name/terraform-${$@_TEMPLATE_NAME}.git ${$@_TEMPLATE_NAME}_name=${$@_NAME};
endef
then it will work. Or you could use simple variable assignment:
render_template = \
$(eval $@_HOST_URI = "[email protected]:company-name") \
$(eval $@_TEMPLATE_NAME = $(1)) \
$(eval $@_TEMPLATE_DIR = $(1)s) \
$(eval $@_NAME = $(2)) \
cd ${$@_TEMPLATE_DIR}; cookiecutter --no-input [email protected]:company-name/terraform-${$@_TEMPLATE_NAME}.git ${$@_TEMPLATE_NAME}_name=${$@_NAME};
Answered By - MadScientist Answer Checked By - David Marino (WPSolving Volunteer)