Reputation: 566
Here is a simple Makefile
content:
CXXCOMP=g++
CXXFLAGS=-std=c++11 -g
%.o: %.cpp
$(CXXCOMP) $(CXXFLAGS) -c $<
main: %.o
$(CXXCOMP) $(CXXFLAGS) -o $@ $^
I expect that the first rule will generate a .o
file for each .cpp
one, while the second rule will generate a main
file from all the .o
ones.
Actually, the following statement is printed to terminal when I try to make main
:
target `main' doesn't match the target pattern
Can you please explain a bit more in depth what's going on and how to solve this little issue?
Upvotes: 1
Views: 1641
Reputation: 1159
As John Bollinger pointed out in the comments, you should probably name either the sources or the object files explicitly. Not doing so is less safe, less predictable, and the price you pay in makefile complexity isn't even worth it.
To circumvent the complexity, you would have to have all of your header files, source files, object files, and output files in the same folder, which isn't great. This would, however, allow you to simply use the $(wildcard *.cpp)
function. Otherwise you would have to chain a series of calls to first get all of the source files in the src
directory, use the notdir
function to strip the directory part of the name, and then use the patsubst
function to do a switcheroo on the filename extension. That would look something like this:
OBJS = $(patsubst %.cpp, %.o, $(notdir $(wildcard src/*.cpp)))
I personally prefer listing all the object files explicitly, but this will do what you want.
As an aside, the reason you need the wildcard
function specifically is because while wildcards will generally work as expected in rules and targets, you're using it in a variable assignment, where it is being taken literally. If you set OBJS
to *.o
and echo the variable, you get this:
OBJS = *.o
echo_objs:
@echo $(OBS)
Output:
echo *.o
Running the whole makefile gives the following error:
make: *** No rule to make target '*.o', needed by 'project'. Stop.
There are no object files in the directory currently, so using the wildcard function in the assignment results in echoing out nothing, which is the usual behavior. I echoed the names of all the object files in the directory: none.
What I like to do is list the object files like this:
OBJS = main.o file.o string.o # or whatever
Then, having previously set the vpath
variable like this:
vpath %.cpp src
vpath %.hpp include
I can simply write:
OBJS = main.o
ASMS = $(patsubst %.o, %.asm, $(OBJS))
TARGET = project_name
all: assembler_output $(TARGET) # etc
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -I include -o $@ $^ $(LDFLAGS)
%.o: %.cpp
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -I include -c -o $@ $^
assembler_output: $(ASMS)
%.asm: %.cpp
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -I include -masm=intel -S -o $@ $^
.PHONY: clean
clean:
$(RM) $(OBJS) $(TARGET)
I included the additional target, because a) I like seeing what the compiler (and occasionally the preprocessor) are doing before the linker is invoked, and b) hopefully it helps modularize your goals somewhat.
Having hopefully answered the main question, I do have some suggestions on your makefile. You should really stick to the convention of naming the C compiler variable CC
and the C++ compiler CXX
, especially if you want to publish your code for others to use. Your users will expect to be able to configure the compilation settings for the project when they build it (maybe they don't have or use g++), and they'll be in for a surprise when they keep changing the CXX
variable to no avail.
While a makefile for a toy project is short, they can get pretty unwieldy pretty fast, especially with test code, installation directives, etc., so making your users have to cat -n ./makefile | grep -E 'CXX'
for all possible compilation directives to hunt down the actual variable will be pretty annoying. Not to mention you're declaring the variable in the makefile, so they can't override the settings. You might want to use ?=
for those variables, that way they're only defined if no settings have been set through the console.
In fact, if you run make --print-data-base
in the project directory and grep -E 'COMPILE'
, you can see the exact commands make
has built-in to compile C, C++, assembler, fortran, etc. For C++ the command is:
COMPILE.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
Since your users can configure those flags though, make sure your mandatory include
directories aren't in CPPFLAGS
. That variable is for optional libraries' include directories, in case your application allows users to compile with additional functionality if they have a specific library installed.
The linker command is LINK.cc = $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)
, where LDFLAGS
is the linker variable your users will also expect to be able to configure.
All of this really starts to matter once your projects start gaining size and complexity. Large projects will usually have nested subfolders and a top-level makefile will call each nested makefile recursively, and this is when sticking to a clear, well-defined convention starts to really matter.
Writing good makefiles takes practice, but it can be super rewarding. I've seen some truly masterful ones, none of them written by me, unfortunately. Hopefully this helps somewhat, though; for me it always helps to know why things are done they way they are, from a technical standpoint, before I really understand and embrace accepted practices.
Upvotes: 2
Reputation: 72707
The %.o
is not a shell wildcard, it works only in the context of pattern rules. To do what you want you have to use the wildcard function, e.g.
main: $(wildcard *.o)
$(CXXCOMP) $(CXXFLAGS) -o $@ $^
but as Matt correctly points out, has a set of different problems. A better way is to use a make variable for the source files, and then construct the list of object files.
SRC = $(wildcard *.cpp)
OBJ = $(SRC:.cpp=.o)
main: $(OBJ)
...
Upvotes: 2