Makefile define: recursive expansion question - makefile

In a makefile, I define a variable using the define directive. This variable will hold a configurable list of commands that I want to execute.
I would like this variable to get a list of files (.foo files, for example). These files are created during the makefile execution. For example makefile:
MY_VAR = $(wildcard *.foo)
define MY_VAR2
echo $(1) $(MY_VAR)
endef
foo: create_files
$(call MY_VAR2, ls)
rm -f *.foo
create_files:
touch foo.foo
touch bar.foo
I do not get the desired results. It appears that MY_VAR2 is evaluated upon declaration.
Is there a way to get the desired behavior?
edit:
The $(shell) command, as sateesh correctly pointed out, works for the example above. However, it does not work for the example below. The main difference in this example is that the new files are created inside MY_VAR2.
MY_VAR = $(wildcard *.foo)
TEST_VAR = $(shell ls *.foo)
define MY_VAR2
#touch foo.foo
#touch bar.foo
#echo "MY_VAR" $(1) $(MY_VAR)
#echo "TEST_VAR" $(1) $(TEST_VAR)
endef
foo:
$(call MY_VAR2, ls)
#rm -f *.foo
I can solve the above by adding rules. Is there a simpler method?

It looks to me like you are abusing make, trying to write a shell script in make.
If you write a shell script, write a shell script. You execute your commands in sequence, and you are able to know what files are present when executing each line.
touch foo.foo
touch bar.foo
your-command `ls *.foo`
On the other side, if you want to make usage of make, then have rules and dependencies, you won't even have to use define if you go the make way of thinking.
foo: create_files
your-command $(wildcard *.foo)
rm -f *.foo
create_files:
touch foo.foo
touch bar.foo

I'm presuming here that you are using here GNU Make. I think you can get the desired
result using the "shell" built-in function provided with GNU Make.
Below is the make file snippet that demonstrates how you can get the result:
MY_VAR = $(wildcard *.foo)
TEST_VAR = $(shell ls *.foo)
define MY_VAR2
#echo "MY_VAR" $(1) $(MY_VAR)
#echo "TEST_VAR" $(1) $(TEST_VAR)
endef
foo: create_files
$(call MY_VAR2, ls)
#rm -f *.foo
create_files:
#touch foo.foo
#touch bar.foo
The result of running the above make file for the target "foo" is:
MY_VAR ls
TEST_VAR ls bar.foo foo.foo
So as demonstrated usage of shell function gets the desired result.
From the GNU make documentation:
The shell
function performs the same function that backquotes (‘‘’) perform in most
shells: it does command expansion. This means that it takes as an argument a shell
command and evaluates to the output of the command. The only processing make does on
the result is to convert each newline (or carriage-return / newline pair) to a single space.
...
The commands run by calls to the shell function are run when the function calls are expanded.
...
files := $(shell echo .c)
sets files to the expansion of ‘.c’. Unless make is using a very strange shell, this has the
same result as ‘$(wildcard *.c)’ (as long as at least one ‘.c’ file exists).
So I think using a recursively expanded variable (= form) along with shell function you
can get the desired result.

4.4.3 The Function wildcard
Wildcard expansion happens automatically in rules. But wildcard expansion does not normally take place when a variable is set, or inside the arguments of a function. If you want to do wildcard expansion in such places, you need to use the wildcard function
You may work your away around the eval function.
These created files should be dependencies of another rule so that the dependency graph is correct.
References:
4.4.3 The Function wildcard
8.8 The eval function

Could you try the filter command? You can find an example here.

How about using include?
The point is that the make needs two pass of execution.
First, you need to let make create what it wants,
Then you'll let make to parse the generated rule in the second pass after
the execution of the first rule.
define MY_VAR2
echo $(1) $(MY_VAR)
$(1) $(MY_VAR)
endef
-include .listoffiles
foo: create_files
$(call MY_VAR2, ls -al)
rm -f *.foo .listoffiles
create_files: .listoffiles
.listoffiles:
touch foo.foo
touch bar.foo
#echo "MY_VAR = \$$(wildcard *.foo)" > $#
It just does what you want, I suppose.
Two pass evaluation doesn't mean you have to type make twice.
It will be done by make automagically.
% make -f test.mk
touch foo.foo
touch bar.foo
echo ls -al bar.foo foo.foo
ls -al bar.foo foo.foo
ls -al bar.foo foo.foo
-rw-rw-r-- 1 yo-hei clearusers 0 Jan 8 08:44 bar.foo
-rw-rw-r-- 1 yo-hei clearusers 0 Jan 8 08:44 foo.foo
rm -f *.foo .listoffiles

