Prerequisites in assorted directories - makefile

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.

Related

Loop through files in make file

Right now I have the following in my make file to create a symbolic link to a file in my current directory:
MY_FILE := "$(SOME_PATH)/file.txt"
ln -s $(MY_FILE)
What I would like is to do some sort of glob operation to link a bunch of files (or none).
MY_FILES := "$(SOME_PATH)/*.txt"
for file in files:
ln -s $(MY_FILE)
Could anyone point me in the right direction to do this?
Thanks!
The following should do what you want:
MY_FILES := $(wildcard $(SOME_PATH)/*.txt)
LINKS := $(notdir $(MY_FILES))
.PHONY: links clean-links
links: $(LINKS)
$(LINKS): %: $(SOME_PATH)/%
ln -s $<
clean-links:
rm -f $(LINKS)
Explanation:
Make functions. wildcard and notdir are two make functions. Knowing the make functions (at least the most frequently used) really helps writing nice, elegant and efficient make files. Of course, if you have spaces or special characters in your file names you will encounter some problems because most make functions consider spaces as separators. But if you have such files and directory names you should probably use something else than make.
Static pattern rules:
$(LINKS): %: $(SOME_PATH)/%
is a static pattern rule. For each word foo.txt in $(LINKS) it instantiates one single make rule:
foo.txt: $(SOME_PATH)/foo.txt
ln -s $(SOME_PATH)/foo.txt
Knowing how to use pattern rules (static or not) is essential if you want to write compact and generic make files.
Automatic variables. $< is a make automatic variable. In recipes (the commands part of rules) it expands as the first listed pre-requisite. There are many other automatic variables and they are quite handy to write generic rules.
Phony targets. links and clean-links are phony targets because they do not correspond to real files that we want make to create or update. They are kind of short-hands for actions. make links creates all missing links and make clean-links removes them all. As make has no way to guess that these targets are not regular file names we tell it with the .PHONY special target.
Make creates "targets" from "prerequisites". In your case MY_FILES holds prerequisites to create the (link) targets. The rule for a single file might look like this:
file.txt: $(SOME_PATH)/file.txt
ln -s $<
You want multiple files, and in this case we can use this pattern rule:
%.txt: $(SOME_PATH)/%.txt
ln -s $<
You can now create links with the make commands:
make file.txt
make otherfile.txt
Finally we will look into the wildcard function to get all files, and the function notdir function to get the link names from the file names. Following rule will print all text files, and depends on all files in SOME_PATH linked to current working directory:
MY_FILES := $(notdir $(wildcard $(SOME_PATH)/*.txt))
cat: $(MY_FILES)
cat $^
%.txt: $(SOME_PATH)/%.txt
ln -s $<

Makefile Rule to Move Nested Files

So I am trying to write a Makefile to remove files nested in directories with the same names. As an example, I have a directory with two files whose paths are ./1234/1234.txt and ./567/567.txt. I want to move those files up to the top directory so that they are just ./1234.txt and ./567.txt. I tried something like this but it didn't work:
variable = ./1234/1234.txt ./567/567.txt
run: $(variable)
$(variable): $$(notdir %): %
mv $< $#
It seems like something that would be a common problem but I haven't been able to find an answer.
Thanks so much for the help! :)
You have your targets the wrong way round, and trying to use functions in a rule won't work unless you use secondary expansion. You'll also need to conditionally add the dependencies because otherwise your rule will give an error if you've already moved the files.
variable = ./1234.txt ./567.txt
run: $(variable)
.SECONDEXPANSION:
$(variable): ./%.txt: $$(wildcard ./$$*/$$*.txt)
mv $< $#

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.

How to generate list of make targets automatically by globbing subdirectories?

I would like to use a single Makefile to generate targets in hundreds of subdirectories. Each subdirectory is a date/time stamp like this: 20120119_153957, which matches the following pattern ????????_??????. There are no other subdirectories that match this pattern.
One target I would like to generate is called ????????_??????/graph.pdf. I have a script called make_graph that will make the graph given the subdirectory name. But I'm not sure how to write a Makefile that will automatically glob all of the subdirectores and generate these targets programmatically.
For example, the code SUBDIRS:=????????_?????? seems to correctly glob all of the subdirectories. I can check with this rule:
.PHONY: print
print:
echo $(SUBDIRS)
However this variable assignment
TARGETS:=$(SUBDIRS:%=%/graph.pdf)
does not seem to do what I expect and assign lots and lots of targets. Instead the following rule just prints one target.
.PHONY: print
print:
echo $(TARGETS)
It is very confusing that SUBDIRS should have the correct subdirectories but TARGET only has one file.
In your example glob matching is performed by the shell.
GNU Make has the built-in wildcard function, which you can use as follows:
SUBDIRS := $(wildcard ????????_??????)
Now you can use this variable to construct a list of targets:
.PHONY : all
all : $(SUBDIRS:%=%/graph.pdf)
%/graph.pdf : # list prerequisites here.
# recipe to make '$#' in directory '$(#D)' from '$^'.
See also: pattern rules, automatic variables.

Tricky "make" target requirements

I have a situation where I need to compile some source files from a library into my own program. The directories the source files are in are not writeable by me. Instead I have a local "build" directory where all the work is done.
The problem I am having the the translation of the paths. The source files are named, say xxxx.cpp and yyyy.cpp, and they are in /path/to/source/xxxx/xxxx.cpp and /path/to/source/yyyy/yyyy.cpp.
Using $(patsubst ...) I can happily convert those paths to build/xxxx/xxxx.cpp etc, but I can't get it to strip the first xxxx off.
I could do with crafting a target that would match something like this:
build/%.o: /path/to/source/%/%.cpp
$(CXX) ...
...but I can't get that to work at all. I guess it doesn't like the double wildcard in the latter part of the target.
The "source" for the names is a single variable with just the "xxxx" and "yyyy" in:
SYS_LIBS = xxxx yyyy
Any suggestions on how to get something like this to work?
Oh, I need it to be a "generic" solution - this will be an included makefile in many projects that use this library of files, so hand-crafting a target per file is not an option. I cannot predict what files will be in the library.
One easy way is to use vpath to let make find the files itself. You just define
vpath %.cpp /path/to/source
vpath %.cpp /path/to/source2
vpath %.cpp /path/to/source3
build/%.o : %.cpp
$(CXX)
You can define more than one path to source that way, but be careful if you have the same file in more than one folder (e.g. a.cpp in both path_to_source1 and path_to_source2)
Personally I'd use vpath, as Bruce suggests. If there are many directories under /path/to/source/ you can use
SRCS = $(wildcard /path/to/source/*)
vpath %.cpp $(SRCS)
(This works as long as the directories go only one level down from there. If there are things like /path/to/source/foo/bar/zzzz/zzzz.cpp, then you'll have to fall back on something like find.)
If you really want to do path translation, this will do it:
X := $(patsubst %.cpp,build/%.o,$(notdir $(X)))
Or (I don't know why you'd want to do it this way, but you could):
X := $(shell echo $(X) | sed 's|.*/\(.*\)\.cpp|build/\1\.o|')

Resources