Dynamic Makefile target - makefile

Here's a snippet of a GNU Makefile I'm working with. Basically, I have a directory of images that i want copied into a dist directory when running make, but I'd prefer not to list each image individually.
DISTDIR := dist
IMG := $(shell find app/img -type f)
$(subst app/img, $(DISTDIR), $(filter-out %.svg, $(IMG)))): $(filter-out %.svg, $(IMG))
#cp $? $(DISTDIR)
This sort of works, except any time a source file is changed (in app/img), make always remakes the target file. What's the correct way to handle this situation?

It would be easier to answer if you provided some sample results for the find. Let's say that the find operation returns the files: app/img/foo.svg, app/img/bar.jpg, and app/img/baz.png.
In that case the filter-out functions will return the files app/img/bar.jpg and app/img/baz.png. This means that the rule generated will look like this:
$(DISTDIR)/bar.jpg $(DISTDIR)/baz.png: app/img/bar.jpg app/img/baz.png
#cp $? $(DISTDIR)
Here's the thing: when you define N targets in an explicit rule, it's like defining N rules, one for each target, with all the same prerequisites. So the above is identical to writing this:
$(DISTDIR)/bar.jpg: app/img/bar.jpg app/img/baz.png
#cp $? $(DISTDIR)
$(DISTDIR)/baz.png: app/img/bar.jpg app/img/baz.png
#cp $? $(DISTDIR)
Now you can see why you get the behavior that you do: each target lists ALL the files as prerequisites, so whenever ANY file is changed the target is rebuilt.
The answer is that in make, you should generally try to write rules that create exactly one target from just that target's prerequisites. One simple way to do that is with pattern rules; a pattern rule for the above might look like:
DISTDIR := dist
$(DISTDIR)/% : app/img/%
#cp $< $#
After that, all you have to do is declare a target which depends on the files you want created:
IMG := $(shell find app/img -type f)
all: $(IMG:app/img/%=$(DISTDIR)/%)
and make will figure out the rest!

Related

Building nested directories using makefile

