Reputation: 20870
I use Makefile to control workflow dependency, and the following code causes 'argument too long' error. What would be a good workaround? I am using 3.82 BTW.
make: execvp: /bin/sh: Argument list too long
Here the logic is to merge all the prerequisites if they are all done (each prerequisite file would have a .done
sentinel file if it is done/ready).
contains=$(foreach v,$2,$(if $(findstring $1,$v),$v,))
.SECONDEXPANSION:
%.merged: $$(call contains,_$$*_,$$(STEP1s))
if (for x in $^; do [ -f $$x.done ] || exit; done); then \
for f in $^; do cat $$f; echo; done > $@; fi
EDIT: The following recipe doesn't trigger 'argument too long' error.
for x in $^;do echo $$x;done
Upvotes: 0
Views: 3453
Reputation: 20870
Thank you very much for pointing me to the right direction.
I think the problem is that after recipe expansion, if (for x in $^; do [ -f $$x.done ] || exit; done);
becomes too long
I ended up with this solution, which is somewhat cheating. Here I ignore the prerequisites, and re-compute the prerequisites' sentinels. Then compare their count to an expected value
contains=$(foreach v,$2,$(if $(findstring $1,$v),$v,))
.SECONDEXPANSION:
%.merged: $$(call contains,_$$*_,$$(STEP1s))
if [ $(words $(wildcard *_$(basename $@)_*.txt.done)) == $(n_input) ]; then\
echo -n $^| xargs -I{} -d' ' sh -c "cat {}; echo " > $@;fi
Upvotes: 0
Reputation: 29250
Edit: From the various comments it seems that it is the recipe length itself that causes the error, not the length of the arguments of the for
loops. A way to break it in much smaller pieces would be to introduce intermediate targets, forbid parallel execution, and run make twice, a first time to decide if the target shall be rebuilt and the second time to build it with as many different recipes as there are files to concatenate (not tested):
.NOTPARALLEL:
contains=$(foreach v,$2,$(if $(findstring $1,$v),$v$3,))
.SECONDEXPANSION:
ifneq ($(STEM),)
.PHONY: $$(call contains,_$$(STEM)_,$$(STEP1s),.cat)
$(STEM).merged: $$(call contains,_$$(STEM)_,$$(STEP1s),.cat)
%.cat:
cat $* >> $(STEM).merged
echo >> $(STEM).merged
else
%.merged: $$(call contains,_$$*_,$$(STEP1s),) $$(call contains,_$$*_,$$(STEP1s),.done)
$(MAKE) $@ STEM=$*
endif
First version: Apparently $^
is too large for the shell. Here is a solution that uses make macros instead of shell loops and that declares the .done
files as regular prerequisites instead of testing their existence in the recipe:
contains=$(foreach v,$2,$(if $(findstring $1,$v),$v $v.done,))
.SECONDEXPANSION:
%.merged: $$(call contains,_$$*_,$$(STEP1s))
$(foreach f,$(filter-out,%.done,$^),cat "$f" >> "$@"; echo >> "$@";)
Upvotes: 1
Reputation: 100926
I don't think Renaud's suggestion meets the stated requirements, which as I understand it is the output is only generated if ALL the prerequisites have a .done
file. Also the shell script generated is much longer than needed.
Maybe this will work:
contains=$(foreach v,$2,$(if $(findstring $1,$v),$v $v.done,))
.SECONDEXPANSION:
%.merged: $$(call contains,_$$*_,$$(STEP1s))
$(if $(foreach $F,$^,$(if $(wildcard $F.done),,x)),, \
for fn in $^; do (cat "$$fn"; echo) >> "$@"; done)
You might be able to make this more efficient by avoiding the foreach loop; if you can require GNU make 4.4 you can use intcmp
to compare the lengths:
$(if $(intcmp $(words $^),$(words $(wildcard $(addsuffix .done,$^)))),, \
...dostuff...)
If not you can probably do the same thing with filtering:
$(if $(filter $(words $^),$(words $(wildcard $(addsuffix .done,$^)))), \
...dostuff...)
Note, none of this is tested.
Upvotes: 1