Pattern rules work but suffix rules don't - makefile

Pattern rules work:
$ ls
Makefile hello.txt world.txt
$ cat Makefile
all: hello.out world.out
%.out: %.txt
cp $< $#
$ make
cp hello.txt hello.out
cp world.txt world.out
However, when I try to replace them with what I think is the exact equivalent suffix rules, they don't work:
$ ls
Makefile hello.txt world.txt
$ cat Makefile
.POSIX:
.SUFFIXES:
.SUFFIXES:.txt.out
all: hello.out world.out
.txt.out:
cp $< $#
$ make
make: *** No rule to make target 'hello.out', needed by 'all'. Stop.
I don't understand why.

This line is the problem:
.SUFFIXES: .txt.out
It declares a single suffix, .txt.out, not two of them. You can change it to this:
.SUFFIXES: .txt .out

Related

Gnu Make: how to use the pattern rule

I have this sample (siimplified) makefile
all: a a.e b b.e
.SUFFIXES:
a a.e:
touch $#
b: a
ln -sf $(notdir $<) $#
b.e: a.e
ln -sf $(notdir $<) $#
clean:
rm -f a* b*
and it works.
I would like to use Pattern Rules as follow:
all: a a.e b b.e
.SUFFIXES:
a a.e:
touch $#
b%: a%
ln -sf $(notdir $<) $#
clean:
rm -f a* b*
but it fails:
$ make
touch a
touch a.e
make: *** No rule to make target 'b', needed by 'all'. Stop.
I cannot figure out why, and I don't know how to make it works
Does this info from the manual give you your answer:
The target is a pattern for matching file names; the ‘%’ matches any nonempty substring, while other characters match only themselves.
(emphasis added)?

Generate a directory if a target name has a directory part

