Reputation: 1079
I have a Makefile rule in an RStudio project that builds various intermediate and output data files. Some of the key rules look like this:
outdir/<location>/outdata_<location>.file: script.R datadir/<location>/indata_<location>.file
$(EXEC) $< <location>
where location
differentiates directories as well as the filenames of both targets and prerequisites, and at the same time is passed as an argument to script.R
.
Pictorially, this is how, for example, the prerequisites' directories are structured:
datadir
|- location1
| |- indata_location1.file
| |- ...
|
|- location2
| |- indata_location2.file
| |- ...
|
|- location3
Each of these directories have different levels of data files, hence the decision to organise them by location. Also, more project locations are added over time, hence the need for implicit rules to minimize the need for constant tinkering with the Makefile.
I've tried using pattern rules as described in the GNU Make documentation but it says the pattern placeholder can only appear once in either target or the prerequisite(s). I tried using string manipulation and the foreach
functions but have not been able to solve it as my experience with GNU Make is limited.
I've seen similar SO questions on multiple directories but none that refer to use of the differentiating string as an argument in the recipe.
Any help would be appreciated.
Upvotes: 0
Views: 122
Reputation: 99134
You want to use several very similar rules, and the variance is too complicated for a pattern rule. This looks like a job for a "canned recipe".
We write a template:
define data-rule
outdir/$(1)/outdata_$(1).file: script.R datadir/$(1)/indata_$(1).file
$(EXEC) $$< $(1)
endef
(The exact syntax varies with different versions of Make, so you may need '=' at the end of the "define" line.)
We can now generate the text of the location1
rule with call
:
$(call data-rule,location1)
and have Make interpret that text as actual makefile code with eval
:
$(eval $(call data-rule,location1))
Once we verify that this much works, we can generate the rules one by one:
$(eval $(call data-rule,location1))
$(eval $(call data-rule,location2))
$(eval $(call data-rule,location3))
or use foreach
:
LOCATIONS := location1 location2 location3
$(foreach loc,$(LOCATIONS),$(eval $(call data-rule,$(loc))))
Finally, you may want a target that builds all of these files:
all-locations: outdir/location1/outdata_location1.file outdir/location2/outdata_location2.file outdir/location3/outdata_location3.file
You can automate this construction too:
$(foreach loc,$(LOCATIONS),$(eval all-locations: outdir/$(loc)/outdata_$(loc).file))
Upvotes: 1