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

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.

Related

Issue with Makefile target rule and PHONY

I know this could be done in different ways, but I am actually looking for learning more about makefiles.
I want to be able to call make somefile.txt and run a given script.
In my current folder I have:
makefile
%.txt:
# perl -pe "s/#.*//g; s/^\n//g; s/\n/\t/g;" .txt
echo "Hi"
When I call make, I am getting
make: `somefile.txt' is up to date.
I know I would need to use .PHONY, but I am having trouble trying to use it with %.txt.
I already tried things such as
files = *.txt
.PHONY = $(files)
%.txt:
# perl -pe "s/#.*//g; s/^\n//g; s/\n/\t/g;" .txt
echo "Hi"
But that didn't actually work.
The (a) conventional way to do this is to make your real target have a phony one as a prerequisite. Phony targets are always considered initially out of date, so anything that depends on a phony target will also always be considered out of date. "force" is a conventional name for a target used for this purpose.
For example,
.PHONY: force
force:
%.txt: force
echo "Hi at $$(date)" > $#
As demonstrated in the example, this does not require the phony target to have a recipe.
If you don't want to list your targets to be built in your makefile but instead just give them on the command line, you can use this:
.PHONY: $(MAKECMDGOALS)
The variable MAKECMDGOALS will be set to the set of goals you gave make on the command line.
It's important to understand that a makefile is not the shell, and so just sticking *.txt anywhere in your makefile won't always expand it. Only particular areas of the makefile work with shell globs directly.
If you want to always expand it, you can use make's wildcard function like this:
files = $(wildcard *.txt)

How to do Makefile Pattern Rules with List of Files

Wondering how to apply something such as pattern rules to accomplish the following in the Makefile:
FILES := a b c d
$(FILES).c:
run build $(FILE).c
# use $(FILE) without .c extension, etc.
Without having to name all of the values in FILES like a.c, b.c, etc. because I am going to use the names elsewhere.
If I try the above with make a.c, I get:
No rule to make target `a.c'. Stop.
The addsuffix built-in function, which already operates on lists, could be useful here:
targets := $(addsuffix .c,$(FILES))
However, you could simply use pattern rules instead. The automatic variable $* would correspond to the stem (i.e., the % part):
%.c:
run build $#
# use $* for filename without .c extension, etc.

GNU make - transform every prerequisite into target (implicitly)

