How do I create a static implicit rule in a Makefile? - c++

I originally had two implicit rules(simplified for clarity):
%$(EXESUFFIX) : %.c
$(CC) -o $* $<
%$(EXESUFFIX) : %.cpp
$(CXX) -o $* $<
But the problem is that on OS X and Linux $(EXESUFFIX) is blank which leads the rule to match the wrong things. So I am trying to use the static pattern rule as follows:
$(EXECS) : %$(EXESUFFIX) : %.c
$(CC) -o $* $<
$(EXECS) : %$(EXESUFFIX) : %.cpp
$(CXX) -o $* $<
Where $(EXECS) is the target and therefore devoid of the extension. But now, the top rule is being run for sources which end in .cpp. How do I fix this?
For a complete example:
Makefile:
EXESUFFIX =
EXECS = test
$(EXECS) : %$(EXESUFFIX) : %.c
$(CC) -o $* $<
$(EXECS) : %$(EXESUFFIX) : %.cpp
$(CXX) -o $* $<
test.cpp:
#include <stdio.h>
int main(int argc, char *argv[]){
printf("Hello World\n");
return 0;
}
This prints out the errors:
Makefile:8: warning: overriding commands for target `test'
Makefile:5: warning: ignoring old commands for target `test'
make: *** No rule to make target `test.c', needed by `test'. Stop.

The GNU Make manual you linked to is quite clear about the difference between static rules and implicit rules.
4.12.2 Static Pattern Rules versus Implicit Rules
A static pattern rule has much in common with an implicit rule defined as a pattern rule > (see Defining and Redefining Pattern Rules). Both have a pattern for the target and patterns for constructing the names of prerequisites. The difference is in how make decides when the rule applies.
An implicit rule can apply to any target that matches its pattern, but it does apply only when the target has no recipe otherwise specified, and only when the prerequisites can be found. If more than one implicit rule appears applicable, only one applies; the choice depends on the order of rules.
By contrast, a static pattern rule applies to the precise list of targets that you specify in the rule. It cannot apply to any other target and it invariably does apply to each of the targets specified. If two conflicting rules apply, and both have recipes, that's an error.
I suggest splitting your executables between C and C++ programs and defining independent rules for each.

I cannot reproduce your error, but this works in GNUMake 3.81:
%$(EXESUFFIX) : %.c
$(CC) -o $* $<
%$(EXESUFFIX) : %.cpp
$(CXX) -o $* $<
Having two different rules that fit the same target is legal with ordinary pattern rules, but not with static pattern rules.

Related

what does % mean in a makefile rule

So i know that the pattern rule %.o : %.cpp is for making any file.o from a file.cpp
But what I want is different. Let's say I want to make a file1.o from folder1/file1.cpp, and a file2.o from folder2/file2.cpp.
What i thought about doing was :
file1=folder1/file1
file2=folder2/file2
%.o: $(%).cpp
But that doesn't work. Do anyone have an idea on how to do that apart from manually doing :
file1=folder1/file1
file2=folder2/file2
file1.o: $(file1).cpp
file2.o: $(file2).cpp
The meaning of % in GNU Make is documented in the seciton on Defining and Redefining Pattern Rules.
If the target name is foo.o, then % is foo. I don't want to go into any more depth on the meaning of % here.
In your case, the dependencies between the object files and the source files cannot be expressed easily using a single simple rule. I would advise making the rules explicit instead of using a pattern-based rule.
file1.o : folder1/file1.cpp
file2.o : folder2/file2.cpp
You'll need to add the commands to build those targets too.
file1.o : folder1/file1.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $# $^
file2.o : folder2/file2.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $# $^

Makefile variables not working as expected

