MrDuk
MrDuk

Reputation: 18242

How to construct a makefile that support multiple targets with main()

I have a makefile that's been working pretty great for the past couple weeks. However, now that I've added another target to my project with "main", it starts to flip out a bit.

CC=g++
CCOPTS=-g -w

OBJS = $(BINDIR)/manager.o $(BINDIR)/rtngnode.o
TARGETS = $(BINDIR)/manager $(BINDIR)/rtngnode
BINDIR = build

all: $(TARGETS) $(OBJS)

clean:
    rm -f $(TARGETS) $(OBJS)

.PHONY: all clean

$(TARGETS): $(OBJS)
    $(CC) -o $@ $^ $(CFLAGS) $(LIBS)

$(BINDIR)/%.o: %.cpp
    $(CC) -c $(CCOPTS) -o $@ $<

I don't quite understand makefiles still... so I'm not sure what's going on. I get these two errors:

build/rtngnode.o: In function `__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<int const, Node> > >::new_allocator()':
/home/caleb/Documents/dev/tote2/mp2/rtngnode.cpp:5: multiple definition of `main'
build/manager.o:/home/caleb/Documents/dev/tote2/mp2/manager.cpp:161: first defined here
build/manager.o: In function `main':

manager.cpp:(.text+0xefb): undefined reference to `NetworkConnection::NetworkConnection(char const*, char const*)'

In my rtngnode target, I have dependancies on the tcpcon class... and my manager target also has a main() reference. I'm pretty confused... so not even sure if I'm asking the right question.

I guess my question is:

Upvotes: 0

Views: 360

Answers (4)

MadScientist
MadScientist

Reputation: 100781

The other answers are all accurate, but I think the key thing to understand is this rule:

$(TARGETS): $(OBJS)
        $(CC) -o $@ $^ $(CFLAGS) $(LIBS)

doesn't do what you seem to think that it does. This expands to:

$(BINDIR)/manager $(BINDIR)/rtngnode : $(BINDIR)/manager.o $(BINDIR)/rtngnode.o
        $(CC) -o $@ $^ $(CFLAGS) $(LIBS)

Make doesn't try to infer some magical matching up of targets to prerequisites here; instead make repeats the entire rule again for each target, but with all the prerequisites. The rule above is identical to writing this:

$(BINDIR)/manager : $(OBJS)
        $(CC) -o $@ $^ $(CFLAGS) $(LIBS)

$(BINDIR)/rtngnode : $(OBJS)
        $(CC) -o $@ $^ $(CFLAGS) $(LIBS)

Given this you can see what the problem is: you're trying to link ALL the objects into EACH executable.

Since your programs are built from a single object file with the same prefix as the program name, you actually don't need to write a rule for them at all; you can rely on make's built-in rule for building the programs. Just take the above rule out completely and it should work.

Upvotes: 1

ah008a
ah008a

Reputation: 1145

So basically, TARGETS should contain your source files (.cpp) which are then compiled to create object files, which should be in the OBJS variable so they are then passed to the linker to create the executable file.

As of right now your Makefile seems correct, the error tells you that you have 2 main() functions declared, one in the rtngnode.cpp file and again in the manager.cpp, this cannot happen or else the machine would not know which one to call when the program is executed.

Also undefined reference errors are most of the time due to a linker error, happening most of the time when you include a library to the compiler with #include but not to the linker usually using the LIBS variable in your makefile.

HERE is a small tutorial on Makefiles, which could also be a good read.

Upvotes: 0

Roman
Roman

Reputation: 1503

You should bind your executable with exactly one source file containing the "main" function.

As linker tells you, both manager.cpp and rtngnode.cpp have the definition of main function there. You probably do not want to combine them together. I advice you to start from manually building your code using the gcc commands (using gcc -c to produce object files and gcc -o to produce the executable). Once you understand the logic there - proceed writing makefiles.

By the way, if you can choose your build environment - start with cmake which is less cryptic than makefiles.

Upvotes: 1

DevSolar
DevSolar

Reputation: 70213

The most important thing here has nothing to do with your Makefile (which looks reasonably good except for the somewhat lacking use of compiler warnings):

You may have only one main() in your project, across all source files.

Upvotes: 0

Related Questions