How to use make to convert files in subdirectories? - makefile

I have a directory with a bunch of subdirectories. Each subdirectory contains a traj.dat file. I want to use a Makefile to make sure that the file traj.dat gets converted to a different format, and the output file is printed in the same subdirectory as the original file.
Therefore, if I wanted to specify the names of the subdirectories, I could just use:
subdir1/traj.dat.xyz: subdir1/traj.dat
my_convert subdir1/traj.dat subdir1/traj.dat.xyz
subdir2/traj.dat.xyz: subdir2/traj.dat
my_convert subdir2/traj.dat subdir2/traj.dat.xyz
and so on.
How can I get the above result FOR ALL the subdirectories containing a traj.dat file, regardless of their name, without having to list them explicitly?
Cheers!

Assuming you're using GNU make, try:
DATFILES := $(shell find . -name traj.dat)
OUTFILES := $(addsuffix .xyz,$(DATFILES))
all: $(OUTFILES)
%.dat.xyz : %.dat
my_convert $< $#
You weren't really clear what you mean by "subdirectories"; if you just mean immediate subdirectories you can use this instead of the shell function, which is more efficient (and works on Windows):
DATFILES := $(wildcard */traj.dat)

Related

How to print the list of files in a folder from build logs in make file?

We are using make utility in windows to build our project. The version of make is "GNU Make 3.81". I need to understand how our project is built and so have added additional log lines like below which is working as expected --
$(warning Entering componentsWin32.mak )
Additionally I need to find the current directory and the list of files in that directory, for the first one, this is working--
$(warning Entering componentsWin32.make $(CURDIR))
To print the list of files in a directory I tried this but it did-not work --
$(warning Entering componentsWin32.make $(DIR))
Is it possible using $(SHELL...some command) or any other way? Any pointers to this would be helpful.
The function $(wildcard [path string]) will evaluate to a list of files which are desginated by path string: the path string may be written as a glob, so e.g.
$(wildcard $(CURDIR)/src/*.c)
will evaluate to a list of all C files, given that there is a subdirectory src under your current directory with *.c files in it.
You can even pass an arbitrary number of such path expressions:
$(wildcard $(CURDIR)/src/*.c $(MY_INCLUDE_DIR)/*.h)
Obviously, spaces in path names are not allowed.

How to write a Make rule for files of certain type in a directory tree?

Imagine a directory tree (which might be more than one level deep) containing several Markdown files. A PDF version of each file exist in the same directory and must be updated each time the corresponding Markdown file is updated. What rule must be written in a single Makefile in the root directory of this tree to achieve this?
I am looking a for a solution where files can be added or removed from the directory tree without a need for updating the Makefile.
Assumptions:
all markdown files follow a certain pattern in their name; for example they end with a .md postfix.
GNU Make is being used.
You can use $(shell find) to find files recursively. For example:
markdown := $(shell find . -name '*.md')
all: $(patsubst %.md, %.pdf, $(markdown))
%.pdf: %.md
pandoc -o $# $<

How to get makefile to depend on any files inside a folder

I have a simple makefile that I use to build some latex files. The syntax looks like this:
pdf: thesis.tex chapters/a.tex chapters/b.tex chapters/c.tex
latexmk -pdf -pdflatex="pdflatex thesis.tex
open:
open thesis.pdf
The files inside chapters folder can increase further with d.tex, e.tex and may even contain subfolders f\section1.tex, f\section2.tex etc.
I manually add all the requried tex files inside my thesis.tex like this which is not a problem.
\input{chapters/a.tex}
\input{chapters/b.tex}
\input{chapters/c.tex}
\input{chapters/d.tex}
\input{chapters/e.tex}
How can I get make target pdf to depend upon any file changes inside chapters and its subdirectories?
How do I write inter task dependency in makefile. If target open depends upon target pdf, how do I write it?
open: pdf will sort-of do what you want for your second question.
Though it would be better to not use the phony pdf target for this.
Instead have a thesis.pdf: target which depends on the right prerequisites and have both pdf: thesis.pdf and open: thesis.pdf targets.
For the first question you can either use something like:
SRCS := $(shell find chapters -name '*.tex')
or use from here:
rwildcard=$(strip $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)))
SRCS := $(call rwildcard,chapters,*.tex)
and then:
thesis.pdf: thesis.tex $(SRCS)
to use that variable as the prereq.
If you wanted to get even fancier you could write a script to pull out the actual filenames from the \input{} directives in thesis.tex and use that as your SRCS variable (but that's probably not worth the effort unless you know you will have other, unrelated, .tex files).

Wildcard to obtain list of all directories

In my Makefile I need to get a list of all directories present in some other directory.
To get a list of all directories in the same folder as my Makefile I use:
DIRECTORIES = $(wildcard */)
all:
echo $(DIRECTORIES)
which works fine, and gives me the desired list. However if I want to have a list of all directories in another directory using
DIRECTORIES = $(wildcard ../Test/*/)
all:
echo $(DIRECTORIES)
I get a list of ALL files (with paths) in that directory, including .h and .cpp files.
Any suggestions why this happens and how to fix it? Other solutions to obtain the list are also welcome.
Use sort and dir functions together with wildcard:
DIRECTORY = $(sort $(dir $(wildcard ../Test/*/)))
From GNU make manual:
$(dir names...)
Extracts the directory-part of each file name in names. The directory-part of the file name is everything up through (and including) the last slash in it. If the file name contains no slash, the directory part is the string ‘./’.
$(sort list)
Sorts the words of list in lexical order, removing duplicate words. The output is a list of words separated by single spaces.
Also look at the second and the third method in this article: Automatically Creating a List of Directories
Try:
$(shell find <directory> -maxdepth 1 -type d)
which will give you the list of directories inside a given directory without going into subdirectories. You may need to clean that list up since it will include . and ./ before every directory listed.
Use:
$(wildcard ../Test/*/.)
This works fine.

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