I am trying to compile a program using this makefile:
GCCPARAMS = -m32 -fno-use-cxa-atexit -nostdlib -fno-builtin -fno-rtti -fno-exceptions -fno-leading-underscore
ASPARAMS = --32
objects = src/loader.o src/kernel.o
%.o: src/%.cpp
gcc $(GCCPARAMS) -c -o $# $<
%.o: src/%.s
echo $(ASPARAMS)
as $(ASPARAMS) -o $# $<
kernel.bin: linker.ld $(objects)
ld $(LDPARAMS) -T $< -o $# $(objects)
install: kernel.bin
sudo cp $< boot/mykernel.bin
The problem comes when doing make which prints the following:
as -o src/loader.o src/loader.s
src/loader.s: Assembler messages:
src/loader.s: Warning: end of file not at end of a line; newline inserted
src/loader.s:18: Error: operand type mismatch for `push'
src/loader.s:19: Error: operand type mismatch for `push'
make: *** [<builtin>: src/loader.o] Error 1
As you can se the variable is not properly set, instead, make puts a space where the variable should be. That causes the assembler compiler to think it's a 64 bit architecture and giving some errors.
Any idea of what is going on? I am doing this using WSL2 by the way.
The reason you see this is because your recipe rules are wrong.
You have these object files:
objects = src/loader.o src/kernel.o
and you have this rule:
%.o: src/%.s
echo $(ASPARAMS)
as $(ASPARAMS) -o $# $<
When trying to build src/loader.o what does the stem of the pattern %.o match? It will match src/loader of course. So then what will the prerequisite be? It will be src/%.s with the stem expanded, so it will be src/src/loader.s. I presume that this doesn't exist.
As a result of this, this pattern rule doesn't match. So what does make do? It looks for a different pattern rule, and it turns out that GNU make provides a default pattern rule to build a .s file, which uses a pattern %.o : %.s and that pattern DOES match, so that's used.
But that built-in rule uses the standard make variables, which are AS and ASFLAGS. You haven't set ASFLAGS, so no flags are used.
You can tell that it's not using your rule because your rule has an echo ... in it and make does not print that command out. Therefore, it's not running your rule.

how to fix error 'no rule to make target %.o, needed by all. stop'

