Using a file list for the make prerequisites - makefile

I want to be able to read the prerequisites for a makefile from a file list.
The file list looks something like this:
/projects/abc/a.v
/projects/abc/b.v
/projects/abc/c.v
and the filelist is called source.flist
This filelist is pretty long and I want to simply use this filelist as the prerequisite for one of the targets in the makefile.
Can someone please tell me how this can be achieved.
PS: I am at a beginner level w.r.t. makefiles
Thanks
Sundeep

I would do this by converting your filelist into a makefile in another step:
# Makefileall:
all: depsfromfile.out
depsfromfile.mk:
(echo "depsfromfile:"; cat depsfromfile )| sed -e '/^#/d' -e 's/$/ \\/' -e '$s/ \\//' > depsfromfile.mk # there is a \t at the beginnging of this line!
-include depsfromfile.mk
depsfromfile.out: depsfromfile
#touch depsfromfile depsfromfile.out
depsfromfile:
cat $^ > $#.out
#touch depsfromfile
with a file list depsfromfile
# depsfromfile
a
b
c
The Makefile autogenerates its dependencies from the depsfromfile file. The test target uses the depsfromfile to produce output.
The depsfromfile: target that is autogenerated into depsfromfile.mk and included into the Makefile contains the dependencies.
The depsfromfile.out target is only regenerated when a dependency of depsfromfile has been changed.
Now you can
# echo "test a" > a
# echo "test b" > b
# echo "test c" > c
# make
cat a b c > depsfromfile.out
# make
make: Nothing to be done for 'all'.

Related

Lists on Makefile

How can I get single elements from a list defined in a Makefile. For example:
FILES:= file0 \
file1
all: $(FILES)
$(FILES):
echo "write on file 0 something" > file0
echo "write on file 1 something else" > file1
Now I need to write something like this (using the first element of the list):
echo "write on file 0 something" > "${FILES[0]}"
Thanks in advance
This is not right:
$(FILES):
echo "write on file 0 something" > file0
echo "write on file 1 something else" > file1
You seem to be assuming that this syntax means that one invocation of this recipe will build all the output files. That's not what it means. It means that make will try to build each target and to build it, it will run a separate instance of the recipe. It's the same as writing:
file0:
echo "write on file 0 something" > file0
echo "write on file 1 something else" > file1
file1:
echo "write on file 0 something" > file0
echo "write on file 1 something else" > file1
Since this makefile doesn't really do anything useful we can't advise you on how to fix it.
But the answer to your question, assuming you're using GNU make (you don't say) might be found in the GNU make function documentation, specifically this section.
A potential hint:, $# will refer to the target that is currently being built. So if you have:
FILES := file0 file1
$(FILES):
#echo Compiling $#
it will output
Compiling file0
Compiling file1
(assuming both files need to be rebuilt). Now, if you want to have a file specific string attached to each file, you can use token pasting to create target specific variables as so:
FILES := file0 file1
STRING_file0 := write on file 0 something
STRING_file1 := write on file 1 something else
$(FILES):
#echo "$(STRING_$#)" > $#
In this case it will first expand the $# within the braces, which results in #echo "$(STRING_file0)" > $# for example, and then it would expand $(STRING_file0) to be write on file 0 something. Finally it would expand the second $# to be file0, and would therefore pass
#echo "write on file 0 something" > file0
on the shell when it is building file0. I think this is the behavior you're after. This can, of course be done with functions as well as #MadScientist suggested.

Define a target that depends on a value/variable that need to be resolved