I have this makefile code:
$(DIRS):
#echo " MKDIR build/tmp/base/socket/$#"
$(Q)mkdir -p $#/
%.a.s:
#echo " CC build/tmp/base/socket/$#"
$(Q)$(CC) $(CFLAGS_A) -S $< -o $#
%.so.s:
#echo " CC build/tmp/base/socket/$#"
$(Q)$(CC) $(CFLAGS_SO) -S $< -o $#
%.o: %.s
#echo " AS build/tmp/base/socket/$#"
$(Q)$(AS) $< -o $#
tcp/client.a.s: $(TCP_CLIENT_SRC) $(TCP_CLIENT_INC)
tcp/client.so.s: $(TCP_CLIENT_SRC) $(TCP_CLIENT_INC)
tcp/server.a.s: $(TCP_SERVER_SRC) $(TCP_SERVER_INC)
tcp/server.so.s: $(TCP_SERVER_SRC) $(TCP_SERVER_INC)
I'd like to add a | tcp in the prerequisites of all targets that have a leading tcp/ in their name, but I'd like to be able to do it in one line. I wouldn't want to append that manually to every line that needs it.
I thought of adding this new rule:
tcp/%.s: | tcp
but it isn't doing anything.
I also thought of the more generic, which would be nicer, but same results:
%.s: | $(#D)
How should I write that?
A workaround would be to call mkdir every time (include it in both %.a.s and %.so.s rules), but that would add unnecessary calls to mkdir, wouldn't it?
I am not a fan of pattern rules. They are too arbitrary for my tastes.
(What actually happens depends on what files you may have lying around on your hard disk.)
You cannot just add a prerequisite to a given set of targets using a pattern rule
Well you can if you use static pattern rules. This is a much nicer idiom. Here we prefix a pattern rule with the actual list of sources you want the pattern rule to apply to. This is good where you can describe dependencies using make's noddy pattern matching.
A sketch:
%.a: ; date >$# # Pattern rule
tcp: ; mkdir -p $# # Explicit rule
tcp/a.a: tcp/%.a: | tcp ; # Static pattern rule!
.PHONY: all
all: tcp/a.a
all: c.a
all: dir/b.a
all: ; : $# Success
And we have:
$ make all
mkdir -p tcp
date >tcp/a.a
date >c.a
date >dir/b.a
/bin/sh: dir/b.a: No such file or directory
make: *** [Makefile:3: dir/b.a] Error 1
Here we have told make that before it builds (i.e., "runs the recipe for") tcp/a.a, it must first build tcp. That works. We didn't tell make about the directory for dir/b.a, so that failed. Note that the .a file's recipe is still in an ordinary pattern rule. This is just by way of exposition. I would definitely change this.
Yeah, in this case the pattern rule for tcp/ is over-the-top. Consider though that before you create tcp/a.a, you might first need to create a auto/a.src (say).
tcp/a.a: tcp/%.a: auto/%.src | tcp ; # Static pattern rule!
Easily extensible.
targets := tcp/a.a tcp/b.a tcp/c.a
${targets}: tcp/%.a: auto/%.src | tcp ; # Static pattern rule!
[By the way, in your original makefile, your archive and shared object should depend on the .o files, not the source (???)]
You cannot just add a prerequisite to a given set of targets using a pattern rule. That's not how pattern rules work: they are rules: they must have a recipe associated with them. A pattern rule without a recipe actually deletes that pattern rule (see Canceling Implicit Rules).
You can either create two sets of pattern rules, one for all targets and a second one for just targets that start with tcp/ that have an extra prerequisite, but you have to write the entire pattern rule twice, including a recipe, not just the pattern line.
Or just put the mkdir into the recipe. A mkdir on a directory that already exists won't even be noticeable.
This answer is only to show a working Makefile implementing #bobbogo 's answer.
This Makefile is a leaf of a Makefile tree, so all variables not defined here are exported by upper Makefiles.
Makefile:
#! /usr/bin/make -f
################################################################################
# *AUTHOR*
# FULL NAME "Alejandro Colomar Andrés"
################################################################################
################################################################################
DIRS = \
$(CURDIR)/tcp
OBJ = \
$(CURDIR)/tcp/client.o \
$(CURDIR)/tcp/server.o \
$(CURDIR)/foo.o
SRC = \
$(SRC_DIR)/base/socket/tcp/client.c \
$(SRC_DIR)/base/socket/tcp/server.c \
$(SRC_DIR)/base/socket/foo.c
DEP = $(OBJ:.o=.d)
BOTH_OBJ = $(subst .a.o,.a.o ,$(join $(OBJ:.o=.a.o),$(OBJ:.o=.so.o)))
BOTH_ASM = $(subst .a.s,.a.s ,$(join $(OBJ:.o=.a.s),$(OBJ:.o=.so.s)))
NEEDDIR = $(DEP) $(BOTH_ASM)
################################################################################
PHONY := all
all: $(BOTH_OBJ)
#:
$(DIRS): $(CURDIR)/%:
#echo " MKDIR build/tmp/base/socket/$*"
$(Q)mkdir -p $#
$(NEEDDIR): | $(DIRS)
$(CURDIR)/%.d: $(SRC_DIR)/base/socket/%.c
#echo " CC -M build/tmp/base/socket/$*.d"
$(Q)$(CC) $(CFLAGS_A) -MG -MT"$#" \
-MT"$(CURDIR)/$*.a.s" -MT"$(CURDIR)/$*.so.s" \
-M $< -MF $#
$(CURDIR)/%.a.s: $(SRC_DIR)/base/socket/%.c $(CURDIR)/%.d
#echo " CC build/tmp/base/socket/$*.a.s"
$(Q)$(CC) $(CFLAGS_A) -S $< -o $#
$(CURDIR)/%.so.s: $(SRC_DIR)/base/socket/%.c $(CURDIR)/%.d
#echo " CC build/tmp/base/socket/$*.so.s"
$(Q)$(CC) $(CFLAGS_SO) -S $< -o $#
$(CURDIR)/%.o: $(CURDIR)/%.s
#echo " AS build/tmp/base/socket/$*.o"
$(Q)$(AS) $< -o $#
include $(DEP)
PHONY += clean
clean:
$(Q)rm -rf *.o *.s *.d
################################################################################
# Declare the contents of the PHONY variable as phony.
.PHONY: $(PHONY)
################################################################################
######## End of file ###########################################################
################################################################################
output:
MKDIR build/tmp/base/socket/tcp
CC -M build/tmp/base/socket/foo.d
CC -M build/tmp/base/socket/tcp/server.d
CC -M build/tmp/base/socket/tcp/client.d
CC build/tmp/base/socket/tcp/client.a.s
AS build/tmp/base/socket/tcp/client.a.o
CC build/tmp/base/socket/tcp/client.so.s
AS build/tmp/base/socket/tcp/client.so.o
CC build/tmp/base/socket/tcp/server.a.s
AS build/tmp/base/socket/tcp/server.a.o
CC build/tmp/base/socket/tcp/server.so.s
AS build/tmp/base/socket/tcp/server.so.o
CC build/tmp/base/socket/foo.a.s
AS build/tmp/base/socket/foo.a.o
CC build/tmp/base/socket/foo.so.s
AS build/tmp/base/socket/foo.so.o

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 $<

Make: no remake on mac

I have some kind of make dependency problem. all_2 does not rebuild after a1.src has been touched, but all_1 does. Why? Can't I use absolute paths?
$ cat Makefile
DIR = ${HOME}/tmp
outputs = $(DIR)/dir1/a1.out $(DIR)/dir2/a2.out
all_1 : dir1/a1.out dir2/a2.out
all_2 : $(outputs)
ls -l $(outputs) # debug print
*/%.out : $(notdir %.src)
#touch $#
#echo 'Build $# from $(notdir $*.src)'
This is my directory structure:
$ ls -R
Makefile a1.src a2.src dir1 dir2
./dir1:
a1.out
./dir2:
a2.out
all_1 works fine:
$ touch a1.src
$ make all_1
Build dir1/a1.out from a1.src
$ make all_1
make: Nothing to be done for `all_1'.
but all_2 does not rebuild a1.out (although the out files exist, so I guess that the targets are ok):
$ touch a1.src
$ make all_2
ls -l /Users/eternity/tmp/dir1/a1.out /Users/eternity/tmp/dir2/a2.out # debug print
-rw-r--r-- 1 eternity staff 0 Jan 20 15:25 /Users/eternity/tmp/dir1/a1.out
-rw-r--r-- 1 eternity staff 0 Jan 20 14:46 /Users/eternity/tmp/dir2/a2.out
$
First, the * is not a "wildcard" in the target side. You need a pattern rule with %.out as target:
%.out: ...
That will match against target names with a trailing .out.
The stem (i.e.: the % part in the target) with which the pattern rule was matched against will be stored in the automatic variable $*:
%.out: $(notdir $*.src)
However, that alone won't do: you also need to enable secondary expansion to use the variable $* as part of the rule definition, because its value is empty during the first expansion. In order to enable secondary expansion, simply define the target .SECONDEXPANSION, i.e.:
.SECONDEXPANSION:
Once enabled, for delaying the expansion of a variable or function at the rule definition, you need to replace $ by $$:
%.out: $$(notdir $$*.src)
So, with all that in mind, your last rule should look like:
.SECONDEXPANSION:
%.out: $$(notdir $$*.src)
#touch $#
#echo 'Build $# from $(notdir $*.src)'

GNU Make Force Removal of Intermediate Files

I have a compiler that produces .c files from .ec files as an intermediate step. The compiler does not remove the .c file. The compiler cannot be asked to skip invocation of $CC to produce the .o file. I am trying to have GNU make (3.81) treat the .c files produced as intermediate files and clean them up. The following is an isolated example that reproduces the bad behavior with a file extension that has no implied rules.
.INTERMEDIATE: %.delme
%.o: %.ec
cp $< $(<:.ec=.delme)
cp $(<:.ec=.delme) $#
all: test.o
To execute the test case:
rm -f test.*
touch test.ec
make
if [[ -e test.delme ]]; then echo "Failure"; else echo "Success"; fi
Try using a pattern rule to tell make their your compiler produces both .o and .c files from the .ec source. And then declare all the c files as INTERMEDIATE. Pattern rules with multiple outputs work differently than non-pattern rules. Make will run pattern rules only once to produce all output files from the rule, while static rules will be run for each output file. Make also understand that a pattern rule will produce all output files, even if it only wanted one of them as a target.
The result is something like this:
SRC := foo.ec bar.ec
OBJS := $(SRC:.ec=.o)
all: program
program: $(OBJS)
cat $^ > $#
%.o %.c: %.ec
cp $< $(<:.ec=.c) ; cp $< $(<:.ec=.o)
.INTERMEDIATE: $(SRC:.ec=.c)
The command to make the .c and .o from the .ec will be run once to produce both those files. Since make knows it made the .c (even though it only wanted the .o), it will know enough to delete it. The .INTERMEDIATE target will only work if the files are listed explicitly, not using a pattern, so we haven't used %.c. Which seems like a bad idea anyway, what if you had C source that wasn't produce from an .ec file and make deleted it for you? Example output:
$ make
cp foo.ec foo.c ; cp foo.ec foo.o
cp bar.ec bar.c ; cp bar.ec bar.o
cat foo.o bar.o > program
rm bar.c foo.c
$ touch foo.ec ; make
cp foo.ec foo.c ; cp foo.ec foo.o
cat foo.o bar.o > program
rm foo.c
Notice how in the second invocation it only deleted foo.c since bar.o/c wasn't rebuilt.
Make can only consider make targets to be intermediate. You can't just declare a random file on the filesystem as intermediate and have make delete it for you.
Here the .delme file is created as a side effect of the recipe that builds the .o file; make doesn't know anything about it, so make will not delete it because there are no targets in the makefile that are intermediate.
In your example you could split the two cp commands into separate rules and that would allow the intermediate setting to work:
%.delme : %.ec
cp $< $#
%.o : %.delme
cp $< $#
I'm assuming that in your real environment you can't do that because it's all one command that generates the intermediate file and the real file. In that case you'll have to deal with the delete yourself inside the recipe:
%.o : %.ec
cp $< $(<:.ec=.delme)
cp $(<:.ec=.delme) $# && rm -f $(<:.ec=.delme)
Note this leaves the .delme file existing if the cp command fails; if you want to remove it no matter what you can do that too.
EDIT
To delete the intermediate file even if the command fails you have to preserve the exit code so you can tell make what it was. Something like:
%.o : %.ec
cp $< $(<:.ec=.delme)
cp $(<:.ec=.delme) $#; e=$$?; rm -f $(<:.ec=.delme); exit $$e

Resources