Use make to copy files with different filenames - makefile

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

Related

GNU make: create targets baed on specific directory contents (1:1 target-directory mapping)

I have a series of directories organized like this:
foo/
foo.file1 foo.file2
bar/
bar.file1 bar.file2
baz/
baz.file1 baz.file2
Right now I'm processing these files using a script that does all the checking for file existence etc but I thought that perhaps I could use a Makefile for it (since said script is very fragile), to avoid reprocessing files that did not change.
The problem is that each directory is independent, and I'd need to do, for example:
foo.file1.processed: foo.file1
run_random_program foo.file1 -o foo.file1.processed
for each of the 71 directories that are in total in that path. This looks like being extremely tedious and I wonder if there's something that would prevent me from writing all of this by hand.
Is such a thing possible?
EDIT: Some examples that show what I have in mind, had I a single Makefile for each directory:
file1.cds.callable: file1.callable
long_script_name -i $< -o $#
file1.rds: file1.cds.callable
another_long_script_name $< additional_file_in_folder $#
file1.csv: file1.rds
yet_another_script $< $#
Seems like pattern rules are exactly what you need:
# These are the original source files (based on the example)
CALLABLE := $(wildcard */*.callable)
# These are the final targets
TARGETS := $(CALLABLE:%.callable=%.csv)
all: $(TARGETS)
%.csv : %.rds
yet_another_script $< $#
%.rds: %.cds.callable
another_long_script_name $< additional_file_in_folder $#
%.cds.callable: %.callable
long_script_name -i $< -o $#

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.

Copy files with GNU make

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.

GNU Make Force Removal of Intermediate Files

I have a compiler that produces .c files from .ec files as an intermediate step. The compiler does not remove the .c file. The compiler cannot be asked to skip invocation of $CC to produce the .o file. I am trying to have GNU make (3.81) treat the .c files produced as intermediate files and clean them up. The following is an isolated example that reproduces the bad behavior with a file extension that has no implied rules.
.INTERMEDIATE: %.delme
%.o: %.ec
cp $< $(<:.ec=.delme)
cp $(<:.ec=.delme) $#
all: test.o
To execute the test case:
rm -f test.*
touch test.ec
make
if [[ -e test.delme ]]; then echo "Failure"; else echo "Success"; fi
Try using a pattern rule to tell make their your compiler produces both .o and .c files from the .ec source. And then declare all the c files as INTERMEDIATE. Pattern rules with multiple outputs work differently than non-pattern rules. Make will run pattern rules only once to produce all output files from the rule, while static rules will be run for each output file. Make also understand that a pattern rule will produce all output files, even if it only wanted one of them as a target.
The result is something like this:
SRC := foo.ec bar.ec
OBJS := $(SRC:.ec=.o)
all: program
program: $(OBJS)
cat $^ > $#
%.o %.c: %.ec
cp $< $(<:.ec=.c) ; cp $< $(<:.ec=.o)
.INTERMEDIATE: $(SRC:.ec=.c)
The command to make the .c and .o from the .ec will be run once to produce both those files. Since make knows it made the .c (even though it only wanted the .o), it will know enough to delete it. The .INTERMEDIATE target will only work if the files are listed explicitly, not using a pattern, so we haven't used %.c. Which seems like a bad idea anyway, what if you had C source that wasn't produce from an .ec file and make deleted it for you? Example output:
$ make
cp foo.ec foo.c ; cp foo.ec foo.o
cp bar.ec bar.c ; cp bar.ec bar.o
cat foo.o bar.o > program
rm bar.c foo.c
$ touch foo.ec ; make
cp foo.ec foo.c ; cp foo.ec foo.o
cat foo.o bar.o > program
rm foo.c
Notice how in the second invocation it only deleted foo.c since bar.o/c wasn't rebuilt.
Make can only consider make targets to be intermediate. You can't just declare a random file on the filesystem as intermediate and have make delete it for you.
Here the .delme file is created as a side effect of the recipe that builds the .o file; make doesn't know anything about it, so make will not delete it because there are no targets in the makefile that are intermediate.
In your example you could split the two cp commands into separate rules and that would allow the intermediate setting to work:
%.delme : %.ec
cp $< $#
%.o : %.delme
cp $< $#
I'm assuming that in your real environment you can't do that because it's all one command that generates the intermediate file and the real file. In that case you'll have to deal with the delete yourself inside the recipe:
%.o : %.ec
cp $< $(<:.ec=.delme)
cp $(<:.ec=.delme) $# && rm -f $(<:.ec=.delme)
Note this leaves the .delme file existing if the cp command fails; if you want to remove it no matter what you can do that too.
EDIT
To delete the intermediate file even if the command fails you have to preserve the exit code so you can tell make what it was. Something like:
%.o : %.ec
cp $< $(<:.ec=.delme)
cp $(<:.ec=.delme) $#; e=$$?; rm -f $(<:.ec=.delme); exit $$e

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.

Resources