Makefile and wildcard targets - makefile

I have a directory with many files. I would like to run a command against these files (ie. md5sum). Instead of keep recomputing the checksum I would like to store the checksum. I would like to compute the checksums in a parallel manner (make -j)

Something like:
md5sums: $(addsuffix .md5sum,$(wildcard *.foo))
%.md5sum: %
md5sum $< > $#
That's about the best we can do with the limited description available.

Related

GNU Make: Batching multiple targets in single command invocation

Consider the following setup:
$ touch 1.src 2.src 3.src
$ cat Makefile
%.dst: %.src
#convert -o "$#" "$<"
We can compile our .src files into .dst files by running make 1.dst 2.dst 3.dst which calls the convert (just a placeholder) tool three times.
This setup is fine if there is little overhead in calling convert. However, in my case, it has a startup penalty of a few seconds for every single call. Luckily, the tool can convert multiple files in a single call while paying the startup penalty only once, i.e. convert -o '{}.dst' 1.src 2.src 3.src.
Is there a way in GNU make to specify that multiple src files should be batched into a single call to convert?
Edit: To be more precise, what feature I am looking for: Say that 1.dst is already newer than 1.src so it doesn't need to be recompiled. If I run make 1.dst 2.dst 3.dst, I would like GNU make to execute convert -o '{}.dst' 2.src 3.src.
A quick and dirty way would be creating a .PHONY rule that simply converts all src files to dst files but that way I would convert every src file each and every time. Further more, specifying dst files as prerequisites in other rules would also no longer be possible.
Thanks in advance!
If you have GNU make 4.3 or above, you can use grouped targets like this:
DST_FILES = 1.dst 2.dst 3.dst
SRC_FILES = $(_DST_FILES:.dst=.src)
all: $(DST_FILES)
$(DST_FILES) &: $(SRC_FILES)
convert -o '{}.dst' $?
#touch $(DST_FILES)
If your convert is only updating some of the targets then you need the explicit touch to update the rest.
Here's a way to do it with passing a goal on the command line that might work; change DST_FILES to:
DST_FILES := $(or $(filter %.dst,$(MAKECMDGOALS)),1.dst 2.dst 3.dst)
Is there a way in GNU make to specify that multiple src files should be batched into a single call to convert?
It is possible, but messy, to write make rules for build steps that produce multiple targets with a single run of the recipe, such that the recipe is executed just once if any of the targets needs to be updated. However, you clarify that
[if] 1.dst is already newer than 1.src [, and] I run make 1.dst 2.dst 3.dst, I would like GNU make to execute convert -o '{}.dst' 2.src 3.src.
. That's a slightly different problem. You can use the $? automatic variable in a recipe to get the prerequisites that are newer than the rule's target, but for that to serve the purpose, you need a rule with a single target.
Here's one slightly convoluted way to make it work:
DST_FILES = 1.dst 2.dst 3.dst
SRC_FILES = $(DST_FILES:.dst=.src)
$(DST_FILES): dst.a
ar x $< $#
dst.a: $(SRC_FILES)
convert -o '{}.dst' $?
x='$?'; ar cr $# $${x//src/dst}
The dst.a archive serves as the one target with all the .src files as prerequisites, so as to provide a basis for use of $?. Additionally, it provides a workaround for the problem that whenever that target is updated, it becomes newer than all the then-existing .dst files: .dst files that are out of date with respect to the archive but not with respect to the corresponding .src file are extracted from the archive instead of being rebuilt from scratch.

make - Only create intermediate files if needed