When I type make and hit enter, it says:
no rule to make target %.o, needed by all. stop'.
This is my makefile:
CC=g++
LDFLAGS=""
all: %.o %.h
$(CC) $< $(LDFLAGS) -o bin
%.o: %.cpp %.h
$(CC) -c $<
These are the contents of my directory:
$ ls
main.cpp makefile
I'm new to GNU make and just learned to use automatic variables. Can someone help me out?
% is for substituting patterns, which is what you want when defining a rule for a single module (like you did), but not what you want as the dependency list of 'all'. A % will expand to a single value.
Instead, you want an actual wildcard (like in the shell expansion) to match all *.o files. So, change your target definition to
all: *.o *.h
Not related to your question, but this is not right:
LDFLAGS=""
make doesn't parse quotes, so you've set the variable LDFLAGS to the literal string "" (two double-quotes). When you use that in your recipe:
$(CC) $< $(LDFLAGS) -o bin
it will be as if you'd typed:
g++ ... "" -o bin
which is a syntax error (you're passing an empty argument to g++. If you want an empty variable you just write:
LDFLAGS =
Now, your problem. This is not a pattern rule:
all: %.o %.h
A pattern rule must have a pattern in the target (left-hand side of the colon). Since this is an explicit rule, the prerequisites (the right-hand side of the colon) are the literal strings %.o and %.h. Make doesn't know how to build a file named %.o so it fails.
It's not really clear what you expect this rule to do, so I can't say more than that.

Makefile match patterns in rule and recipe

I have a pattern emerging for my tests in my makefile. I'm aware of the pattern matchers % and $<. I'm trying to compile some C++.
I have a snippet of a makefile that looks like:
diff-test: main-test.o diff.o test/diff-test.cpp
$(CC) $(FLAGS) $(BOOST_LINK_FLAGS) build/main-test.o build/diff.o test/diff-test.cpp -o build/diff-test
build/diff-test
walk-test: main-test.o walk.o test/walk-test.cpp
$(CC) $(FLAGS) $(BOOST_LINK_FLAGS) build/main-test.o build/%.o test/%-test.cpp -o build/%-test
build/walk-test
I tried something along the lines of:
%-test: main-test.o %.o test/%-test.cpp
$(CC) $(FLAGS) $(BOOST_LINK_FLAGS) build/main-test.o build/$<.o test/$<-test.cpp -o build/$<-test
build/$<-test
But that just couldn't match the tests I wanted:
make diff-test
make: *** No rule to make target `diff-test'. Stop.
I don't think you want $<. That expands to the first prerequisite which in this case is main-test.o which means you'll be building build/main-test.o-test using main-test.o.o and test/main-test.o-test.cpp which seems highly unlikely.
You probably mean $* instead.
However you can't use automatic variables in prerequisite lists: they are only defined inside the recipe itself.
Why not use the pattern in the prerequisite list; that's how pattern rules are supposed to work:
%-test: %.o test/%-test.cpp main-test.o
$(CC) $(FLAGS) $(BOOST_LINK_FLAGS) $^ -o build/$*-test
build/$*-test

Simple Makefile using WiringPi

I'm new to Makefiles. I've been making a simple C++ library using WiringPi for my RaspberyPi related projects. So far its very simple with things like an LED class, DuelLED, Motor, Button, etc (both .h and .cpp). I was able to piece together a Makefile but I am getting an error "No rule to make target RumPi.o needed by RumPi". (Note: The name of my library is called RumPi)
Thanks for all the help, Here is the final fix:
CXX = g++
CXXFLAGS = -std=c++11
LDLIBS = -lwiringPi
HEADERS = Components/Headers/RumPi.h ./Common.h Components/Headers/Component.h Components/Headers/Vehicle.h Components/Headers/Motor.h Components/Headers/Receiver.h Components/Headers/DualLED.h Components/Headers/LED.h Components/Headers/RGBLED.h Components/Headers/Relay.h
OBJECTS = RumPi.o Vehicle.o Motor.o Receiver.o DualLED.o LED.o RGBLED.o Relay.o
default: RumPi
RumPi: $(OBJECTS)
$(CXX) $(CXXFLAGS) $^ $(LDLIBS) -o $#
%.o: Components/Implementations/%.cpp
$(CXX) $(CXXFLAGS) -c -o $# $<
$(OBJECTS): $(HEADERS)
clean:
-rm -f $(OBJECTS)
-rm -f RumPi
That message means make can't figure out how to build that target (RumPi.o). Do you have a Components/Implementations/RumPi.cpp file? Do all the header files you've listed in the $(HEADERS) variable actually exist? If either of those is not true, then your pattern rule will not match. If your pattern rule doesn't match (and the default rule doesn't match) then make can't figure out how to build the object file.
If you want full details you can run make -d and examine the debug output. You might want to redirect it to a file so you can look at it with less or a text editor since there's a lot of output.
By the way, you should not add the -lwiringPi option to your compile line. Libraries are only used at link time, not compile time.
ETA
Something else you should consider is rewriting your pattern rule so that only the source file is listed as a prerequisite and moving the headers to a separate line:
%.o: Components/Implementations/%.cpp
gcc -c $< -o $# -lwiringPi
$(OBJECTS): $(HEADERS)
The advantage to this is you'll get a clearer error message if one of the headers is not found. When you use a pattern rule, make will try to match all the patterns it can and if none match it tells you it doesn't know how to build the target. Since there are usually many different ways to build a target, make doesn't show an error for every one that doesn't match.
But if you declare a direct explicit prerequisite relationship between two files, rather than using a pattern that may or may not match, then if there is a missing file make will tell you directly.
ETA2
In your original makefile you had a rule like this:
RumPi: $(OBJECTS)
gcc $(OBJECTS) -o $# -lwiringPi
but then later you removed it. Why did you take this out? You need this rule to tell make how to link your executable file RumPi.
Also, you shouldn't be using gcc as the compiler/linker for C++ code; gcc is for C, not C++. You should use g++ for C++ code. And it's best to contain this information in variables so it's easy to find and modify (even from the command line if you like). The standard variable names for the C++ compiler is CXX etc.
Your makefile should look something like this:
CXX = g++
CXXFLAGS = -std=c++11
LDLIBS = -lwiringPi
HEADERS = Components/Headers/RumPi.h ./Common.h Components/Headers/Component.h \
Components/Headers/Vehicle.h Components/Headers/Motor.h Components/Headers/Receiver.h \
Components/Headers/DualLED.h Components/Headers/LED.h Components/Headers/RGBLED.h \
Components/Headers/Relay.h
OBJECTS = RumPi.o Vehicle.o Motor.o Receiver.o DualLED.o LED.o RGBLED.o Relay.o
default: RumPi
RumPi: $(OBJECTS)
$(CXX) $(CXXFLAGS) $^ $(LDLIBS) -o $#
%.o: Components/Implementations/%.cpp
$(CXX) $(CXXFLAGS) -c -o $# $<
$(OBJECTS): $(HEADERS)
clean:
-rm -f $(OBJECTS)
-rm -f RumPi