Looping Through API Calls in a Makefile
Learn how to efficiently handle repetitive tasks in Makefiles by looping through API calls with parameter arrays. This guide demonstrates refactoring techniques to avoid command duplication and ensure smooth execution.
When working with Makefiles, you might encounter scenarios where you need to perform repetitive tasks, such as calling an API to list information from a server. Instead of duplicating code, it’s more efficient to extract a function and loop through an array of parameters. Let’s walk through an example and address a common issue that arises.
Initial Task
Consider the following task in a Makefile, which fetches data files for app1
and app2
:
list.data.files:
@echo "Data files for app1"
@curl https://example.com/file/list?app=app1
@echo "Data files for app2"
@curl https://example.com/file/list?app=app2
Refactoring with a Function
To avoid code duplication, we can define a function and use an array to loop through the apps:
APPS = app1 app2
define list_data_files
@echo "Data files for $(1)"
@curl https://example.com/file/list?app=$(1)
endef
list.data.files:
@$(foreach app,$(APPS),$(call list_data_files,$(app));)
Encountering Issues
However, running the above code results in an error:
/bin/sh: @echo: command not found
make: *** [list.data.files] Error 127
The first app executes correctly, but the second one fails. Further simplification to isolate the issue shows the same problem:
APPS = app1 app2
define list_data_files
@echo "Data files for $(1)"
endef
list.data.files:
@$(foreach app,$(APPS),$(call list_data_files,$(app));)
Output:
Data files for app1
/bin/sh: @echo: command not found
make: *** [list.data.files] Error 127
Removing the @
symbol before the command in the function leads to the same issue with curl
:
APPS = app1 app2
define list_data_files
@curl https://example.com/file/list?app=$(1)
endef
list.data.files:
@$(foreach app,$(APPS),$(call list_data_files,$(app));)
Output:
...
/bin/sh: @curl: command not found
make: *** [list.data.files] Error 127
Solution
The solution is to remove the @
symbol before the commands in the function. Here’s the final, working Makefile:
APPS = app1 app2
define list_data_files
echo "Data files for $(1)"
curl https://example.com/file/list?app=$(1)
endef
list.data.files:
@$(foreach app,$(APPS),$(call list_data_files,$(app));)
With this approach, the commands won't be printed when executed in the function, achieving the desired behavior without errors.