Combine path to an array of filenames in makefile - makefile

I've a list of files in a Makefile variable. I use it to call a couple of implicit rules.
In shorts:
CHAPTERS_FOLDER = chapters
CHAPTERS := $(CHAPTERS_FOLDER)/lesson1.Md $(CHAPTERS_FOLDER)/lesson2.Md [...] $(CHAPTERS_FOLDER)/lesson23.Md
$(CHAPTERS_FOLDER)/%.Md : %.xml
mkdir -p $(CHAPTERS_FOLDER)
pandoc -f docbook -t markdown_strict $< -o $#
$(CHAPTERS_FOLDER)/%.Md : %.odt
mkdir -p $(CHAPTERS_FOLDER)
pandoc -t markdown_strict $< -o $#
Is there a way to not have to manually add CHAPTERS_FOLDER to each item in CHAPTER? Something like a cartesian product of strings or something...
Is there a better way to design these rules to build the files?

Why not just use addprefix?
CHAPTERS = lesson1.Md lesson2.Md lesson3.Md ...
CHAPTER_TARGETS = $(addprefix $(CHAPTERS_FOLDER)/,$(CHAPTERS))
Read the first few paragraphs of the manual on file name functions to see that they operate on lists of file names, not single file names.

Related

Makefile for calling make with multiple targets

I have a rule in a makefile which uses sox to convert wav files to another format.
Makefile:
INPUTFILE?=file1.wav
OUTFILE=$(INPUTFILE:.wav=.s32)
wav_to_s32:
#sox $(INPUTFILE) -r 16000 $(OUTFILE)
How would I make this work for multiple files specified at input (with multiple outputs)?
INPUTFILE?=file1.wav file2.wav file3.wav
As explained in this other answer, the key here is pattern rules. If you want to automate all conversions for all files that you list in your INPUTFILE variable you can complete your initial version and that other answer like this:
INPUTFILE?=file1.wav file2.wav file3.wav
OUTFILE=$(INPUTFILE:.wav=.s32)
.PHONY: wav_to_s32
wav_to_s32: $(OUTFILE)
%.s32: %.wav
sox $< -r 16000 $#
This tells make that:
wav_to_s32 is not a real file, it's just a short name for something else (it is a "phony" target).
When invoked with make wav_to_s32 it shall build all files listed in $(OUTFILE).
And if you prefer make to discover automatically the input files, you can further improve all this with:
INPUTFILE := $(wildcard *.wav)
OUTFILE := $(patsubst %.wav,%.s32,$(INPUTFILE))
.PHONY: wav_to_s32
wav_to_s32: $(OUTFILE)
%.s32: %.wav
sox $< -r 16000 $#
Note that make is smart enough to not reconvert a file if it has been converted already and did not change since. It is even the main purpose of make: track inter-files dependencies.
I would use the following pattern rule in your makefile:
%.s32: %.wav
sox $< -r 16000 $#
The rule above will build .s32 files from .wav files.
With that rule in your makefile you could call make for multiple targets in the following way:
$ make foo.s32 bar.s32
sox foo.wav -r 16000 foo.s32
sox bar.wav -r 16000 bar.s32
It generates foo.s32 and bar.s32 from foo.wav and bar.wav, respectively.

Makefile: building LaTeX files in subdirectories with two versions of each file