I'm writing a Makefile to build a Latex document depending on plots whose data is generated from some other data by some python script.
It looks like this
% pdf plot needed by final document
build/tikz-standalone/%.pdf: build/tikz-standalone/%.tex xy_data
cd $$(dirname $#) && ../../latexrun $$(basename $<)
xy_data: $(PLOT_DATA) tools/plots/crunch.py | build
% crunch.py will create data for plots needed by build/tikz-standalone/%.tex
PYTHONPATH=. tools/plots/crunch.py
build:
mkdir -p build build/other_stuff ...
crunch.py generates several data files in build/data which are needed by build/tikz-standalone/%.tex. To create these files it uses other files stored in the variable PLOT_DATA. I could put a list of the intermediate data files in build/data into the Makefile at the position of xy_data. I don't like this as this would require me to update the list whenever a new file is added. What I want is that all data files are recreated whenever crunch.py or $(PLOT_DATA) has changed.
Is there a way to express this in Make?
If you do not want to provide and maintain the list of the generated files you can turn your (implicitly) phony xy_data target into an empty file used as a marker. Simply touch it at the end of the recipe:
BUILDDIRS := build build/other_stuff ...
build/tikz-standalone/%.pdf: build/tikz-standalone/%.tex xy_data
cd $(dir $#) && ../../latexrun $(notdir $<)
xy_data: $(PLOT_DATA) tools/plots/crunch.py | $(BUILDDIRS)
PYTHONPATH=. tools/plots/crunch.py
touch $#
$(BUILDDIRS):
mkdir -p $#
Note: I also improved a bit some other aspects:
Use of make functions dir and notdir instead of the shell equivalents.
Variable declaration for the build directories to avoid writing the same list several times, which is tedious and error prone.
Explicit list of all build directories as order-only prerequisites instead of just one, which could lead to unexpected results if this single one exists but not some others.
Generic rule for all build directories thanks to the $# automatic variable.

copying only changed files between directories using Makefile

I read this: Makefile: Copying files with a rule but couldn't do it.
To make it simple, suppose I have two directories dir1 and dir2. Under dir1 I have three files: rabbit.c, tiger.c and bus.c .
I made Makefile like this:
dir2/rabbit.c:dir1/rabbit.c
dir2/tiger.c:dir1/tiger.c
dir2/bike.c:dir1/bike.c
dir2/%:
cp -f $< $#
I specified the prerequisites in three separate lines and specified the unified recipe for the three targets. I expected when I touch any file under dir1, make will copy that file to dir2. But this happend only for rabbit.c. What is wrong?
ADD(after selecting an answer) :
After realizing what's wrong by Takkat's answer, I fixed it and later modified it further and I think this is the correct simplest Makefile for this case.
.PHONY:all
LIST:=rabbit.c tiger.c bike.c
DSTFILES:=$(addprefix dir2/, $(LIST))
all: $(DSTFILES)
dir2/%:dir1/%
cp -f $< $#
Make chooses a default target in your makefile and, unless you specify differently on the command line, it builds just that target (and all prerequisites required to build that target).
The default target in a makefile is, by default, the first explicit target listed.
So in your makefile the first rule is:
dir2/rabbit.c:dir1/rabbit.c
so the first explicit target is dir2/rabbit.c, so that's all make builds.
If you want to build multiple targets by default, you need a first target that lists all the "real" targets as prerequisites; put this line first in your makefile:
all: dir2/rabbit.c dir2/tiger.c dir2/bike.c
and it will work. It's often considered good practice to declare targets like this, which don't relate to real files on the disk, as phony:
.PHONY: all

Target not known beforehand in the Makefile

I am trying to use makefile to manage my building process in a small project, where the target number and target names are not known beforehand but depends on the input. Specifically, I want to generate a bunch of data files (say .csv files) according to a cities_list.txt file with a list of city names inside. For example, if the contents of the txt file are:
newyork
washington
toronto
then a script called write_data.py would generate three files called newyork.csv, washington.csv and toronto.csv. When the content of the cities_list.txt file changes, I want make to deal with this change cleverly, i.e. only update the new-added cities files.
I was trying to define variable names in target names to make this happen but didn't succeed. I'm now trying to create a bunch of intermediate .name files as below:
all: *.csv
%.name: cities_list.txt
/bin/bash gen_city_files.sh $<
%.csv: %.name write_data.py
python3 write_data.py $<
clean:
rm *.name *.csv
This seems to be very close to success, but it only gives me one .csv file. The reason is obvious, because make can't determine what files should be generated for the all target. How can I let make know that this *.csv should contain all the files where there exists a corresponding *.name file? Or is there any better way to achieve what I wanted to do here?
All right, this should do it. We'd like a variable assignment at the head of the file:
CITY_FILES := newyork.csv washington.csv toronto.csv
There are two ways to do this. This way:
-include cities.mak
# this rule can come later in the makefile, near the bottom
cities.mak: cities_list.txt
#sed 's/^/CITIES := /' $< > $#
and this way:
CITIES := $(shell cat cities_list.txt)
After we've done one of those two, we can construct the list of needed files:
CITY_FILES := $(addsuffix .csv, $(CITIES))
and build them:
# It is convenient to have this be the first rule in the makefile.
all: $(CITY_FILES)
%.csv: write_data.py
python3 $< $*.name

Prerequisites in assorted directories

I have a variable with a list of prerequisites in varying directories, each specified by a relative path. For example:
HTML_FILES := ../foo1/bar1.html ../foo1/bar2.html ../foo2/bar3.html foo3/bar4.html
(Note that this variable is actually generated, so the full list of folders isn't known in advance.)
For each of these, I want to generate a target file in the current directory, e.g. bar1.xml, bar2.xml, bar3.xml, bar4.xml.
How can I write a rule which will match for this? This is as close as I've come. It seems like something magic in the place of ?????? might do the trick.
build: $(XML_FILES)
$(XML_FILES): %.xml : ??????/%.html
perl $(HTML_TO_XML) $<
Use vpath.
vpath %.html $(dir $(HTML_FILES))
Now one can use simple pattern rule as follows:
$(XML_FILES): %.xml : %.html
perl $(HTML_TO_XML) $<
This should be enough to get things work, but I'm not sure how it would behave if there are some files with the same name in different directories, like ../foo1/bar.html and ../foo2/bar.html.

Resources