I tried to build a nested directories following: Iterating through a list of directories in a Makefile
Here is my script
LAYER1 = a b
LAYER2 = 1 2
$(LAYER1)/foo%/run: $(DIR)
mkdir -p $#
DIR: $(addsuffix /foo$(LAYER2)/run, $(LAYER1))
The expected output is creating a/foo1/run a/foo2/run b/foo1/run b/foo2/run but the output is only 1 directory named a. How do I create the expected directory structure?
What does the rule:
$(LAYER1)/foo%/run: $(DIR)
expand to? It expands to this:
a b/foo%/run: $(DIR)
(I don't know what $(DIR) is supposed to be) which is an explicit rule that tells make how to build two things: a and b/foo%/run (this is not a pattern rule because not all the targets contain %).
Since a here is the first target in the makefile, it's the one that will be run by default when you run make.
What does $(addsuffix /foo$(LAYER2)/run, $(LAYER1)) do? It takes every word in $(LAYER1) and prefixes it by the string /foo$(LAYER2)/run. What is that string? It's /foo1 2/run. So the result of this is:
a/foo1 2/run b/foo1 2/run
which means DIR has the files a/foo1, 2/run, b/foo1, and 2/run as dependencies.
If LAYER1 has multiple words you need to loop through it, not just use it as-is.
See MadScientist's answer as to why your answer would not work. As far as something that will work, you likely want code that looks like this:
LAYER1 = a b
LAYER2 = 1 2
DIRS := $(foreach L1,$(LAYER1),\
$(foreach L2,$(LAYER2),\
$(L1)/foo$(L2)/run))
all: | $(DIRS)
$(DIRS):
mkdir -p $#
Notice the use of order-only dependencies as the prerequisite type for $(DIRS)

Makefile applies a rule recursively even if it shouldn't

I have a very bizzare problem with GNU make. I have the following files:
a/x.html
b/Makefile
b/c/Makefile
The contents of a/x.html are irrelevant. The contents of b/Makefile are as follows:
SRC=../a
all: x.html
%.html: ${SRC}/%.html
rsync $< $#
The contents of b/c/Makefile are the same, except for the definition of SRC:
SRC=../../a
If I run make in b/c/ the result is as expected:
rsync ../../a/x.html x.html
and x.html gets copied from a/ to b/c/.
However, if I run make in b/ the output I get is several lines of:
make: stat: ../a/../a/.. (repeated many times) ../a/x.html: File name too long
It seems that make is applying the rule for %.html recursively, but why? Is there something obvious I am missing?
To build a target that matches the pattern %.html (i.e. any target name that ends in .html), make applies the rule if it can build the dependency (target built from the original target with ../a/ prepended).
You ask to build x.html. This matches the pattern %.html, so the rule applies: make sees if it can build ../a/x.html.
../a/x.html matches the pattern %.html, so the rule applies: make sees if it can build ../a/../a/x.html.
../../a/x.html matches the pattern %.html, so the rule applies, etc.
The stem character can match any part of a path, including directory separators.
You can see what make is trying by running make -r -d (-d to show debugging output, -r to turn off built-in rules which would cause a huge amount of noise).
When you're in b/c, this stops at step 2 because ../../a/x.html exists but ../../../../a/x.html doesn't.
One way to fix this is to list the files on which you want to act. You can build that list from the list of files that already exist in ../a:
$(notdir $(wildcard ${SRC}/*.html)): %.html: ${SRC}/%.html
rsync $< $#
This has the downside that if the HTML files in ../a are themselves built by a rule in b/Makefile, then running make in b won't built them in a pristine source directory. This shouldn't be a problem though: it would be unusual to have a makefile in b build things outside b.
Another approach which doesn't have this defect is to use an absolute path.
%.html: $(abspath ${SRC})/%.html
rsync $< $#

Make target using a pattern

I'm doing something which it feels like should be pretty straightforward. I have source files in a directory called ./src which are transformed and saved to ./. For the sake of the question, I'll just say they get copied there.
Here's what the directory looks like before building:
/src/lib/foo.js
/src/lib/mod/bar.js
/src/bin/baz.js
/Makefile
Here's what should be there after building:
/src/lib/foo.js
/src/lib/mod/bar.js
/src/bin/baz.js
/lib/foo.js
/lib/mod/bar.js
/bin/baz.js
/Makefile
In my Makefile I have this:
SRC_FILES := src/lib/foo.js src/lib/mod/bar.js src/bin/baz.js
OUT_FILES := lib/foo.js lib/mod/bar.js bin/baz.js
These are generated from find and a pattern substitution, but are listed like this here for simplicity...
Now, what I was hoping would work would be:
%.js: src/%.js
cp $< $#
But when I run make lib/foo.js I get Nothing to be done for 'lib/foo.js'.
Looking at the output from debugging, it appears to be trying to find a dependency named lib/src/foo.js. This is kind of what is described in the manual.
This feels as though it should be a really easy thing! What am I doing wrong?
Additionally, I tried:
$(OUT_FILES): $(SRC_FILES)
cp $< $#
...which works, but it rebuilds every single file if any of the sources change. (Remember that my actual transformation isn't just cp, it's a relatively expensive operation, so this is undesirable)
Found something which works. Using secondary expansion, you have access to the target variable in the dependencies. I saw this earlier, but missed the all-important thing: adding an empty target called .SECONDEXPANSION
.SECONDEXPANSION:
all: $(OUT_FILES)
%.js: src/$$#
cp $< $#

Directory independent target in Makefile

I'm trying to make a rule that will generate files regarding their names but regardless of the directory.
I'm starting from this makefile (see a previous question of mine):
TARGETS:=$(patsubst %_tpl,%,$(wildcard *_tpl))
.PHONY: all
all: $(TARGETS)
.SECONDEXPANSION:
$(TARGETS): %: $$(wildcard %*_tpl)
./generate $#_tpl > $#
With this, I can do, for instance, make foo.xml. It looks if a set of foo.xml*_tpl files are there, consider them as prerequisites and call the generate script to generate the target.
What I would like to do is, for example, make ../ressources/foo.xml and have make use the rule to create foo.xml but creating it in the ../ressources/ directory, without having to explicitely specify this directory in the makefile.
What I have tried for the moment is adding this to the Makefile:
../ressources/%: $(notdir %)
mv $< $#
Which works, but I would like to avoid creating the file in the current directory before moving it to the destination folder. I would also like not having to specify the possible destination folders in the makefile (but this is less important).
But first of all, does this make any sense? Or is what I want to do just conceptually wrong?
EDIT: Some precisions regarding the _tpl files and the generate script to avoid confusions:
Each target has a main template ($#_tpl) that includes others ($#-part1_tpl, $#-part2_tpl...) and the generate script only takes the main template as argument. The templates are written with Jinja2 (the subparts included with the {% include %} jinja directive).
If you always want the targets in another directory, just say so.
TARGETS:=$(patsubst %_tpl,../resources/%,$(wildcard *_tpl))
.PHONY: all
all: $(TARGETS)
.SECONDEXPANSION:
$(TARGETS): ../resources/%: $$(wildcard %*_tpl)
./generate $#_tpl > $#
I'm not sure if you should have generate $^ >$# instead; superficially, this would make more sense.
If there are multiple *_tpl files for each target (i.e. there are more tpl files than xml files), the TARGETS definition isn't really correct; but we don't have enough information to actually fix it.
On the other hand, if the target directory can change a lot, the sane way forward might be to cd into the target directory and use make -f ../path/to/Makefile -- just make sure your VPATH is set up so that the source files can be found.

GNU Make -- Append to a variable all targets matching a pattern

Before I start, I'll mention that I'm not using GNU Make in this case for building a C/C++ project.
Makefile:
DEST_DIR = build/
SRC_DIR = src/
$(SRC_DIR)a/ : $(SOMETHING_ELSE)
$(DO_SOMETHING_TO_GENERATE_A_DIR)
$(DEST_DIR)% : $(SRC_DIR)%
cp -r $^ $#
ALL_DEPS += <SOMETHING>
... more code which appends to ALL_DEPS ...
.PHONY: all
all : $(ALL_DEPS)
I've got some files not generated via Make rules in $(SRC_DIR). (For the sake of this example, let's say there's a directory $(SRC_DIR)b/ and a file $(SRC_DIR)c .)
I want to append to ALL_DEPS all targets which represent files or directories in $(DEST_DIR) so that "make all" will run all of the available $(DEST_DIR)% rules.
I thought to do something like this:
ALL_DEPS += $(addprefix $(DEST_DIR),$(notdir $(wildcard $(SRC_DIR)*)))
But of course, that doesn't catch anything that hasn't yet been made. (i.e. it doesn't append $(DEST_DIR)a/ to the list because $(SRC_DIR)a/ doesn't yet exist when the $(wildcard ...) invocation is evaluated and the shell doesn't include it in the results returned by the $(wildcard ...) invocation.)
So, rather than a function which finds all (currently-existing) files matching a pattern, I need one which finds all targets matching a pattern. Then, I could do something like this:
ALL_DEPS += $(addprefix $(DEST_DIR),$(notdir $(targetwildcard $(SRC_DIR)*)))
If it matters any, I've got much of the GNU Make code split across multiple files and included by a "master" Makefile. The ALL_DEPS variable is appended to in any of these files which has something to add to it. This is in an attempt to keep the build process modular as opposed to dropping it all in one monster Makefile.
I'm definitely still learning GNU Make, so it's not unlikely that I'm missing something fairly obvious. If I'm just going about this all wrong, please let me know.
Thanks!
It is simply not possible to do what you're trying to do; you're trying to get make to recognise something that doesn't exist.
This is part of the reason why, in general, wildcards are bad (the other being that you can end up including stuff you didn't mean to). The right thing to do here is to explicitly create a list of source files (ls -1 | sed -e 's/\(.*\)/sources+=\1/' > dir.mk) and perform the patsubst transformation on that list.
If you have additional files that are generate as part of the build, then you can append them to that list and their rules will be found as you'd expect.

Resources