I have the following folder structure
1st-grade-math-class/
common/
mystyle.sty
mysubstyle.sty
fonts/
font1.ttf
font2.ttf
font3.ttf
week01/
handout.tex
image1.pdf
image2.pdf
week02/
handout.tex
image1.pdf
image2.pdf
...
week13/
handout.tex
output/
[empty]
And I would like to create a Makefile - in the best way - to do the following:
make sure I include the common directory properly in TEXINPUTS
compile the handout.tex into a PDF (using either pdflatex or xelatex) and have it in the output directory as week01-handout-student.pdf
compile the handout.tex with a line of LaTeX prepended to the beginning of the file (that sets a flag) into a PDF and have it in the output directory as week01-handout-teacher.pdf
clean everything up (the log, aux, etc. files)
I am not sure I know how to this in any other way than manually duplicating an elementary Makefile/bash script in every subdirectory, then calling each of them one by one with a for loop.
I would appreciate help on how to build this process, ideally with a single Makefile in the root directory. Thanks.
UPDATE: I purposefully did not want to give any details about how I compile LaTeX, in case somebody has a better suggestion than my current usage. Right now I am using Latexmk (which is already a make-like wrapper of LaTeX):
latexmk -pdf file.tex generates file.pdf
to add the line of code, I do a simple echo "line of code" > temp.tex and cat handout.tex >> temp.tex, then the same latexmk command
latexmk -c file.tex in a directory cleans all temporary files used to compile file.tex
TEXINPUTS is the TeX path variable, to let TeX find (in its path) the style files: I do TEXINPUTS=full-path-to/common and then export TEXINPUTS before compiling anything.
If anybody has a better suggestion, I am a willing taker.
Something like this should do what you want I believe:
OUTDIR := output
# Tell make to export this environment variable to all recipe lines it runs.
export TEXINPUTS := $(abspath common)
# Get the list of all of our week directories.
weekdirs := $(wildcard week*)
#$(info weekdirs:$(weekdirs))
# Create student output filenames from week directory names.
STUDENT_HANDOUTS := $(patsubst %,$(OUTDIR)/%-handout-student.pdf,$(weekdirs))
#$(info STUDENT_HANDOUTS:$(STUDENT_HANDOUTS))
# Create teacher output filenames from week directory names.
TEACHER_HANDOUTS := $(patsubst %,$(OUTDIR)/%-handout-teacher.pdf,$(weekdirs))
#$(info TEACHER_HANDOUTS:$(TEACHER_HANDOUTS))
# Default target depends on all output files.
all: $(STUDENT_HANDOUTS) $(TEACHER_HANDOUTS)
# Pattern rule for building pdf files.
%.pdf:
#echo + Making $# from $^
#echo cd $(#D) && echo latexmx -pdf $(abspath $<)
#echo cd $(#D) && echo latexmk -c $(abspath $<)
# Static pattern rule mapping student output files to input files.
$(STUDENT_HANDOUTS) : $(OUTDIR)/%-handout-student.pdf : %/handout.tex
# Pattern rule to generate temporary input files from original input files.
%/handout-tmp.tex: %/handout.tex
#echo echo 'line of code' '>' $#
#echo cat $^ '>>' $#
# Static pattern rule mapping teacher output files to (temporary) input files.
$(TEACHER_HANDOUTS) : $(OUTDIR)/%-handout-teacher.pdf : %/handout-tmp.tex
Uncomment the $(info) lines to see a bit of how the variables are put together.
This uses latexmk -c to clean up auxiliary files after creating the output files.
Technique 1. The echo foo >$#; cat $< >>$# thing is something I've done before; I think it's reasonable.
Technique 2. Another technique is to have your .tex document, or a package file it uses, include a line like:
\InputIfFileExists{lecturenotes.config}{}{}
That allows you to make certain adjustments in a makefile, as in for example:
# any bits of configuration that should be in all the .config files
SWITCHES=\makeindex
%-notes.pdf: %.tex
printf '$(SWITCHES)\\ExecuteOptions{sidenotes}\n' >lecturenotes.config
TEXINPUTS=styles: pdflatex $<
mv ${<:.tex=.pdf} $#
rm lecturenotes.config
%-single.pdf: %.tex
printf '$(SWITCHES)\\ExecuteOptions{oneside}\n' >lecturenotes.config
TEXINPUTS=styles: pdflatex $<
mv ${<:.tex=.pdf} $#
rm lecturenotes.config
Technique 3. A third technique for controlling LaTeX from outside is to include in your document (or in a package file):
\newif\ifdemonstrator
\expandafter\ifx\csname demonstrator\endcsname\relax
\demonstratorfalse
\else
\demonstratortrue
\fi
...
\ifdemonstrator
\typeout{Demonstrator is TRUE}
\else
\typeout{Demonstrator is FALSE}
\fi
Then call latex with:
%-plain.pdf: %.tex
latex $<
mv ${<:.tex=.pdf} $#
%-demo.pdf: %.tex
latex --jobname ${<:.tex=} '\def\demonstrator{x}\input{$<}`
mv ${<:.tex=.pdf} $#
Technique 1 is a bit of a blunt instrument, but if that's all that's needed, it's pretty lightweight.
Technique 2 is probably the most neatly engineered, but it is slightly more effort.
Technique 3 is probably the one I use most in this sort of circumstance.

Make Placeholder Dependence