What about doing your expansion in the shell instead of in make?
MY_VAR = $$(ls *.foo)
define MY_VAR2
#echo $(1) $(MY_VAR)
endef
foo: create_files
$(call MY_VAR2, ls)
#rm -f *.foo
create_files:
#touch foo.foo
#touch bar.foo
The double-$ will allow the shell to expand the file search part. I can't help but agree with the other posts about there being a more elegant way to do this, but this is an option for you.
Works for your second example too:
MY_VAR = $(wildcard *.foo)
TEST_VAR = $$(ls *.foo)
define MY_VAR2
#touch foo.foo
#touch bar.foo
#echo "MY_VAR" $(1) $(MY_VAR)
#echo "TEST_VAR" $(1) $(TEST_VAR)
endef
foo:
$(call MY_VAR2, ls)
#rm -f *.foo

Related

Dynamic targets based on the dependency in Makefile

There are a couple of kind of similar issues but I could not fit any of the proposed concepts to my case.
Just to give a little bit of context: I have a set of Julia files which create plots as PDFs which are part of a make procedure to create scientific papers, something like:
plots = $(shell find $(PLOT_PATH)/*.jl | sed 's/\.jl/\.pdf/g')
$(PLOT_PATH)/%.pdf: $(PLOT_PATH)/%.jl $(JULIA_SYSIMAGE)
$(JL) --project $< -o $(PLOT_PATH)
$(DOCUMENT_FILENAME).pdf: FORCE $(plots) $(figures)
latexmk $(DOCUMENT_FILENAME).tex
In the current setup, each XYZ.jl file is creating a XYZ.pdf file and it works absolutely fine.
Now I am dealing with cases where it would be much easier to create multiple plots from single Julia files, so a script like this:
#!/usr/bin/env julia
using PGFPlotsX
...
...
pgfsave("whatever.pdf")
pgfsave("another.pdf")
pgfsave("yetanother.pdf")
so that one could do a grep pgfsave SCRIPT | awk... to figure out the targets. However, I could not figure out how to generate dynamic targets (plots) based on the contents of the dependency file (Julia script).
An MWE for my problem is the following: I have a couple of files (dependencies) which are generating a bunch of targets, which are defined inside those files (and can be access via awk/grep/sed/whatever). For now, let's say that these are simply *.txt files and each line is a target.
file: a.txt
foo
bar
baz
file: b.txt
naarf
fjoord
A very basic (non-working) manual Makefile to demonstrate the goal would be something like this (it does not work as it cannot figure out how to make foo etc. but it shows the pattern for *.txt which needs to be repeated):
file: Makefile
all_products := $(shell find *.txt | xargs cat)
final_product: $(all_products)
echo $< > $#
(foo bar baz): a.txt
touch $(shell cat $<)
(narf fjoord): b.txt
touch $(shell cat $<)
so in principle, I need something to "process" the dependency (*.txt) to create a list of the targets, like
$(shell cat $%): %.txt
echo $< > $#
but I cannot manage to get a reference to the dependency on the target side ($% does not work).
Any ideas? Maybe the whole approach is just a bad idea ;)
A combination of GNU make foreach, eval and call functions is probably what you need. With your example:
TXT := $(wildcard *.txt)
.PHONY: all
.DEFAULT_GOAL := all
define MY_MACRO
$(1)-targets := $$(shell cat $(1))
$$($(1)-targets): $(1)
echo $$< > $$#
all: $$($(1)-targets)
endef
$(foreach t,$(TXT),$(eval $(call MY_MACRO,$(t))))
(pay attention to the $$ in the macro definition, they are needed). And then:
$ make
make
echo a.txt > foo
echo a.txt > bar
echo a.txt > baz
echo b.txt > naarf
echo b.txt > fjoord
If you want the recipe to build all targets at once you'll need a recent enough GNU make version (4.3 or later) and its new rule with grouped targets (x y z&: w):
TXT := $(wildcard *.txt)
.PHONY: all
.DEFAULT_GOAL := all
define MY_MACRO
$(1)-targets := $$(shell cat $(1))
$$($(1)-targets)&: $(1)
touch $$($(1)-targets)
all: $$($(1)-targets)
endef
$(foreach t,$(TXT),$(eval $(call MY_MACRO,$(t))))
And then:
$ make
touch foo bar baz
touch naarf fjoord
Note that in this case we could also use a simpler and less GNU make-dependent solution. Just use empty dummy files as time stamps, for instance .a.txt.tag for a.txt, and a static pattern rule:
TXT := $(wildcard *.txt)
TAG := $(patsubst %,.%.tag,$(TXT))
.PHONY: all
all: $(TAG)
$(TAG): .%.tag: %
touch `cat $<` $#

Makefile dependencies based on target

I have a Makefile with user-specified input files in the variable INPUT_FILES.
For each input file, I need to create an input file prime.
Some notes:
Each input file can have an arbitrary file location
It is reasonable to assume there aren't duplicate filenames
Each output file needs to go into $(OUTPUT_DIR)
My basic strategy has been to generate the set of targets based INPUT_FILES and then try to determine which input file is the actual dependency of the target.
A few variations I've tried:
# Create a list of targets
OUTPUT_FILES = $(foreach file,$(notdir $(INPUT_FILES)),$(OUTPUT_DIR)/$(file))
# This doesn't work, because all input files are dependencies of each output file
$(OUTPUT_FILES): $(INPUT FILES)
program --input $^ --output $#
# This doesn't work because $# hasn't been resolved yet
$(OUTPUT_FILES): $(filter,$(notdir $#),$(INPUT FILES))
program --input $^ --output $#
# This doesn't work, I think because $# is evaluated too late
.SECONDEXPANSION:
$(OUTPUT_FILES): $(filter,$(notdir $$#),$(INPUT FILES))
program --input $^ --output $#
# This doesn't work either
.SECONDEXPANSION:
$(OUTPUT_FILES): $$(filter,$(notdir $#),$(INPUT FILES))
program --input $^ --output $#
I've looked into static pattern rules as well, but I'm not sure if it can help with what I need.
In your case .SECONDEXPANSION: works because you can use make functions (filter) to compute the prerequisite of each output file. In other circumstances it could be impossible. But there is another GNU make feature that can be used in cases like yours: if you use GNU make you can programmatically instantiate make statements using foreach-eval-call. Just remember that the macro that is used as the statements pattern gets expanded twice, reason why you must double some $ signs (more on this later):
OUTPUT_DIR := dir
OUTPUT_FILES := $(addprefix $(OUTPUT_DIR)/,$(notdir $(INPUT_FILES)))
.PHONY: all
all: $(OUTPUT_FILES)
# The macro used as statements pattern where $(1) is the input file
define MY_RULE
$(1)-output-file := $(OUTPUT_DIR)/$$(notdir $(1))
$$($(1)-output-file): $(1)
#echo program --input $$^ --output $$#
endef
$(foreach i,$(INPUT_FILES),$(eval $(call MY_RULE,$(i))))
Demo:
$ mkdir -p a/a b
$ touch a/a/a b/b c
$ make INPUT_FILES="a/a/a b/b c"
program --input a/a/a --output dir/a
program --input b/b --output dir/b
program --input c --output dir/c
Explanation:
When make parses the Makefile it expands $(foreach ...): it iterates over all words of $(INPUT_FILES), for each it assigns the word to variable i and expands $(eval $(call MY_RULE,$(i))) in this context. So for word foo/bar/baz it expands $(eval $(call MY_RULE,$(i))) with i = foo/bar/baz.
$(eval PARAMETER) expands PARAMETER and instantiates the result as new make statements. So, for foo/bar/baz, make expands $(call MY_RULE,$(i)) with i = foo/bar/baz and considers the result as regular make statements. The expansion of $(eval ...) has no other effect, the result is the empty string. This is why in our case $(foreach ...) expands as the empty string. But it does something: create new make statements dynamically for each input file.
$(call NAME,PARAMETER) expands PARAMETER, assigns it to temporary variable 1 and expands the value of make variable NAME in this context. So, $(call MY_RULE,$(i)) with i = foo/bar/baz expands as the expanded value of variable MY_RULE with $(1) = foo/bar/baz:
foo/bar/baz-output-file := dir/$(notdir foo/bar/baz)
$(foo/bar/baz-output-file): foo/bar/baz
#echo program --input $^ --output $#
which is what is instantiated by eval as new make statements. Note that we had a first expansion here and the $$ became $. Note also that call can have more parameters: $(call NAME,P1,P2) will do the same with $(1) = P1 and $(2) = P2.
When make parses these new statements (as any other statements) it expands them (second expansion) and finally adds the following to its list of variables:
foo/bar/baz-output-file := dir/baz
and the following to its list of rules:
dir/baz: foo/bar/baz
#echo program --input $^ --output $#
This may look complicated but it is not if you remember that the make statements added by eval are expanded twice. First when $(eval ...) is parsed and expanded by make, and a second time when make parses and expands the added statements. This is why you frequently need to escape the first of these two expansions in your macro definition by using $$ instead of $.
And it is so powerful that it is good to know.
When asking for help please provide some kind of actual example names so we can understand more clearly what you have. It also helps us use terminology which is not confusing.
You really want to use $< in your recipes, not $^, I expect.
IF your "input files" are truly input-only (that is, they are not themselves generated by other make rules) then you can easily solve this problem with VPATH.
Just use this:
VPATH := $(sort $(dir $(INPUT_FILES)))
$(OUTPUT_DIR)/% : %
program --input $< --output $#
I finally found a permutation that works - I think the problem was forgetting that filter requires a % for matching patterns. The rule is:
.SECONDEXPANSION:
$(OUTPUT_FILES): $$(filter %$$(#F),$(INPUT_FILES))
program --input $^ --output $#
I also realized I can use #F (equivalent to $$(notdir $$#)) for cleaner syntax.
The rule gets the target's filename on its second expansion ($$(#F)) and then gets the input file (with path) that corresponds to it on second expansion ($$(filter %$$(#F),$(INPUT_FILES))).
Of course, the rule only works if filenames are unique. If someone has a cleaner solution, feel free to post.

Makefile: target-specific variables in target dependencies declaration

I'm trying to write a makefile that uses target-specific variables, but while the variables are set correctly in each target and prerequisite body, the prerequisites list itself isn't updated with the variable, thus causing the wrong prerequisite to be checked and called.
How can I update target-specific variables in the prerequisites too?
In the example below, both make foo and make bar should print "world", but make foo prints "hello".
X=hello
hello:
echo "hello"
world:
echo "world"
foo:X=world
foo:$(X)
bar:X=world
bar:
make $(X)
The goal I'm trying to achieve is that different targets will build similar prerequisites - the same files, in different folders - by passing the folders as a target-specific variable. The issue is that as in the example below, if one target is called first (foo, in the example), calling the second will not do anything.
DIR=fooDir
FILE=$(DIR)/filename
$(FILE):
touch $(FILE)
echo $(FILE)
foo: $(FILE)
bar:DIR=barDir
bar: $(FILE)
6.11 Target-specific Variable Values:
As with automatic variables, these values are only available within the context of a target’s recipe (and in other target-specific assignments).
But not in the prerequisite list. In other words, in foo:$(X) X is not target specific.
One way to achieve the desired results is:
same_files := filename another_filename
# ${1} is the target name.
# ${2} is the directory.
define similar_prerequisites
${1} : $(addprefix ${2}/,${same_files})
$(addprefix ${2}/,${same_files}) : | ${2}
touch $$#
${2} :
mkdir $$#
endef
all : foo bar
foo bar :
#echo "$# prerequisites are $^"
$(eval $(call similar_prerequisites,foo,fooDir))
$(eval $(call similar_prerequisites,bar,barDir))
Output:
$ make
mkdir fooDir
touch fooDir/filename
touch fooDir/another_filename
foo prerequisites are fooDir/filename fooDir/another_filename
mkdir barDir
touch barDir/filename
touch barDir/another_filename
bar prerequisites are barDir/filename barDir/another_filename
I needed something similar. Does this exemple suit your needs ?
# Basic rules can be ignored
# Use % to not take much space
.PHONY: foo bar
foo bar:
#printf "%s depends on %s\n" "$#" "$^"
fooDir/ barDir/:
#printf "%s\n" "Building $#"
#mkdir "$#"
fooDir/%: fooDir
barDir/%: barDir
%/filename: %/
#printf "%s\n" "Building $#"
#touch "$#"
%/filename2: %/
#printf "%s\n" "Building $#"
#touch "$#"
%/filename3: %/
#printf "%s\n" "Building $#"
#touch "$#"
fooFile = fooDir/filename2
barFile = barDir/filename2
DIR = $(*)Dir
FILE = $(DIR)/filename3
.SECONDEXPANSION:
# Here the fun begin
# See full details on https://stackoverflow.com/a/73679964/7227940
foo bar: %: $$(*)Dir/filename $$($$(*)File) $$(FILE)
output:
$ make foo; make bar
Building fooDir/
Building fooDir/filename
Building fooDir/filename2
Building fooDir/filename3
foo depends on fooDir/filename fooDir/filename2 fooDir/filename3
Building barDir/
Building barDir/filename
Building barDir/filename2
Building barDir/filename3
bar depends on barDir/filename barDir/filename2 barDir/filename3
The tricks is in .SECONDEXPANSION: all target defined after (that's why I put it at the end) will get a second expansion. The $(*) aka the stem of matched rules will only become available in the second expansion. This allow us to build name that is linked to the target.
The pattern targets: %: deps allow to match only on listed target and not catch all target.
I prefer the version 1 or 2 because the third version you might be tempted to use in place before .SECONDEXPANSION: and thus you will get weird error messages. If you really want to use 3 then put this special target at the top. Also 3 might have wrong value (you might want MAKECMDGOALS or something like that instead of *) if used with multiple wildcard rule. Just remember if you want to use 3 then put $$ either in DIR in FILE definition or in the use site.

Makefile: defining rules and prerequisites in recipes

I have a setup where the files I want to process with make are dependent on the output of another program. Building the program and all its prerequisites
is also a complicated task so I would like to use make for this as well. Now my problem is, that it doesn't seem that one can generate rules and prerequisites in Makefile recipes. Consider the following code:
bar:
echo target1 target2 target3 > bar
foo: bar
$(eval BAR := $(shell cat bar))
define FUN
$(1):
touch a$(1)
endef
ifdef BAR
$(foreach i,$BAR,$(eval $(call FUN,$(i))))
endif
blub: foo $(BAR)
I replaced a big set of complicated recipes that lead to the generation of the list of files I want to have in the end by the bar recipe. In reality, producing the content of bar is very complicated and should be done by a set of Makefile recipes and cannot just be done by (as the above suggests):
BAR:=$(shell echo target1 target2 target3)
I would like to put the foreach loop into the recipe for foo but that fails with prerequisites cannot be defined in recipes which makes sense and is also explained in function define in makefile
But it seems that when I do make blub that at the time when foo eval's BAR to a different value, the prerequisites for blub are not re-evaluated.
So I think ultimately I'm looking for two things:
how do I generate recipes dynamically at runtime, based on (and dependent on) what another recipe (bar in this case) outputs?
how do I update the prerequisites of a target (blub in this case) dynamically at runtime, based on (and dependent on) what another recipe (bar in this case) outputs?
Thank you!
EDIT: SOLUTION
With the help of #user657267 the following seems to solve my problem:
.PHONY: all
all: blub
-include bar.make
.PHONY: blub
blub: $(BAR)
echo $^
bar.make: Makefile
printf 'BAR=target1 target2 target3\n' > $#
printf 'target1 target2 target3:\n' >>$#
printf '\ttouch $$#' >> $#
.PHONY: clean
clean:
rm -f target1 target2 target3 bar.make
Sounds like you should be using make's self-remaking features
-include bar.make
blub: $(BAR)
#echo $^
bar.make:
#echo BAR := target1 target2 target3 > $#
#echo target1 target2 target3: ; touch $$# >> $#
Obviously the recipes for bar.make are contrived, in the real world they'd probably invoke some kind of script that outputs a valid makefile.

How do I force a target to be rebuilt if a variable is set?

Assume I have a build-target foo:
foo:foo.c
$(CC) $(CFLAGS) $(ARGS) -c foo.c -o foo
Now, ARGS is something that I pass on the command line:
$ make ARGS:=-DX=1 foo
So, I need to bypass make's cleverness, because the foo target does not only depend on which files have changed, but also on the value of ARGS.
Is there something in make to do this? My hack (see answer) doesn't seem to be the most elegant but it works. Anything better?
Here is a general solution to your specific problem.
You want to be able to depend on a variable as a prerequisite. That is, you can make it a prerequisite to any target in your makefile, and when the value of the variable changes, you rebuild those targets.
Here is a function that does that, you use this function to declare a variable to be dependable, and then you can use it as a prerequisite.
Note that if the variable is not used on the command line, it will still mean that variable still has a value, namely, the empty string.
define DEPENDABLE_VAR
.PHONY: phony
$1: phony
#if [[ `cat $1 2>&1` != '$($1)' ]]; then \
echo -n $($1) > $1 ; \
fi
endef
#declare ARGS to be dependable
$(eval $(call DEPENDABLE_VAR,ARGS))
foo:foo.c ARGS
$(CC) $(CFLAGS) $(ARGS) -c foo.c -o foo
In fact, we could omit the need for "declaration", and just write a similar function that will make all variables dependable by default. But I don't like that. I prefer that the users that modify makefiles I write, declare their intentions explicitly. It is good for them :)
My solution was to create a dummy phony target:
.PHONY:dummy
dummy:
#:
and have foo depend on dummy if ARGS is nonempty:
foo:foo.c $(patsubst %,dummy,$(ARGS))
Note on Mark's excellent answer
The bare necessities of Mark's answer are actually very simple. It really boils down to just:
.PHONY: phony
ARGS: phony
#if [[ `cat ARGS 2>&1` != '$(ARGS)' ]]; then echo -n $(ARGS) >ARGS; fi
The rest of his code is just to let you reproduce the recipe for other variable names without repeating yourself. Useful in practice, but the above version will help you see what's going on more easily.
In fact, my answer can even be made general (like Mark's) for any variable name, but in a less complicated way as follows:
.PHONY: phony
.ARG~%: phony
#if [[ `cat .ARG~$* 2>&1` != '$($*)' ]]; then echo -n $($*) >.ARG~$*; fi
Then you simply add .ARG~MYVARNAME as a dependency to any target to make that target depend on variable MYVARNAME.
Note that the dot in front of .ARG~% simply causes it to create a dependency-tracking file that is 'hidden' in linux.
So in your case, you would do:
foo: foo.c .ARG~ARGS
$(CC) $(CFLAGS) $(ARGS) -c foo.c -o foo
I don't understand how the other solutions are supposed to work. If the ARGS target is .PHONY or depends on a .PHONY, then it will always be run, right?
Here is my solution using the $(file) function in newer versions of gmake:
.PHONY: FORCE
define DEPENDABLE_VAR
$(1):
echo -n $($(1)) > $(1)
ifneq ("$(file <$(1))","$($(1))")
$(1): FORCE
endif
endef
#declare ARGS to be dependable
$(eval $(call DEPENDABLE_VAR,ARGS))
foo: foo.c ARGS
touch foo
And the result:
~/stuff/makevars> make foo ARGS=1
echo -n 1 > ARGS
touch foo
~/stuff/makevars> make foo ARGS=1
make: 'foo' is up to date.
~/stuff/makevars> make foo ARGS=2
echo -n 2 > ARGS
touch foo
~/stuff/makevars> make foo ARGS=2
make: 'foo' is up to date.

Resources