GNU make: several targets in one pattern rule - makefile

With explicit targets I can combine several rules like
foo.o bar.o: $(SOURCES)
cc $< -o $#
This is equivalent of
foo.o: $(SOURCES)
cc $< -o $#
bar.o: $(SOURCES)
cc $< -o $#
But I want to use pattern rules.
I have several troff documents (man, README) and I want to generate .html and .ascii files.
Naive approach is
GROFF := groff
DOCS := man README
DOC_FILES = $(foreach doc,$(DOCS),$(doc).html $(doc).ascii)
CALL_GROFF = $(GROFF) -T$(subst $*.,,$#) -mman $< > $#
%.html %.ascii: %.doc
$(CALL_GROFF)
.DEFAULT: all
all: $(DOC_FILES)
.PHONY: clean
clean:
rm $(DOC_FILES)
But it doesn't work, because make believes that all files are created with one command (much like & in modern make: https://www.gnu.org/software/make/manual/html_node/Multiple-Targets.html)
Obviously I can do
GROFF := groff
DOCS := man README
DOC_FILES = $(foreach doc,$(DOCS),$(doc).html $(doc).ascii)
CALL_GROFF = $(GROFF) -T$(subst $*.,,$#) -mman $< > $#
%.ascii: %.doc
$(CALL_GROFF)
%.html: %.doc
$(CALL_GROFF)
.DEFAULT: all
all: $(DOC_FILES)
.PHONY: clean
clean:
rm $(DOC_FILES)
But it is a kind of copy-paste.
Could it be solved with GNU make?

This is exactly how this works; it's a long-standing feature. From the documentation:
Pattern rules may have more than one target; however, every target must contain a % character. Pattern rules are always treated as grouped targets (see Multiple Targets in a Rule) regardless of whether they use the : or &: separator.
As example states, it was meant to deal with programs that generate more than one output in one invocation, like bison. You can either update your recipe to generate both files in one shot, or keep the rules separated as you do now.

Related

Makefile - multiple recipes calling one, with variables - only the first gets run

I have set up my makefile like below, to minimize code duplication
The recipes are a set of blocks that set a variable, and then run the sleeper_agent recipe. They work great when called individually as make xlsx_sleeper for example.
But when I call all_sleepers, only the first one (xlsx_sleeper) gets compiled.
I have tried declaring them as phony (.PHONY: all_sleepers xlsx_sleeper docx_sleeper pptx_sleeper pdf_sleeper png_sleeper), which changes nothing
and adding a .FORCE rule to the sleeper_agent rule, which results in no such file or directory:
.FORCE:
sleeper_agent: .FORCE [...]
Here is my makefile:
all_sleepers: xlsx_sleeper docx_sleeper pptx_sleeper png_sleeper pdf_sleeper
sleeper_agent: $(OBJ)/sleeper_agent.o $(OBJ)/identities.o
windres icons/$(ext)/resource.rc -O coff -o obj/$(ext).res
$(CC) -o $(BIN)/sleeper_$(ext).exe $^ $(OBJ)/$(ext).res $(CFLAGS) $(LIBS)
xlsx_sleeper: ext=xlsx
xlsx_sleeper: sleeper_agent
docx_sleeper: ext=docx
docx_sleeper: sleeper_agent
pptx_sleeper: ext=pptx
pptx_sleeper: sleeper_agent
png_sleeper: ext=png
png_sleeper: sleeper_agent
pdf_sleeper: ext=pdf
pdf_sleeper: sleeper_agent
Your problem is that make does not see any reason why it should rebuild the sleeper_agent target several times. You should probably stick to the make philosophy:
Try to have real files as targets ($(BIN)/sleeper_xlsx.exe).
Use phony (non-file) targets only:
To give symbolic names to other targets or groups of targets (all_sleepers, xlsx_sleeper, ...)
For rules that don't produce files (clean, help...)
Declare phony targets as such (.PHONY: ...)
Example using static pattern rules, automatic variables and the patsubst make function:
SLEEPER := xlsx docx pptx png pdf
EXE := $(patsubst %,$(BIN)/sleeper_%.exe,$(SLEEPER))
SHORT := $(patsubst %,%_sleeper,$(SLEEPER))
.PHONY: all_sleepers $(SHORT) clean_sleepers
all_sleepers: $(EXE)
$(SHORT): %_sleeper: $(BIN)/sleeper_%.exe
$(EXE): $(BIN)/sleeper_%.exe: $(OBJ)/sleeper_agent.o $(OBJ)/identities.o
windres icons/$*/resource.rc -O coff -o obj/$*.res
$(CC) -o $# $^ $(OBJ)/$*.res $(CFLAGS) $(LIBS)
clean_sleepers:
rm -f $(EXE)
And then you should be able to run:
make all_sleepers
to build them all or:
make xlsx_sleeper
to build only one of them. EXE is the list of real executable files and a static pattern rule explains how to build them. In its recipe the $* automatic variable expands as the string matching the % wildcard. SHORT is the list of xxxx_sleeper shortcuts and another static pattern rule explains for each of them to which real executable it corresponds. all_sleepers and the xxxx_sleeper shortcuts (plus the clean_sleepers I added as example) are properly declared as phony because there are no such real files.

GNU Make ignoring a phony rule specified by wildcard?

I am learning some courses about compiling some C code into specific assembly. I decided that the generated assembly should be manually inspected, so I came up with less something.s as a "test" rule.
As a fan-but-newbie of Make, I wrote this Makefile:
CODES := a
LESS ?= less
CODES_TEST := $(patsubst %,%-test,${CODES})
.PHONY: all test ${CODES_TEST} clean
all: $(patsubst %,%.s,${CODES})
test: all
%-test: %.s
${LESS} $^
%.s: %.c
${CC} ${CFLAGS} -S -o $# $^
clean:
rm -f *.o *.s
And I have this minimal a.c file:
int asdfg(void) { return 54321; }
I then typed make a-test in Bash, expecting less showing up with the content of a.s, only to be told this:
make: Nothing to be done for 'a-test'.
I got the above response regardless of the presence of a.s, which generates normally if I do make a.s or just make (implicitly runs the first rule, all).
I checked my Makefile and I don't think I made a typo or another simple mistake.
What did I miss with the above Makefile?
How can I get Make to execute less a.s when I run make a-test?
There is nothing to be done for a-test because the only rule that would make it is the implicit pattern rule:
%-test: %.s
${LESS} $^
and, per the manual 4.6 Phony Targets:
The implicit rule search (see Implicit Rules) is skipped for .PHONY targets.
and, since it is .PHONY, its mere non-existence does make it out-of-date.
To get around this, while preserving the phoiness, replace:
%-test: %.s
${LESS} $^
with:
${CODES_TEST}: %-test: %.s
${LESS} $^
Then the rule is a static pattern rule and no longer an implicit one.

Makefile Dynamic Rules w/ No GNU-make Pattern

I have a set of .cpp files that I want to compile. These .cpp files are in a hierarchical directory structure. I want the corresponding .o files to all end up in one build folder.
Here's how I get GNU make to enumerate the files:
SRCS = \
$(wildcard $(CODE)/**/*.cpp) \
$(wildcard $(CODE)/AlgebraLibraries/**/*.cpp) \
$(wildcard $(CODE)/Calculator/Environments/**/*.cpp)
BARE_SRCS = $(notdir $(SRCS))
BARE_OBJS = $(BARE_SRCS:.cpp=.o)
OBJS = $(addprefix $(BUILD)/, $(BARE_OBJS))
Having done this, I have no idea how to create the rules that will create the .o files from the .cpp files. Intuitively, what I want to do is the following pseudocode:
for i=0, N do # <-- a for-loop!
$(OBJS)[i]: $(SRCS)[i] # <-- the rule!
$(CPP) -c $(SRCS)[i] -o $(OBJS)[i] # <-- the recipe
end
Of course, this is not valid GNU make code, but I trust you understand what it is here that I'm trying to do. The following will not work.
%.o: %.cpp
$(CPP) -c $< -o $#
This doesn't work, because GNU make is matching up the % signs, assuming that the .o files live along-side the .cpp files.
The alternative to all of this, which I know will work, but will be extremely tedious, is to enumerate all of the rules by-hand as explicit rules. There has to be a better way!
I've been researching GNU make's ability to generate rules, but there appears to be no way to do it without the built-in logic. It would be really nice if I could utilize some flow-control statements to generate the rules that I want to make. Is this asking too much of GNU-make?
In any case, is there a way to do what it is I'm trying to do with GNU make? If so, how?
This looks like a job for... several advanced Make tricks:
all: $(OBJS)
define ruletemp
$(patsubst %.cpp, $(BUILD)/%.o, $(notdir $(1))): $(1)
$$(CPP) -c $$< -o $$#
endef
$(foreach src,$(SRCS),$(eval $(call ruletemp, $(src))))
If $(BUILD) is constant, you can always just do:
$(BUILD)/%.o: %.cpp
$(CPP) -c $< -o $#

Makefile target matching

I'm having troubles with my Makefile :-(
I have a mix of assembly and C sourcecode that I need to link together. I need different build-instructions for those two types. Since both the assembler and C compiler output *.o files, I cannot use the general %.o:%.c construction often found in example Makefiles
This what I'm trying now:
Get a list of all C files and their resulting output files:
C_SRCFILES := $(shell find $(SRCDIRS) -type -f -name "*.c")
C_OBJFILES := $(patsub %.c,%.o,$(C_SRCFILES))
Get a list of all asm files and their resulting output files:
A_SRCFILES := $(shell find $(SRCDIRS) -type -f -name "*.asm")
A_OBJFILES := $(patsub %.asm,%.o,$(A_SRCFILES))
When I echo those vars to the screen, they seem to be correct, but how I do define my targets now?
I tried something like this
$(A_OBJFILES): ($A_SRCFILES)
$(AS) $(AFLAGS) -o $# $*
$(C_OBJFILES): ($C_SRCFILES)
$(CC) $(CFLAGS) -c -o $# $*
all: $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $(A_OBJFILES) $(C_OBJFILES) -o $(TARGET_OUTPUT)
but ofcourse, this doesn't work...
Any suggestions?
First problem: a misplaced parenthesis or two.
$(A_OBJFILES): ($A_SRCFILES)
Notice that you have the $ inside the ( in ($A_SRCFILES). Make expands $A, which is nothing, and things go downhill. I think you meant $(A_SRCFILES), and the same thing in the other rule.
Second problem: I don't know the syntax of the assembler, but the syntax of the compiler command is wrong:
$(CC) $(CFLAGS) -c -o $# $*
The variable $* is nothing if we're not in a pattern rule, which we're not (yet). And anyway, if we were in a pattern rule and you were trying to build foo.o, this command would look for the source file foo, and there's no such file. Do it this way:
$(CC) $(CFLAGS) -c -o $# $<
Third problem: each object file depends on all source files (in each rule). Try this instead:
$(A_OBJFILES): %.o : %.asm
...
$(C_OBJFILES): %.o : %.c
...
(Now it's a pattern rule.)
Fourth problem: a lot of redundancy in the last rule. Change it to this:
all: $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $^ -o $(TARGET_OUTPUT)
or better still:
all: $(TARGET_OUTPUT)
$(TARGET_OUTPUT): $(A_OBJFILES) $(C_OBJFILES)
$(LD) $(LDFLAGS) $^ -o $#
Since both the assembler and C compiler output *.o files, I cannot use the general %.o:%.c construction often found in example Makefiles
Sure you can:
%.o : %.c
# commands to make .o from a corresponding .c
%.o : %.asm
# commands to make .o from a corresponding .asm

How can I manage make targets that don't have suffixes?

I've got a make file that generates multiple targets. Something like:
target-a: target-a.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
target-b: target-b.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
target-c: target-c.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
The actual build process (abbreviated as $(BUILD_TOOL) above) is a multiple line thing involving compilers, scripts and various whatnot, but suffice to say, the build process acts on the first target dependency ($<) and produces the output target ($#).
This is quite unwieldly. Would what I've got below be considered a safe way to replace the above (using a pattern rule that doesn't have a suffix)?
all: target-a target-b target-c
% : %.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
The make tool is GNU, and I'm content to use it's powerful extensions.
If target is a literal string, renierpost's solution is very good. If it isn't (or even if it is) this will work:
TARGETS := target-a target-b target-c
all: $(TARGETS)
$(TARGETS): % : %.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
Note that this rule will not build targets you did not intend, not even target-include.
It depends on the rest of your Makefile, but in principle this should work,
if all files are in one directory.
It's better practice to use extensions on your targets.
Is target a literal string? In that case, you can be more specific
(and speed up rule application a tiny little bit, but it's fast already) by using
all: target-a target-b target-c
target-% : target-%.src target-include.src
#$(BUILD_TOOL) -f $< -o $#
GNU make's advanced syntax will come into play if you want to automatically deduce the names of target-a target-b target-c from the target-*.src filenames on the filesystem or something similar.

Resources