make functions in the prerequisites - makefile

I'm in the process of learning make and am getting stuck on whether it is possible to use functions in the prereq's list for a target. Say I have the following...
FILES = folder1/file folder2/file folder3/file
$(FILES) : code/my_script.bash $(subst folder,dir,$#)
bash code/my_script.bash $(subst folder,dir,$#)
In this case my_script.bash will run, even if folder1/file does not exist. I would anticipate that it would execute another rule to build that target first. When I replace the bash line with echo $^, all that is outputted is code/myscript.bash. It doesn't seem to be doing the replacement on $# to build the prereq. I see now that I can get the desired goal by using % in the target and prereq, but is it possible to use a function in the prereqs?

To use functions that depend on the target of the rule, you need to enable secondary expansion. To enable secondary expansion you need to use .SECONDEXPANSION: besides doubling your $ in the depdnencies. So:
FILES = folder1/file folder2/file folder3/file
all: $(FILES)
.SECONDEXPANSION:
$(FILES): code/my_script.bash $$(subst folder,dir,$$#)
bash code/my_script.bash $(subst folder,dir,$#)
What the doubling of the $ does is prevent the code from being evaluated during the first expansion.
You do not need secondary expansion if the functions you use in your prerequisites do not depend on variable that exist only when the rule is being evaluated (like $#).

FILES = folder1/file folder2/file folder3/file
$(FILES) : code/my_script.bash $$(subst folder,dir,$$#)
bash code/my_script.bash $(subst folder,dir,$#)

Related

How to check a string whether contain a substring in Makefile commands

I want to check whether a string variable contains a specified substring in the Makefile. The purpose is to clean the sub folders.
I used the below code, but it did not work.
SERVICES_LIST = A_Service B_Service C_Service #example
SPECIFIC_SERVICE_LIST = A_Service B_Service
clean:
#list='$(SERVICES_LIST)';for subdir in $$list;do \
echo "clean in $$subdir";\
if [[ "*$$subdir*" == "$(SPECIFIC_SERVICE_LIST)" ]];then\
make $$subdir clean;\
fi;\
done;\
This hasn't much to do with make, because substantially all the logic involved is expressed in the shell language. In particular, you seem to be assuming bash.
The problem is here:
if [[ "*$$subdir*" == "$(SPECIFIC_SERVICE_LIST)" ]];then\
You seem to by trying to match (make's expansion of) "$(SPECIFIC_SERVICE_LIST)" against a glob pattern formed as (make's expansion of) "*$$subdir*". But the left-hand side is quoted, so it is not interpreted as a pattern, and the == operator performs (exact) string matching, not pattern matching.
One of the main ways to apply such pattern-matching tests in the shell language is with a case construct, because the selection expressions used with it are always interpreted as globs. That might look like so in your makefile:
case "$(SPECIFIC_SERVICE_LIST)" in *$$subdir*) make $$subdir clean ;; esac
But the whole thing seems pretty non-idiomatic. Generally speaking, a makefile is tuned to the project. Even if it is dynamically generated in part or in whole, it is reasonable and appropriate to design your build system so that the clean target can do something more like this:
clean:
for subdir in $(SPECIFIC_SERVICE_LIST); do make -C $$subdir clean; done
... or maybe like this:
clean: clean_services
...
clean_services:
for subdir in $(SPECIFIC_SERVICE_LIST); do make -C $$subdir clean; done
I would make it more make way by defining a target for cleaning up any supported service and then call all required clean targets as a prerequisite to clean. This has additional advantage to make clean in parallel when running with -j option as opposed to strictly sequential shell loop.
$ cat Makefile
SERVICES_LIST = A_Service B_Service C_Service #example
SPECIFIC_SERVICE_LIST = A_Service B_Service
.PHONY: $(addsuffix -clean, $(SERVICES_LIST))
$(addsuffix -clean, $(SERVICES_LIST)): %-clean:
$(MAKE) -C $* clean
.PHONY: clean
clean: $(addsuffix -clean, $(SPECIFIC_SERVICE_LIST))

GNU Make | How to create dependency between source code hierarchy and compiler output h-hy?

Some code from Makefile:
tempDir := ...
javaSources := $(wildcard src/java/**/%.java)
javaClasses := $(subst src/java, $(tempDir)/java/classes, $(subst .java,.class, $(javaSources)))
$(javaClasses): $(javaSources)
mkdir -p $(tempDir)/java/classes || true
javac \
-d $(tempDir)/java/classes \
-cp $(tempDir)/java/classes \
$?
How to create a pattern rule (like here) to preserve in / out order?
#MadScientist
First, your wildcard won't work. GNU make uses only basic shell globbing, which means it can't understand advanced globbing like ** meaning "search all subdirectories". Second, % is not a shell globbing character at all so you're just looking for files that are literally named %.java.
Instead you probably want something like this:
javaSources := $(shell find src/java -name '*.java')
Next, to create the javaClasses content you really don't want to use subst because it substitutes everywhere which can give false matches (e.g., $(subst .x,.y,foo.xbar) will yield foo.ybar which is probably not what you want).
Something like this is simpler to understand:
javaClasses := $(patsubst src/java/%.java,$(tempdir)/java/classes/%.class,$(javaSources))
Finally, you are repeating exactly the same error you made in the previous question, where you tried to list all the targets and all the prerequisites in the same rule. Just as I said for that question, that is not right.
The answer is exactly the same as in the previous question: you should write a pattern rule that describes how to build one single target from one single source file.
And again you need an all target or similar which depends on all the outputs.
In complement to MadScientist answer, you should probably use a pattern rule like:
$(tempDir)/java/classes/%.class: src/java/%.java
mkdir -p $(dir $#)
javac -d $(dir $#) -cp $(dir $#) $<
(not sure what -cp should be in this case, it depends on your specific project). And as MadScientist also suggested, you will need:
.PHONY: all
all: $(javaClasses)
such that you can call make all to compile all the source files that need to be. Put it before any other explicit target if you want all to be the default goal (the goal make selects if you just call make), or use the .DEFAULT_GOAL special variable:
.DEFAULT_GOAL := all

Why does my Makefile pattern rule run its recipe multiple times?

As per the gnu make documentation, a pattern rule's "...recipe is executed only once to make all the targets." However, I have the following Makefile
.PHONY: entrypoint
entrypoint: test_1.cpp test_2.cpp
test_%.cpp:
echo $#
And running make produces:
echo test_1.cpp
test_1.cpp
echo test_2.cpp
test_2.cpp
I'm new to make, and I'm probably misunderstanding something, but the documentation seems misleading if clear.
$ make -v
GNU Make 4.0
...
You're misreading the documentation. It means, the recipe is run only one time assuming that all the target patterns in that rule will be created.
Since you have only one target pattern in your rule (test_%.cpp`) make knows that each time it runs that recipe it will create one output file matching that pattern. To create different targets that match that pattern it will run multiple instances of the recipe.
If you had a rule like this:
%.x %.y %.z :
dothings
then make would expect that a single invocation of the recipe dothings would create all the targets matching this pattern (e.g., foo.x, foo.y, and foo.z).
Contrast this with an explicit rule like this:
foo.x foo.y foo.z :
dothings
Make here treats this exactly as if you'd written this:
foo.x :
dothings
foo.y :
dothings
foo.z :
dothings
That is, to build all three of these targets it would run the recipe three times.
There's no way to tell make "please run this recipe one time and it will produce every single target that could possibly match the pattern foo_%.cpp".
The following functions construct a dynamic list of dependencies of your multiple-target where the non-existent files are named last. This is more or less the method named "Another attempt" in the link you gave, except that it doesn't trip over missing files and is able to make a missing file by giving it as target on the command line. What it does not: execute the multitarget recipe if one of the multitargets is out of date relative to the others, but I think this is more of wanted side effect than a problem. The only drawback is the syntactic ugliness as you have to write it into an eval expression which forces you to quote all variables in the recipe which shall be evaluated at execution time.
define newline :=
endef
list2rules = $(firstword $1) $(if $(word 2,$1),: $(word 2,$1)$(newline)$(call list2rules,$(wordlist 2,1000,$1)))
multitarget = $(call list2rules,$(wildcard $1) $(filter-out $(wildcard $1),$1))
.PHONY: all
targets = test1 test2 footest3
#$(info $(call multitarget,$(targets)))
all: somefile
somefile: $(targets)
touch somefile
# here we generate the dependency list on the spot. Only one recipe to update all targets.
$(eval $(call multitarget,\
$(targets)) : ; \
touch $(targets) \
)

dividing outputs in make by filename

I am processing some files and want to at one point create two categories depending on the filename so I can compare the two. Is this possible in a makefile?
%.output1: %.input
ifneq (,$(findstring filename1,$(echo $<)))
mv $<.output1 $#
endif
%.output2: %.input
ifneq (,$(findstring filename2,$(echo $<)))
mv $<.output2 $#
endif
%.output_final: %.output1 %.output2
do_something
I think there is two things wrong with this code:
There is a mistake in the ifneq line.
%.output1 %.output2 will always use the same filename - it may not be possible to do this in 'make' and this may require ruffus.
You have tab-indented the ifneq line so make doesn't consider it a make directive and is considering it a shell command and attempting to pass it to the shell to execute (hence the shell error you removed in your recent edit).
Use spaces (or no indentation) on that line to have make process it correctly. That being said having done that you cannot use $< in the comparison as it will not be set at that point.
$(echo) is also not a make function. You have mixed/confused processing times. You cannot combine make and shell operations that way. (Not that you need echo there to begin with.)
If you want the comparison to happen at shell time then do not use make constructs and instead use shell constructs:
%.output1: %.input
if [ filename1 = '$<' ]; then
mv $<.output1 $#
fi
Though that is also incorrect as $< is %.input and $# is %.output1 for whatever stem matched the %. That rule should probably look more like this (though I'm having trouble understanding what you are even trying to have this rule do so I may have gotten this wrong).
%.output1: %.input
# If the stem that matched the '%' is equal to 'filename1'
if [ filename1 = '$*' ]; then
# Then copy the prerequisite/input file to the output file name.
cp $< $#
fi
I'm not sure I understand your second question point. The % in a single rule will always match the same thing but between rules it can differ.
This %.output_final: %.output1 %.output2 target will map the target file foo.output_final to the prerequisite files foo.output1 and foo.output2. But will also map any other *.output_final file to appropriately matching prerequisite files.

Can prerequisites in a static pattern rule be filtered?

I'm trying to limit $(all_possible_inputs) to $(relevant_inputs). $(all_possible_inputs) is a concatenation of multiple files from other included makefiles. The following functions correctly (the perl scripts know how to ignore the extra inputs), but everything is rebuilt if a single input changes:
$(step2_outputs): $(data)/%.step2: $(routines)/step2.%.pl $(all_possible_inputs)
perl $^ > $#
UPDATE: Filter must match more than one *.step1 file. If step1 produced:
A.foo.step1
A.bar.step1
B.foo.step1
B.bar.step1
B.baz.step1
Then step2's rules should expand to:
A.step2: routines/step2.A.pl A.foo.step1 A.bar.step1
B.step2: routines/step2.B.pl B.foo.step1 B.bar.step1 B.baz.step1
Logically, this is what I want to work:
$(step2_outputs): $(data)/%.step2: $(routines)/step2.%.pl $(filter $(data)/%.*.step1,$(all_possible_inputs))
perl $^ > $#
The % is supposed to match the static pattern rule stem. The * is supposed to be a wildcard (which I'm aware won't work). I believe the problem is that filter repurposes '%', so the filter expression fails. I thought it might be solvable with Secondary Expansion, but I tried this, and the filter still returned the empty string:
UPDATE: I switched the examples to use $$* based on Beta's good suggestion:
.SECONDEXPANSION:
$(step2_outputs): $(data)/%.step2: $(routines)/step2.%.pl $$(filter $(data)/$$*.%.step1,$(all_possible_inputs))
perl $^ > $#
This is running on gnu make 3.81 in a linux environment.
Your third method works for me, but you can try this: instead of % (which is expanded in the first phase) use $$*
.SECONDEXPANSION:
$(step2_outputs): $(data)/%.step2: $(routines)/step2.%.pl $$(filter $(data)/$$*.step1,$(all_possible_inputs))
perl $^ > $#

Resources