I have another make-like tool that produces an XML as an artifact after parsing my makefile which I'll then further process with Python.
It'd simplify things for me - a lot - if I could have make consider every single prerequisite to be an actual target because then this other tool
will classify each and every file as a "job".
This is a fragment of my makefile:
.obj/eventlookupmodel.o: C:/Users/User1/Desktop/A/PROJ/src/AL2HMIBridge/LookupModels/eventlookupmodel.cpp C:\Users\User1\Desktop\A\PROJ\src\AL2HMIBridge\LookupModels\eventlookupmodel.h \
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qabstractitemmodel.h \
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qvariant.h \
...
I'd want for make to think I have a dummy rule for each prerequisite such as below:
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qvariant.h:
#echo target pre= $#
C:/Users/User1/Desktop/A/PROJ/qt5binaries/include/QtCore/qabstractitemmodel.h:
#echo target pre=$#
C:/Users/User1/Desktop/A/PROJ/src/AL2HMIBridge/LookupModels/eventlookupmodel.cpp :
#echo target pre=$#
C:\Users\User1\Desktop\A\PROJ\src\AL2HMIBridge\LookupModels\eventlookupmodel.h:
#echo target pre=$#
I don't care about the exact form of the rule just that each file is considered an actual target.
My method of passing in this rule would be by setting the MAKEFILES variable like so
make all MAKEFILES=Dummy.mk
with Dummy.mk containing this rule so that I do not modify the makefiles.
I've tried the following so far.
Dummy.mk:
%.h:
#echo header xyz = $#
%:
#echo other xyz= $#
This partially works.
I run make all --trace --print-data-base MAKEFILES=Dummy.mk and I can see that
make does "bind" the %.h: rule to the header files. In the --print-data-base section, I see that rule being assigned to the header files.
C:/Users/User1/Desktop/A/QNX_SDK/target/qnx6/usr/include/stddef.h:
# Implicit rule search has been done.
# Implicit/static pattern stem: 'C:/Users/User1/Desktop/A/QNX_SDK/target/qnx6/usr/include/stddef'
# Last modified 2016-05-27 12:39:16
# File has been updated.
# Successfully updated.
# recipe to execute (from '#$(QMAKE) top_builddir=C:/Users/User1/Desktop/A/HMI_FORGF/src/../lib/armle-v7/release/ top_srcdir=C:/Users/User1/Desktop/A/HMI_FORGF/ -Wall CONFIG+=release CONFIG+=qnx_build_release_with_symbols CONFIG+=rtc_build -o Makefile C:/Users/User1/Desktop/A/HMI_FORGF/src/HmiLogging/HmiLogging.pro
', line 2):
#echo header xyz = $#
However, I do NOT see the "echo header xyz $#"-rule being executed.
Regarding the %: rule, it is neither executed for the .cpp files nor "bound" to them in the --print-data-base section.
However, it is bound and executed for existing targets which have no suffix i.e.
all: library binary
binary: | library
ifs: | library
For the %: rule, the reason for this behavior is because of 10.5.5 Match-Anything Pattern Rules: If you do not mark the match-anything rule as terminal, then it is non-terminal. A non-terminal match-anything rule cannot apply to a file name that indicates a specific type of data. A file name indicates a specific type of data if some non-match-anything implicit rule target matches it.
If I make it non-terminal - no double colon - then the rule doesn't apply to built-in types like .cppunless I un-define the built-in rules that negate my intended %: rule.
If I make it terminal, "it does not apply unless its prerequisites actually exist". But a .h or .cpp doesn't technically have prerequisites; can I just create a dummy file and have that as its prerequisite?
NOTE: This has NOTHING to do with gcc -M generation. Yes the -M option would help in the specific case of header and source files but this question is for more generic targets and prerequisites that already exist in the makefile when make is launched.
This may take a few iterations. Try:
%.h: null
#echo header xyz = $#
%: null
#echo other xyz= $#
null:
#:
Try generating static pattern rules for the header files. See one of the answers to Make ignoring Prerequisite that doesn't exist.
Static pattern rules only apply to an explicit list of target files like this:
$(OBJECTS): %.o: %.c
*recipe here*
where the variable OBJECTS is defined earlier in the makefile to be a list of target files (separated by spaces), for example:
OBJECTS := src/fileA.c src/fileB.c src/fileC.c
Note that you can use the various make utility functions to build that list of target files. For example, $(wildcard pattern), $(addsuffix), etc.
You should also ensure that the recipe "touches" the header file to change the timestamp.
I've found that using static pattern rules instead of pattern rules fixes problems where make doesn’t build prerequisites that don’t exist, or deletes files that you want.
Here is an example of using wildcard to copy files from one directory to another.
# Copy images to build/images
img_files := $(wildcard src/images/*.png src/images/*.gif src/images/*.jpg \
src/images/*.mp3)
build_images := $(subst src/,$(BUILD_DIR)/,$(img_files))
$(build_images): $(BUILD_DIR)/images/% : src/images/%
mkdir -p $(dir $#)
cp -v -a $< $#
There are other make functions like addprefix that could be used to generate a more complex file specification.

Fake dynamic files in Makefile

I want to run pocketlint on all **/*.js files.
.PHONY: lint_js2
LINT_JS = $(wildcard static/js/*.js static/js/**/*.js)
LINT_JS_TARGETS = $(addprefix lint__,$(LINT_JS))
#.PHONY: $(LINT_JS_TARGETS)
lint_js2: $(LINT_JS_TARGETS)
echo $<
lint__%: %
pocketlint $<
However, I get this error:
make: *** No rule to make target `lint__static/js/ad_list.js', needed by `lint_js2'. Stop.
Why lint__static/js/ad_list.js is not captured by lint__%?
If I uncomment second .PHONY, it echoes lint__static/js/ad_list.js, but does not invoke pocketlint static/js/ad_list.js. Why?
If my approach is wrong, what would be the right approach? Since tasks are independent, I would appreciate if make -j50 would do what I am expecting.
Thanks!
It's not clear what is intended: does pocketlint write a file named lint__static/js/ad_list.js, or is that really a phony filename? Anyway...
Reread the second paragraph of How Patterns Match:
When the target pattern does not contain a slash (and it usually does not), directory names in the file names are removed from the file name before it is compared with the target prefix and suffix. After the comparison of the file name to the target pattern, the directory names, along with the slash that ends them, are added on to the prerequisite file names generated from the pattern rule's prerequisite patterns and the file name.
In short, % generally matches just a filename, not a pathname with slashes in it. So lint__static/js/ad_list.js is not captured because actually it is only ad_list.js that is being matched against lint__%.
If you can arrange it so that the output files from pocketlint are static/js/lint__ad_list.js etc, then this could be made to work:
LINT_JS_TARGETS = $(foreach f,$(LINT_JS),$(dir $f)lint__$(notdir $f))
lint__%: %
pocketlint $<
Alternatively you can make % match pathnames by having the target pattern be a pathname (containing a slash):
LINT_JS_TARGETS = $(addprefix linted/,$(LINT_JS))
linted/%: %
pocketlint $<
This time % = static/js/ad_list.js does match the pattern rule.
In either case, you're going to have to have pocketlint produce output (if indeed it produces output) named differently than lint__static/*.
Implicit rule search is suppressed for phony targets (see Phony Targets, paragraph 5). So the rule involving pocketlint is never considered when lint__static/js/ad_list.js is phony.
It's not obvious why the result is Nothing to be done for (phony) lint__static/js/ad_list.js rather than No rule to make target lint__static/js/ad_list.js, but I wouldn't lose sleep over it!

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