Copy files with GNU make - makefile

I need to copy several files. Doing a makefile:
FILES=foo.txt d1/bar.dat d2/baz.txt
TARGETDIR=/app
targets=$(addprefix $(TARGETDIR)/,$(FILES))
all: $(targets)
$(targets): $(FILES)
cp $(subst $(TARGETDIR)/,,$#) $#
Files copied correctly, but if I do touch foo.txt, all three files are copied.
I know that "the correct way" is to define three rules like:
$(TARGETDIR)/foo.txt: foo.txt
cp $^ $#
$(TARGETDIR)/d1/bar.dat: d1/bar.dat
cp $^ $#
$(TARGETDIR)/d2/baz.txt: d2/baz.txt
cp $^ $#
But in this case I have to write names of the files twice, once for these rules and once for all rule.
Is there a way to 'multiply' the rule for each name in the prerequisite?
Something like
$(TARGETDIR)/%: $(FILES)
cp $< $#

You can manipulate the target name using the text manipulation functions, if you use secondary expansion:
.SECONDEXPANSION:
$(targets): $$(patsubst $(TARGETDIR)/%, %, $$#)
echo cp $< $#
This is not the only way, but it's probably the simplest.

Related

Make: Target name in Rule

I have created a Makefile to generate pdf and html for a md file mentioned while invoking make like make a.md should generate a.pdf and a.html, and should not convert other md files present in the directory.
My make file
But I am, getting error Nothing to be done for a.md
Could you please suggest changes?
Because a.md is already in place, make indeed does have nothing further to do. You can either just use for instance a as the specified target (which can depend on pdf and html files as prerequisites). Or pass the desired source through a variable and determine your desired target from that.
One (the latter) option would be this:
EXPORTED= $(SOURCE:%.md=%.html) $(SOURCE:%.md=%.pdf)
%.html : %.md
pandoc -o $# $<
%.pdf : %.md
pandoc -o $# $<
all: $(EXPORTED)
Which you can call with make SOURCE=a.md.
The other option (former):
%.html : %.md
pandoc -o $# $<
%.pdf : %.md
pandoc -o $# $<
.PHONY: $(MAKECMDGOALS)
$(MAKECMDGOALS): $(MAKECMDGOALS:%=%.html) $(MAKECMDGOALS:%=%.pdf)
Allows you to call make a.
But to reiterate. You cannot use (existing) source as a target, because make would (as it did) conclude it's done making that target.
Except (but I personally do not really like this as it is IMO confusing), if you insisted, you could do a variation on the previous take... but work with *.md targets... which you all declare as .PHONY. I.e. not real files and always to be considered/remade:
%.html : %.md
echo pandoc -o $# $<
%.pdf : %.md
echo pandoc -o $# $<
.PHONY: $(MAKECMDGOALS)
$(MAKECMDGOALS): $(MAKECMDGOALS:%.md=%.html) $(MAKECMDGOALS:%.md=%.pdf)
Now you could indeed call make a.md. I'd still prefer one of the two above.

Wildcards in Makefile rules

I have the following rules in a Makefile
%.00.png: %.dat
genimg.py $< $#
%.10.png: %.dat
genimg.py $< $#
%.20.png: %.dat
genimg.py $< $#
%.30.png: %.dat
genimg.py $< $#
where genimg.py is a script that generates an image based on the data in a *.dat file and a parameter which is stored in the name of the target file (00, 10, 20, 30). Is it possible to combine all these rules into one? I tried the obvious:
%.*.png: %.dat
genimg.py $< $#
but that does not work.
No, there's no way to combine this into one rule.
But, you could auto-generate the rules. Assuming you can write it as a single line you can use something like:
SIZES := 00 10 20 30
$(foreach S,$(SIZES),$(eval %.$S.png: %.dat ; genimg.py $$< $$#))
You can achieve something very close to this using secondary expansion, the automatic variable $* and the basename function :
.SECONDEXPANSION:
%.png: $$(basename $$*).dat
genimg.py $< $#
But this has the following limitation that a file named foo.png will still be fitted by this rule and depend upon foo.dat.
If this is not acceptable behavior for your application, you can use the more elaborate version :
.SECONDEXPANSION:
# This relies on the assumption that there's no file named '/-'
%.png: $$(if $$(word 2,$$(subst ., ,$$*)),$$(word 1,$$(subst ., ,$$*)).dat,/-)
genimg.py $< $#
But this comes with the additional limitation that a file named a.b.10.png will get as a prerequisite a.dat instead of the expected a.b.dat.

Makefile: source files in a particular directory

What do I need to do if I want to compile several files (e.g. a.f90, b.f90, c.f90) in a given directory (say, MYDIR)?
My Makefile code is something like:
CC=gfortran
CFLAG=-g
HOME=MYDIR
SRC=$(HOME)/(a.f90,b.f90,c.f90)
OBJ=$(SRC:,=.o)
EXE=test.x
%.o: %.f90
$(CC) $(CFLAG) -c -o $# $<
$(EXE): $(OBJ)
$(CC) -o $# $^ $(CFLAG)
clean:
rm -f *.o
I think, the 4-th line is not correct. So what could be the replacement?
Another thought: Can I use a wildcard if I want to compile all .f90 file inside MYDIR?
There are lots of ways to do it. You can do something like:
SRC = $(addprefix $(HOME)/,a.f90 b.f90 c.f90)
Also your assignment of OBJ is wrong; there's no comma needed after the colon.
Yes you can use wildcard if you want.

Pattern rule with different dependencies for different files

In my Makefile I have a pattern rule
%.out: %.in
myscript $< $#
I want this rule to be triggered not only when the files quux.in or foobar.in are modified, but also when any of the files quux/* or foobar/* (respectively) are modified.
How can one express such a dependence in a (GNU) Makefile?
How about this? You could even have a variable like:
DIR_LIST = quux foobar home france china mexico mars
%.out: %.in
myscript $< $#
$(foreach dir,${DIR_LIST},${dir})/%.out: $(foreach dir,${DIR_LIST},${dir})/%.in
myscript $< $#
OR.....
%.out: %.in
myscript $< $#
quux/%.out: quux/%.in
myscript $< $#
foobar/%.out: foobar/%.in
myscript $< $#
I found this solution:
.SECONDEXPANSION:
%.out: %.in $$(call find, $(basename %), *)
my_script $< $#
find = $(foreach dir,$(1),$(foreach d,$(wildcard $(dir)/*),$(call find,$(d),$(2))) $(wildcard $(dir)/$(strip $(2))))
The recursive wildcard find function comes from https://plus.google.com/101663514639216293981/posts/h5Xr1i8kgfu.

Use make to copy files with different filenames

I would like to have a makefile copy files from another directory and change their names. At the moment, I do something like this:
ALL: figure1.eps figure2.eps figure3.eps
figure1.eps: ../other_directory/a_nice_graph.eps
cp $< $#
figure2.eps: ../other_directory/a_beautiful_graph.eps
cp $< $#
figure3.eps: ../other_directory/an_ugly_graph.eps
cp $< $#
I would like to avoid writing the same rule (cp $< $#) for every line. I can't use the standard wildcards (%.eps) because the filenames do not match. Is there any way to do this?
Try this:
ALL: figure1.eps figure2.eps figure3.eps
%.eps:
cp $< $#
figure1.eps: ../other_directory/a_nice_graph.eps
figure2.eps: ../other_directory/a_beautiful_graph.eps
figure3.eps: ../other_directory/an_ugly_graph.eps

Resources