Updated my question as it seemed to be not clear enough!
I was listing when to use make over bash. One thing I like about make is its declarative way of describing necessary steps; we can write a rule by relying on other rules knowing how to provide necessary files (or other external states).
I'm wondering how I can get the same benefit for a value not a file, without changing outer world (like leaving a temporary file).
hello.txt: (here, tell that it needs to resolve person's name)
# Here, person's name is available.
echo Hello $(var_name) > $#
We can imperatively prepare a necessary value with $(call prepare_name, ...) at the beginning of a command in a rule, but that's not what I'm after here.
I posted my attempts as an answer when I opened this question. Hopefully that adds more info on what I'm trying to achieve.
It's not overly clear what you're after, however to clarify a few concepts:
A target must be dependent on other targets. It cannot be dependent on a variable name. It can be dependent on the value of a variable, if that variable resolves to a target name.
So you could do:
VAR=some_target
hello.txt: $(VAR)
echo "hello $^" > $#
some_target:
touch $#
You CANNOT do:
VAR=some_target
hello.txt: VAR
and expect it to work (make would try to build VAR which likely doesn't exist and it would fail).
I'm assuming from the question that you want make to request the variable name of a person, and put that into hello.txt. In that case you would likely want to store the name in a temporary file and use that for the output:
.getname.txt:
#read -p "enter name" name > $#
hello.txt: .getname.txt
#echo "hello $$(cat $$<)" > $#
This will update .getname.txt if it didn't previously exist (so it will not necessarily ask on every invokation of make...). You could make .getname.txt be a .PHONY target, and it will run every time.
If you do want to run every time, then you can simply do:
hello.txt:
#read -p "enter name: " name && echo "hello $$name" > $#
.PHONY: hello.txt
Which will invoke the hello.txt rule regardless of whether hello.txt already exists, and will always prompt the user for a name and rebuild hello.txt.
I can think of a way using eval function. Below suppose foo is a value obtained by a complex calculation.
hello.txt: var_name
echo Hello $($<) > $#
.PHONY: var_name
var_name:
$(eval $# = foo)
Or with .INTERMEDIATE target, this also works, but I feel it's more complicated.
var_name = var-name.txt
hello.txt: $(var_name)
echo Hello $$(< $<) > $#
.PHONY: $(var_name)
.INTERMEDIATE: $(var_name)
$(var_name):
rm -f $# # In case the var file already exists
echo bar > $#
Another way could be to use a target-specific variable. It's not listing a variable as a prerequisite, but I still don't need to think about how to get var_name when writing echo Hello ....
define get_name
echo foo
endef
hello.txt: var_name = $(call get_name)
hello.txt:
echo Hello $(var_name) > $#
As noted in other answers, make track dependencies between files, using timestamps. The regular solution for handling a value will be to store it in a file (or to generate it into a file). Assuming that there is significant work to do whenever the data is changing, you can follow one of the patterns below to implement dependency check on the file value.
The following makefile snapshot will trigger rebuild of complex-result, only when the content of var-value is modified. This is useful when the content of var-value is continuously regenerated, but does not change very frequently.
all: complex-result
last-value.txt: var-value.txt
cmp -s $< $# || cat <$^ > $#
complex-result: last-value.txt
echo Buildig for "$$(cat var-value.txt)"
touch $#
Or more realistic example: trigger a build if the value (content) of any file was modified, using md5 checksum,
all: complex-result
last-value.txt: $((wildcard *.data)
md5sum $^ > $#
last-value.txt: var-value.txt
cmp -s $< $# || cat <$^ > $#
complex-result: last-value.txt
echo Building for "$$(cat var-value.txt)"
touch $#

How to use makefile variable outside of a rule

Right now I am learning how to use makefiles and am having trouble with variables inside rules. My goal is to parse a file containing names of other files, save that in a variable, and use the variable as a "target pattern" to run a rule on each file. Here is a simple example of my problem (I'm writing the file names in to make it simpler).
When I run this:
variable = file1.txt file2.txt file3.txt
run : $(variable)
$(variable): %.txt:
echo File is $#
I get the following, which is what I want:
$ make run
echo File is file1.txt
File is file1.txt
echo File is file2.txt
File is file2.txt
echo File is file3.txt
File is file3.txt
The problem is I want to define variable by parsing a file, and I want that file to be a dependency because, if it does not exist, I will make it using another rule in the makefile. So when I define the variable in a rule like this (which I know is there from my echo):
target1 :
$(eval variable = file1.txt file2.txt file3.txt)
echo $(variable)
run : $(variable)
$(variable): %.txt:
echo File is $#
I get the following:
$ make target1
echo file1.txt file2.txt file3.txt
file1.txt file2.txt file3.txt
And this:
$ make run
make: Nothing to be done for `run'.
So my question is how to define the variable in a rule and and use it in something line this:
$(variable): %.txt:
echo File is $#
I've tried reading through the manual and Googling but I just can't figure this out. I also imagine there is some simple answer I am missing. Thanks so much for your help! :)
If you can control the content of the target1 created file this is easy: just make the format of that file be a make variable assignment, then use the include command, like this:
run:
include target1
run: $(variable)
$(variable): %.txt:
echo File is $#
target1:
echo 'variable = file1.txt file2.txt file3.txt' > $#
Through the magic of automatically generated makefiles make will recreate that target file then re-invoke itself automatically.

Makefile, how to have prerequisites given by function with target as argument

I have a list of files contained in another file. I want to use this list as prerequisite for some target and for doing so I use a function that reads the list from file.
The problem is that I have different lists for different targets so I need to pass the target as argument to the function that reads the list. Something like that (that does not work):
getlist = $(shell cat $1)
tmp%: $(call getlist, %)
#cat file1 > $#
#cat file2 >> $#
file%:
#touch $#
#echo "$#" > $#
clean:
#rm file1 file2 tmp
where the list for building the tmp1 file is in the 1 file, the one for building the tmp2 file is in the 2 file and so on and so forth.
If I have instead tmp1: $(call figlist, 1) all works, but I need something capable of treating different file names.
If needed for the solution I can also change the way I named the files.

Redirection of output in shell script lines in Makefiles

I'm trying to figure out what one specific line in a makefile is doing:
foo: smth_foo_depends_on
...
#echo $< | bar >> $#
...
In particular I'd like to know:
why do we write # before echo;
what does $< mean;
why do we output something in $# which, as far as I know, is the list of arguments given to the script? Why do we modify it?
Thanks!
Why do we write # before echo?
Source: It is done to suppress the echoing for one specific line in the recipe.
What does $< mean?
Source: $< is the name of the first prerequisite (i.e. smth_foo_depends_on)
Why do we output something in $# which, as far as I know, is the list of arguments given to the script? Why do we modify it?
Source: In makefile language, $# is the name of the current target (i.e. foo). Do not confuse it with the shell list of positional parameters. The recipe line echo $< | bar >> $# is expanded to:
echo smth_foo_depends_on | bar >> foo

Resources