I'm using make to copy files to a DEST directory. I have the following rule
$(THUMBS): $(DEST)/% : %
mkdir -p $(dir $#)
cp $^ $#
The problem is that sometimes the source file may not exist. Rather than generating an error, I would rather copy a placeholder file instead.
I tried adding the placeholder as a dependence with the actual sources as intermediates. That kind of worked, but then if the placeholder is updated make overwrites all of the actual source files with it.
Is there an elegant way to accomplish this?
If the files in $(DEST) are being built externally (that is, not via a make recipe), then you can do this by embedding a little shell script in your recipe:
$(THUMBS):
mkdir -p $(#D)
for file in $(DEST_FILES); do\
if [[ -f $file ]]; then\
cp -f $file $#;\
else\
cp -f $(PLACEHOLDER_FILE) $#;\
fi;\
done
You aren't listing the files in $(DEST) as prerequisites, so make should never try to rebuild them. You will need to set PLACEHOLDER_FILE to the name of the placeholder file that you wish to use for missing files, and set DEST_FILES to the list of files that you expect to see in DEST. The downside is that without prerequisites, make won't know when it doesn't actually need to re-run this rule. You will run it unconditionally every time.
How about this:
$(DEST)/% : %
mkdir -p $(dir $#)
cp $^ $#
$(DEST)/% :
mkdir -p $(dir $#)
touch $#

Directory wildcard in Makefile pattern rule

I'm trying to create a Makefile that will compile terminfo files residing in a directory via tic. tic also copies the termcap files it creates automatically to a system- or user-specific destination folder. For a regular user if the terminfo file is e.g. screen-256color-bce-s.terminfo, it will be compiled and copied to ~/.terminfo/s/screen-256color-bce-s. So it will look something like this:
terminfo/screen-256color-bce-s.terminfo => /home/user/.terminfo/s/screen-256color-bce-s
terminfo/screen-256color-s.terminfo => /home/user/.terminfo/s/screen-256color-s
If I put something like this into my Makefile:
TISRC = $(wildcard terminfo/*.terminfo)
TIDST = $(foreach x, $(TISRC), $(HOME)/.terminfo/$(shell basename $x|cut -c 1)/$(shell basename $x .terminfo))
$(HOME)/.terminfo/s/%: terminfo/%.terminfo
#echo "$< => $#"
#tic $<
install: $(TIDST)
it works. However, I'd like to make it general, and use a wildcard in the target, i.e.:
$(HOME)/.terminfo/**/%: terminfo/%.terminfo
#echo "$< => $#"
#tic $<
to be able to add terminfo files to my local repository. The above, however, does not work. How can I specify a wildcard directory in a pattern rule?
You can do that with GNU Make Secondary Expansion feature:
all : ${HOME}/.terminfo/x/a
all : ${HOME}/.terminfo/y/b
.SECONDEXPANSION:
${HOME}/.terminfo/%: terminfo/$$(notdir $$*).terminfo
#echo "$< ---> $#"
Output:
[~/tmp] $ make
terminfo/a.terminfo ---> /home/max/.terminfo/x/a
terminfo/b.terminfo ---> /home/max/.terminfo/y/b
As a side note, make provides some path manipulation functions, so that you don't really need to invoke the shell for that.
I don't think you can use wildcards the way you're trying to, but if you don't mind using eval trickery, you can get the effect you're shooting for without having to spell out all the directory paths explicitly:
TISRC = $(wildcard terminfo/*.terminfo)
BASENAMES = $(notdir $(basename ${TISRC}))
MKDST = ${HOME}/.terminfo/$(shell echo $1 | cut -c 1)/$1
TIDST := $(foreach s,${BASENAMES},$(call MKDST,$s))
DIRLTRS = $(notdir $(patsubst %/,%,$(sort $(dir ${TIDST}))))
install: ${TIDST}
# $1 - Directory Name
# $2 - File name
define T
${HOME}/.terminfo/$1/$2 : terminfo/$2.terminfo
#echo "$$< => $$#"
tic $$<
endef
# This is the tricky part: use template T to make the rules you need.
$(foreach d,${DIRLTRS},$(foreach f,${BASENAMES},$(eval $(call T,$d,$f))))

How can I write a shell script program to go through all my esql files and then execute that file to create exe files

This is what I mean:
ls -l *.ec
For each result found, I would like to compile it one at a time.
e.g., for $something.ec:
esql $something.ec -o $something
$something.ec is original file
$something is target exe file.
My question is: how can I put that into a loop to go through all the files in my directory?
You can use a for-loop as follows:
for file in *.ec
do
esql "$file" -o "${file//.ec}"
done
I recommend you write a simple Makefile:
INPUTS = $(wildcard *.ec)
all: $(INPUTS:.ec=)
%: %.ec
esql $# -o $<
and then simply run make (or make -B to force execution) whenever you want to re-execute them.

Resources