I'm fairly new to makefiles so please don't be to harsh. :)
My problem is that I want to convert all .dot files in the directory of the makefile to .png files.
Usually this command should solve my problem:
DOT_FILES=$(wildcard *.dot)
DOTPNG=$(DOT_FILES:.dot=.png)
...
...
dot: $(DOT_FILES)
#dot -Tpng $(DOT_FILES) > $(DOTPNG)
.. but it doesn't. It seems that it is converting all files but also overwriting them. So all I get is 1 of 3 .png files and this error:
Error: dot: can't open graph.png
Error: dot: can't open example.png
Makefile:35: recipe for target 'dot' failed
make: *** [dot] Error 3
Am I doing something wrong or isn't this the code I am looking for?
Let's say your files are foo.dot, graph.dot, and example.dot. You're basically running this command:
dot -Tpng foo.dot graph.dot example.dot > foo.png graph.png example.png
... which tells the shell to execute dot -Tpng foo.dot graph.dot example.dot graph.png example.png and redirect the output to foo.png.
You should instead teach make how to create a .png file from a .dot file:
%.png: %.dot
dot -Tpng $< > $#
($< and $# are so-called automatic variables; the %.png: %.dot thing is a pattern rule.)
Then tell it that the dot target depends on all the .png files:
.PHONY: dot
dot: $(DOTPNG)
(.PHONY tells make that dot is not really the name of a file, it's just a rule that triggers commands.)
Related
I am currently in the process of writing quite a large document, that I divided into multiple chapters. What I chose to do is to put each chapter in a different file, and then compile them with a makefile into one big document. When I type make get in the terminal, the .tex docs appear with the right name in my folder but completely blank.
Here is my makefile :
pdf:
pdflatex --shell-escape file.tex
clean:
rm -f chapter1.tex
rm -f chapter2.tex
get:
sed -e '/BEGIN_BOOK/,/END_BOOK/!d' ../01-chapter1/chapter1.tex> chapter1.tex
sed -e '/BEGIN_BOOK/,/END_BOOK/!d' ../02-chapter2/chapter2.tex> chapter2.tex
diff:
git latexdiff HEAD -- --main file.tex --latexopt "--shell-escape"
But if I replace these blank ones with the correct files, the make command works properly and I get what I want.
Thank you in advance
Using make for generic purposes (not compiling)
Suppose I have a set of files with full path names, and I would like to do something with that file.
something --file a/b/c/X > $< # (for example)
And I have made a rule:
something-%:
something --file $*
Which matches fine against, say, "something-foo" but does not catch "something-a/b/c/foo". Is there a way to write a wildcard rule for this latter case?
The manual describes how patterns match:
When the target pattern does not contain a slash (and it usually does not), directory names in the file names are removed from the file name before it is compared with the target prefix and suffix.
In your case when calling as make something-a/b/c/foo, something-a/b/c/ is treated as directory and removed, so the rest does not match your rule. This can be easily checked:
$ cat Makefile
something-%:
echo something --file $<
f%o:
echo $*
Output:
$ make something-OtherDirectory/src/foo -dr
GNU Make 4.2.1
...
Considering target file 'something-OtherDirectory/src/foo'.
File 'something-OtherDirectory/src/foo' does not exist.
Looking for an implicit rule for 'something-OtherDirectory/src/foo'.
Trying pattern rule with stem 'o'.
Found an implicit rule for 'something-OtherDirectory/src/foo'.
Finished prerequisites of target file 'something-OtherDirectory/src/foo'.
Must remake target 'something-OtherDirectory/src/foo'.
echo something-OtherDirectory/src/o
...
Note that it matched the other pattern rule with the stem of o.
You can make it work your way if your pattern does include a slash. For sake of completeness I would also define a prerequisite if your rule is based on a file and declare target as phony if it does not generate a real output file:
$ cat Makefile
.PHONY: something/%
something/%: %
echo something --file $<
Output:
$ make something/OtherDirectory/src/foo.c
echo something --file OtherDirectory/src/foo.c
something --file OtherDirectory/src/foo.c
I thought this is quite simple
%.png: ../figs/%.png
convert $? -resize '40%' $#
That is, I want to generate an image in this directory from the corresponding image in "../figs/" .
But, the above leads to an infinite chain of dependencies because ../figs/foo.png matches %.png and therefore make tries to check ../figs/../figs/foo.png, which matches %.png and therefore make tries to . . .
Eventually, make stops with "File name too long".
I must be missing something. What is a clean solution?
Kill the chain with an empty rule
%.png: ../figs/%.png
convert $? -resize '40%' $#
../figs/%.png: ;
All the answers above are quite interesting. However, I'll like to mention the terminal rule solution:
%.png:: ../figs/%.png
convert $? -resize '40%' $#
By changing to a double colon ::, we then mark the prerequisites terminal:
One choice is to mark the match-anything rule as terminal by defining it with a double colon. When a rule is terminal, it does not apply unless its prerequisites actually exist. Prerequisites that could be made with other implicit rules are not good enough. In other words, no further chaining is allowed beyond a terminal rule.
Note: only suitable for match-anything rules.
What are double-colon rules in a Makefile for?
GNU make seems to ignore non-terminal match-anything rules for intermediate files
user657267's solution is perfect. Another option is to use static pattern rules:
PNGS := $(patsubst ../figs/%.png,%.png,$(wildcard ../figs/*.png))
all: $(PNGS)
$(PNGS): %.png: ../figs/%.png
convert $< -resize '40%' $#
clean:
rm -f $(PNGS)
Computing the list of all targets from the list of all prerequisites has several nice side effects, like the prossibility of adding the all and clean targets, for instance.
I have this very simple Makefile to create plots from tab-separated data files:
%s.png: %s.tsv
Rscript make-plots.r $< $#
I have a file genus.tsv from which I want to make a plot. This is done as follows:
$ make -v
GNU Make 3.81
$ make genus.png
Rscript make-plots.r genus.tsv genus.png
That works as expected. Here comes the strange part. If I create a copy test.tsv from genus.tsv, and I try to make a plot from that, it fails for some reason:
$ cp genus.tsv test.tsv
$ make test.png
make: *** No rule to make target `test.png'. Stop.
The same happens with any other .png target I've tried. I expect the pattern rule to match any .png target. Why doesn't this work?
I have a makefile which looks for .txt files in a directory and for each file makes echo of it name.
pchs := $(wildcard $(OUTPUT:%=%/*.txt))
txt: $(pchs)
%.txt:
echo $#
But when I start it the make utility returns me that nothing to be done for txt. Why?
EDIT1:
After some answers I understand what I should make with my makefile. Now it looks like this:
pchs := $(wildcard $(OUTPUT:%=%/*.txt))
.PHONY : $(pchs)
txt: $(pchs)
%.txt:
#echo pch is '$<'
But .PHONY does not help me the result of making is the same.
Why does make says, that there ist nothing to do? Because make calculates dependencies of targets, usually file targets. And the "txt" target produces no file.
.PHONY is for targets, that produce no file, like the clean target.
This here should work:
pchs := $(wildcard $(OUTPUT:%=%/*.txt))
.PHONY: txt
txt: $(pchs)
echo $#
But, since you only echo the filename, I guess that you are post processing this output. Maybe you could formulate this post processing as a rule in the makefile?
Because makefiles define what you want to have built. And the .txt files already exist, so there is nothing to do.
To solve this there are a number of possibilities, but you should look into the .PHONY record if using gnu-make at least.
You can build fake-things out of the txt records and mark them as phony. But... it might just be easier to do this:
pchs := $(wildcard $(OUTPUT:%=%/*.txt))
txt:
for i in $(pchs) ; do echo $$i ; done
That's because every .txt file you've listed in $(pchs) is up-to-date and Make decides to take no action.
Add them to .PHONY target to force rebuilding them every time you run Make:
.PHONY : $(pchs)
UPD.
Also check that $(pchs) list is not empty, it could be done i.e. as follows:
txt : $(pchs)
#echo pchs is '$^'
I would use Bash to determine the *.txt files, instead of Make:
txt:
ls | grep -F '.txt'
You could also use this as a template to make a more general target, that echos any files that exist in the directory with a particular extension.
You may want the target to be PHONY, since it's not making a file.