Makefile recipe that works when target file base name is different from prerequisite file? - makefile

I have a Makefile in which a couple of targets need to be made the same way, but one of the targets is a file whose basename is different from its prerequisite. Here is a minimal example:
ABOUT.html: README.md
help.html: help.md
%.html: %.md
pandoc --standalone --quiet -f gfm -H $(github-css) -o tmp.html $<
inliner -n < tmp.html > $#
rm -f tmp.html
With this Makefile, help.html gets made but ABOUT.html is never made. The reason, I presume, is because the base file name for %.html and %.md don't match up in the case of ABOUT.html because that target depends on README.md.
Is there a way to make this work without having to make a separate recipe for ABOUT.html?

One option is to create ABOUT.md symlink, so that your pattern rule works, by adding the following rule:
ABOUT.md : README.md
ln -s ${<F} ${#F}
You may like to avoid using the same temporary file in the recipe because that breaks in parallel builds. A better way is to use a unique temporary file for the target based on the target name:
%.html: %.md
pandoc --standalone --quiet -f gfm -H $(github-css) -o $#~ $<
inliner -n < $#~ > $#
rm -f $#~

Related

How to make a recursive make evaluate sub-directory pre-requisites?

Appreciating that the title is not quite on "target", how can I make it so that when I call make at the top level, it will recursively call the makefiles in the sub-directories?
Having been intrigued by the Kconfig pattern, to learn it, I've applied it to a mark down to pdf generator.
The recursive Makefile resides in ./scripts/Makefile.boilerplate and is defined:
HEADER=$(wildcard section-header.md)
.PHONY:all clean $(md-sub-y)
all clean: $(md-sub-y)
all: $(TARGET)
clean:
# $(RM) $(TARGET)
$(TARGET): $(HEADER) $(md-y) | $(md-sub-y)
# cat /dev/null $^ > $#
$(md-sub-y):
# $(MAKE) -C $(#D) TOPDIR=$(TOPDIR) $(MAKECMDGOALS)
I'm likely using the order-only prerequisite for the $(TARGET) target inappropriately, but it solved a minor problem.
In each directory there is a unique KConfig file (not shown), which lists CONFIG_{OPTION} macros that evaluate to either y or n. Then each directory contains a Makefile that has the form:
include Kconfig
md-$(CONFIG_INTRODUCTION)+= Introduction.md
md-$(CONFIG_FW_UPDATE)+= FW-update.md
md-sub-$(CONFIG_CHAPTERS)+= Chapters/Chapters.md
md-$(CONFIG_CHAPTERS)+= Chapters/Chapters.md
md-$(CONFIG_EXAMPLES)+= Examples.md
md-$(CONFIG_APPENDIX_I)+= Appendix-I.md
md-$(CONFIG_APPENDIX_II)+= Appendix-II.md
md-$(CONFIG_APPENDIX_III)+= Appendix-III.md
include ${TOPDIR}/scripts/Makefile.boilerplate
And finally, the very top level makefile is (abbreviated):
.PHONY: all clean pdf embedded_html
all clean test: JsonAPI/JsonAPI.md
all: pdf embedded_html
pdf: $(MARKDOWN_FILES:.md=.pdf)
embedded_html: $(MARKDOWN_FILES:.md=.html)
MAKEFLAGS += --no-print-directory
clean:
# $(RM) *.pdf *.html
JsonAPI/JsonAPI.md:
# $(MAKE) -C $(#D) TOPDIR=${CURDIR} $(MAKECMDGOALS)
%.html:%.md
# pandoc -s --toc -c /var/www/css/bootstrap.css $< -f markdown -t html -s -o $#
%.pdf:%.md
# pandoc --read=markdown --table-of-contents --toc-depth=3 --preserve-tabs --standalone --template=template.latex $(PANDOC_ENGINE)=pdflatex --listings -V geometry:margin=1in --highlight-style=pygments -H listing-setup.tex -r markdown+simple_tables+table_captions+yaml_metadata_block $< -o $#
If I call make on an unbuilt directory tree, it works fine. But there are a few problems I'm not sure how to address:
How can I ensure that if an updated .md deeply nested in the directory tree will cause the top level PDF file to be updated? Or, How can I force the makefile's in the sub-directories to be called?
The clean target at the top level is problematic, in that it doesn't recurse through the sub-directories. What do I need to do to remedy that?
Is there a better way to include the Makefile.boilerplate makefile, without having to define the TOPDIR on the $(MAKE) command line as I've done?
For 1, and 2, I'm guessing that an empty target dependency (FORCE:) will be required. And for 3, I've tried using $(CURDIR) but it was always evaluating to the directory the Makefile resided in, not the parent directory where the original make command was invoked.
Changing the md-sub-$(CONFIG_EEEE) macro definition to be just the directory was the key, and to make those targets have an empty rule.
Essentially, the per directory Makefile from above becomes:
include Kconfig
md-$(CONFIG_INTRODUCTION)+= Introduction.md
md-$(CONFIG_FW_UPDATE)+= FW-update.md
md-sub-$(CONFIG_CHAPTERS)+= Chapters/Chapters.md
md-$(CONFIG_CHAPTERS)+= Chapters
md-$(CONFIG_EXAMPLES)+= Examples.md
md-$(CONFIG_APPENDIX_I)+= Appendix-I.md
md-$(CONFIG_APPENDIX_II)+= Appendix-II.md
md-$(CONFIG_APPENDIX_III)+= Appendix-III.md
include ${TOPDIR}/scripts/Makefile.boilerplate
and the default Makefile.boilerplate changes the $(md-sub-y) target too:
$(md-sub-y): FORCE
# $(MAKE) -C $# TOPDIR=$(TOPDIR) $(MAKECMDGOALS)
FORCE:
And the top level makefile no longer needs $(#D) on the command line for the JsonAPI recipe, just $#.

Make cannot find rule to make target

I'm trying to take some source files, create some customised versions of those sources, then process those customised sources down to output files I can use. I'm using this make file. Note that this file is not fully complete, currently it only does the CSS* make, once that's working I will add the PHP* make which is similar:
# root sources
CSSSOURCES = $(wildcard *.scss)
PHPSOURCES = $(wildcard *.phtml)
# partials, creates a configed source
CSSMSOURCES = $(addprefix m.,$(CSSSOURCES:.scss=.m))
CSSDSOURCES = $(addprefix d.,$(CSSSOURCES:.scss=.d))
PHPMSOURCES = $(addprefix m.,$(PHPSOURCES:.phtml=.m))
PHPDSOURCES = $(addprefix d.,$(PHPSOURCES:.phtml=.d))
# targets
CSSMTARGETS = $(CSSMSOURCES:.m=.css)
CSSDTARGETS = $(CSSDSOURCES:.d=.css)
PHPMTARGETS = $(PHPMSOURCES:.m=.php)
PHPDTARGETS = $(PHPDSOURCES:.d=.php)
# ensure no clash with built in rules
.SUFFIXES: .m .d .scss .css .phtml .php
all: $(CSSMTARGETS)
%.m: %.scss
echo "%define MOBILE" | cat - $< >tmp
mv tmp $#
%.d: %.scss
echo "%define DESKTOP" | cat - $< >tmp
mv tmp $#
%.css: %.m %d
cat $< | mym1.pl >$#
rm $<
.PHONY: test
test:
#echo "sources - $(CSSSOURCES)"
#echo "msources - $(CSSMSOURCES)"
#echo "targets - $(CSSMTARGETS)"
Instead of creating the CSS targets I get this error:
make: *** No rule to make target 'm.page.css', needed by 'all'. Stop.
make operates on file names; if there is no file named m.page.m and no file named m.page.d - which is what m.page.css depends on in accordance with your declared dependencies - then Make will conclude that it needs to create these. If it does not have any rules (built-in or by way of a recipe) to create those, the error message you get tells you pretty much exactly that.
I'm guessing what you actually want is something like
m.%: %
echo "%define MOBILE" | cat - $< >$#
d.%: %
echo "%define DESKTOP" | cat - $< >$#
This tells make how to create m.whatever and d.whatever from whatever; so it now knows how to create m.x.phtml from x.phtml and d.y.scss from y.scss etc.
(Notice also how this avoids the separate mv of a static temporary file name, as discussed in comments.)
%.css: %.scss
mym1.pl <$< >$#
%.php: %.phtml
mym1.pl <$< >$#
This tells make how to create m.z.css from m.z.scss, d.w.php from d.w.phtml, etc. I'm guessing here that mym1.pl can handle both cases.
(Notice also the refactoring to avoid useless use of cat.)
This does away with the somewhat mysterious .m and .d suffixes so you probably have to refactor the variables at the top of your Makefile.
Instead of creating the CSS targets I get this error:
make: *** No rule to make target 'm.page.css', needed by 'all'. Stop.
Your makefile provides exactly one rule by which m.page.css could be built or updated:
%.css: %.m %d
cat $< | mym1.pl >$#
rm $<
In order for that rule to apply, however, both of the prerequisites, expressed as %.m and %d [sic] need to exist or be able to be built. There are problems with both.
The former in this case represents a file m.page.m. This apparently does not already exist, but you have a rule by which it could be built from m.page.scss. Except you don't have an m.page.scss or any rule to build one. The corresponding root source file is actually page.sccs.
The latter prerequisite, %d, would have an analogous problem, but it doesn't even get that far because you have omitted a period from the prerequisite name (should be %.d, not %d).
The rule quoted above is moreover bogus because it designates two prerequisites but only uses one. That's not inherently wrong, but it does not do anything useful to serve you here.
Replacing your current pattern rules with these should help:
m.%.m: %.scss
echo "%define MOBILE" | cat - $< >tmp
mv tmp $#
d.%.d: %.scss
echo "%define DESKTOP" | cat - $< >tmp
mv tmp $#
%.css: %.m
cat $< | mym1.pl >$#
rm $<
%.css: %.d
cat $< | mym1.pl >$#
rm $<

Makefile automatic variable changed by prerequisite

first time here. I am relatively new to makefiles. Here is my current makefile:
# Closure compiler php script path
closure = ../../cli/scripts/Compilers/closure.php
# Destination directory
dest = ../../static/js/
# Build directory
build = build/
# Tell "make" to search build and destination dirs
vpath %.o $(build)
vpath %.js $(dest)
all: main.js
#echo "Done.";
main.js: \
date.o \
jquery.autocomplete.o \
jquery.bullseye.o \
jquery.clickopen.o \
jquery.fbmodal.o \
jquery.helpers.o \
jquery.pulljson.o \
jquery.thumbrotate.o \
jquery.timefmt.o \
jquery.tools.o \
layout.main.o
cat $^ > $(dest)$#
%.o: %.js
php $(closure) $*.js $(build)$#
clean:
rm -rf $(build)*.o
rm -rf $(dest)*.js
The problem is with the following line:
cat $^ > $(dest)$#.
It is supposed to cat all the prerequisite objects (minified javascript) into one final js library. According to makefile docs, $^ is an automatic variable which contains a list of prerequisites with directories they are in. From my experience, it behaves differently depending on wether prerequisite needs to be compiled or not.
If prerequisite is up-to-date, this code works perfectly and $^ contains a list like:
build/date.o build/jquery.autocomplete.o build/jquery.bullseye.o....
However if prerequisite needs a fresh compile, then $^ gets directory part stripped and looks like:
date.o jquery.autocomplete.o jquery.bullseye.o
Only the file which needs a fresh compile gets directory part stripped.
I have managed to work around this issue by replacing
cat $^ > $(dest)$#
with
cat $(addprefix $(build), $(^F) ) > $(dest)$#.
I don't like it because:
It's a hack
$(^F) is semi-deprecated
I want to understand why make behaves like this.
thanks
Look here:
# Tell "make" to search build and destination dirs
vpath %.o $(build)
If Make is looking for foo.o, it will look in the local directory first. If it finds no foo.o there, it will look in $(build) (i.e. build/, and you might reconsider your variable names).
And how would Make build foo.o, if it couldn't find it anywhere? With this rule:
%.o: %.js
php $(closure) $*.js $(build)$#
This rule violates an important guideline of makefiles, in that the target (foo.o) is not the name of the thing actually built (build/foo.o).
Now consider what happens when Make tries to execute this rule:
main.js: date.o ...
cat $^ > $(dest)$#
So if date.o is up to date, it's in build/. Make finds it there, and the automatic variable $^ expands to build/date.o ...
But if date.o must be rebuilt, then Make looks to the %.o rule, which promises to build date.o (not build/date.o), so Make takes that rule at its word and $^ expands to date.o ...
There are several ways to solve this problem. I'd do something like this:
OBJS := date.o jquery.autocomplete.o jquery.bullseye.o ...
OBJS := $(addprefix $(build),$(OBJS))
$(dest)main.js: $(OBJS)
cat $^ > $#
# you might have to tinker with this rule a little
$(build)%.o: %.js
php $(closure) $< $#

How to target multiple directories with a single Makefile?

I'm using GNU Make to build three different editions of a static html document.
I use Less as a CSS preprocessor.
My directory structure looks like this:
Makefile
160x600/style.less
300x250/style.less
728x90/style.less
This is my Makefile:
LESSC=lessc -x # use -x for debugging
.PHONY: all clean
all: 160x600 300x250 728x90
%.css: %.less
$(LESSC) $< > $#
160x600: 160x600/style.css
300x250: 300x250/style.css
728x90: 728x90/style.css
clean:
rm -f 160x600/*.css
rm -f 300x250/*.css
rm -f 728x90/*.css
This way, I can use make 160x600 to build style.css from style.less.
But I don't want to explicitly list a target rule for each directory. Instead, I tried adding this rule instead of the three directory specific ones:
%: %/style.css
But that does not work. I assume it's clear from that example what my goal is. Is there a way to accept any directory as a target, so that I just have to list the directory names in the all: rule?
use static pattern rule:
res_dirs = 160x600 300x250 728x90
$(res_dirs): %: %/style.css

I need help trimming down dependencies in a makefile

I have created a makefile for the generation of a simple web page. The idea behind the makefile is this:
We're compiling one web page, index.html
index.html requires a stylus css main.sty that must be compiled
There are a number of examples used in the page
The code for example one lives in lib/examples/one
Each example contains three parts
The markup (a .jade template file)
Some code (a .coffee script file)
A description (a .md markdown file)
The build script must render each example into a single html file
Jade, Pygments, and Markdown are used to generate three html files
An example.jade template is used to combine these into one example file
example.jade must be copied to the correct build example directory, because the template language can only do relative imports. So in order to import example/one/code.html, we must copy the template to example/one and have it include code.html.
When finished, each example x will have compiled to tbuild/examples/x.html
The lib/index.jade template is moved to build (so that it can include the example files)
Jade is then used to compile the index.jade template into html
This is a wee bit of a simplification, but it's easier to understand this way. The simplification is that there are actually two markup files (left.html and right.html) in each example, and that the code file is both run through pygments and used as a script, so both code.html and code.coffee need to make it into build.
Right now, the makefile looks like this:
LIB = lib
BUILD = build
LIBEX = $(LIB)/examples
BUILDEX = $(BUILD)/examples
EXAMPLES = $(addsuffix .html,$(addprefix $(BUILDEX)/,$(shell ls $(LIBEX) | grep -v '.jade')))
all: $(BUILD)/main.css index.html
index.html: $(BUILD)/index.jade $(EXAMPLES)
jade < $< --path $< > $#
$(BUILD)/index.jade: $(LIB)/index.jade
mkdir -p $(#D)
cp $< $#
$(BUILD)/main.css: $(LIB)/main.sty
mkdir -p $(#D)
stylus -u nib < $< > $#
$(BUILDEX)/%.html: $(BUILDEX)/%/template.jade $(BUILDEX)/%/left.html $(BUILDEX)/%/right.html $(BUILDEX)/%/code.html $(BUILDEX)/%/code.coffee $(BUILDEX)/%/text.html
jade < $< --path $< > $#
$(BUILDEX)/%/template.jade: $(LIBEX)/template.jade
mkdir -p $(#D)
cp $< $#
$(BUILDEX)/%/left.html: $(LIBEX)/%/left.jade
jade < $< --path $< > $#
$(BUILDEX)/%/right.html: $(LIBEX)/%/right.jade
jade < $< --path $< > $#
$(BUILDEX)/%/code.html: $(LIBEX)/%/code.coffee
pygmentize -f html -o $# $<
$(BUILDEX)/%/code.coffee: $(LIBEX)/%/code.coffee
mkdir -p $(#D)
cp $< $#
$(BUILDEX)/%/text.html: $(LIBEX)/%/text.md
markdown < $< > $#
clean:
rm index.html -f
rm $(BUILD) -rf
This works, but the problem is that when I touch "lib/examples/intro/code.coffee" and re-run make, I get the following:
mkdir -p build/examples/intro
cp lib/examples/template.jade build/examples/intro/template.jade
jade < lib/examples/intro/left.jade --path lib/examples/intro/left.jade > build/examples/intro/left.html
jade < lib/examples/intro/right.jade --path lib/examples/intro/right.jade > build/examples/intro/right.html
pygmentize -f html -o build/examples/intro/code.html lib/examples/intro/code.coffee
mkdir -p build/examples/intro
cp lib/examples/intro/code.coffee build/examples/intro/code.coffee
markdown < lib/examples/intro/text.md > build/examples/intro/text.html
jade < build/examples/intro/template.jade --path build/examples/intro/template.jade > build/examples/intro.html
jade < build/index.jade --path build/index.jade > index.html
rm build/examples/intro/right.html build/examples/intro/code.coffee build/examples/intro/code.html build/examples/intro/left.html build/examples/intro/text.html build/examples/intro/template.jade
Which, as you'll notice, is way more than needs to be done to regenerate the example. What I was hoping for was something more like this:
mkdir -p build/examples/intro
pygmentize -f html -o build/examples/intro/code.html lib/examples/intro/code.coffee
cp lib/examples/intro/code.coffee build/examples/intro/code.coffee
jade < build/examples/intro/template.jade --path build/examples/intro/template.jade > build/examples/intro.html
jade < build/index.jade --path build/index.jade > index.html
In other words, what I'm asking is:
What do I need to do to make the makefile not rebuild too much when I change something small?
In the above example, left.html, right.html, and text.html are all rebuilt when I touch code.coffee. How can I prevent this?
Why does make put the rm command at the end of the output? This seems like it might be causing some re-building where unnecessary.
Thanks for reading all the way through this beast of a question! I know that my make-fu is lacking, so any tips as to how to clean up the makefile and reduce redundancy are more than welcome!
This build system is too large and complex to reproduce easily -- and I hate to post solutions I haven't tested -- but try adding this line:
.SECONDARY:
EDIT:
I haven't been able to reproduce the behavior you describe, but I can offer some pointers.
The .SECONDARY: is a rule; it can go anywhere in the makefile. Basically, if Make detects a chain of chain of implicit rules, A->B->C, where A is a file that exists and C is the target, it considers B an intermediate file and will delete it once the job is done. The .SECONDARY: rule blocks the deletion.
You can combine rules that have the same commands. This:
foo: bar
do something $< $#
baz: quartz
do something $< $#
quince: geef
do something $< $#
can be rewritten as this:
foo: bar
baz: quartz
quince: geef
foo baz quince:
do something $< $#
That will remove a lot of redundancy in your makefile, and perhaps make things clearer.
Like #Beta mentioned, you are running into troubles with intermediate files here.
Each file that is not mentioned explicitely in the Makefile, but rather inferred via a pattern rule (a rule with %), is intermediate and is deleted right after make ran. And then remade on the next run.
That's the core of your "problem": The left.html and right.html files are deleted right away. Beta already mentioned that declaring them as .SECONDARY fixes this problem, like the make manual tells you.
By declaring a target .SECONDARY without any prerequisites anywhere in the Makefile (really: anywhere), you declare all targets as secondary - so, no auto-deletes.
You could also use PHONY for targets that are not files to improve the performance. Like all and clean. See here for more information: What is the purpose of .PHONY in a makefile?

Resources