nonsensation
nonsensation

Reputation: 3727

Makefile: multiple definition and undefined reference error

Im currently learning how to code without an IDE and so Im learning how to write makefiles. Here is my current test-project:

\__ /CoDstructor/
      |\__ Makefile
      |\__ /bin/
      |      \__ CoDstructor.exe
      |\__ /src/
      |     \__ /cod/
      |          |\__ main.cpp
      |          |\__ types.cpp
      |           \__ types.hpp
       \__ /obj/
            \__ /cod/
                 |\__ main.o
                 |\__ main.d
                 |\__ types.o
                  \__ types.d

I only have one single top level makefile that handles every module in the src/ directory and creates the objects and dependency files in the obj/ directory. Here are the files:

main.cpp

#include <iostream>
#include <cod/types.hpp>

int main() {
    int s;
    std::cin >> s;
    return lol();
}

types.hpp

#ifndef TYPES_HPP_INCLUDED
#define TYPES_HPP_INCLUDED

int lol();

#endif // TYPES_HPP_INCLUDED

types.cpp

#include <iostream>
#include <cod/types.hpp>

int lol() {
    std::cout << "lol";

    return 0;
}

Makefile

APP_NAME = CoDstructor
DEBUG_TARGET = debug
RELEASE_TARGET = release
SRC_DIR = src
OBJ_DIR = obj
BIN_DIR = bin
INC_DIR = src

INCLUDE_DIRS +=

LIBRARY_DIRS +=

CXXFLAGS += -Wall
CXXFLAGS += -Werror
CXXFLAGS += -Wextra
CXXFLAGS += -pedantic
CXXFLAGS += -std=c++11

$(DEBUG_TARGET): CXXFLAGS += -g
$(RELEASE_TARGET): CXXFLAGS += -O3

LDFLAGS += -static
LDFLAGS += -static-libstdc++
LDFLAGS += -static-libgcc

$(DEBUG_TARGET): LDFLAGS += -g
$(RELEASE_TARGET): LDFLAGS +=

CPPMACROS =

$(DEBUG_TARGET): CPPMACROS += DEBUG
$(RELEASE_TARGET): CPPMACROS += NDEBUG

CXXFLAGS += $(foreach i,$(INC_DIR),$(addprefix -I,$(i)))
CXXFLAGS += $(foreach i,$(INCLUDE_DIRS),$(addprefix -I,$(i)))
CXXFLAGS += $(foreach i,$(CPPMACROS),$(addprefix -D,$(i)))

LIBS = $(foreach i,$(LIBRARY_DIRS),$(addprefix -L,$(i)))

SOURCES =  $(subst ./,,$(shell find . -name *.cpp))
OBJS = $(subst $(SRC_DIR),$(OBJ_DIR),$(SOURCES:.cpp=.o))

DEPS = $(OBJS:.o=.d)

all: $(DEBUG_TARGET) clean

$(RELEASE_TARGET): $(BIN_DIR)/$(APP_NAME) clean
    @echo Building release...

$(DEBUG_TARGET): $(BIN_DIR)/$(APP_NAME) clean
    @echo Building debug...

$(OBJS): $(SOURCES)
    @mkdir -p $(@D)
    $(CXX) -MMD -MP $(CXXFLAGS) -c $< -o $@

$(BIN_DIR)/$(APP_NAME): $(OBJS)
    $(CXX) $(LDFLAGS) $^ -o $@ $(LIBS)
    @echo $^

.PHONY: clean

clean:
    @echo clean
#   @-rmdir $(OBJ_DIR)

-include $(DEPS)

And here is the error:

obj/cod/types.o: In function `main':
D:\PROJECTS\CoDstructor/src/cod/main.cpp:4: multiple definition of `main'
obj/cod/main.o:D:\PROJECTS\CoDstructor/src/cod/main.cpp:4: first defined here
obj/cod/main.o:main.cpp:(.text+0x2a): undefined reference to `lol()'
obj/cod/types.o:main.cpp:(.text+0x2a): undefined reference to `lol()'
collect2.exe: error: ld returned 1 exit status

I searched for almost two days now how to resolve these errors.

For the first one (multiple definitions of main): I already checked the OBJS variable, and it contains and links only one time the main.o. Also why says it obj/cod/types.o in the first line? There is no main in types.hpp/types.cpp.

The second error (undefined reference to lol()): Why is the reference undefined, but gives no compiler error instead?

The third error (it rebuilds everything everytime, instead of only changed ones or instead of looking up the .d dependency files)

I am running the latest MinGW32 build (g++ 4.9.1) and latest MSYS (make).

What am I doing wrong here?

Upvotes: 1

Views: 1825

Answers (3)

David Hammen
David Hammen

Reputation: 33126

Your key problem is here:

$(OBJS): $(SOURCES)
   @mkdir -p $(@D)
   $(CXX) -MMD -MP $(CXXFLAGS) -c $< -o $@

This rule has two problems.

  1. You have multiple object files. The first action won't work when you go beyond having just one subdirectory under your src directory.

  2. The second action compiles your src/cod/main.cpp twice, once into obj/cod/main.o and then into obj/cod/types.o. That's because $< is the first item in the dependency list, and that first item is src/cod/main.cpp.

The second problem is easier to fix than the first. You need a pattern rule:

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
   $(CXX) -MMD -MP $(CXXFLAGS) -c $< -o $@

Now to address the first problem. What if you have multiple source directories, each a subdirectory of your src directory? You want to make a corresponding obj subdirectory for each one of those. Also note that there's a directory you aren't making, the bin directory. The first thing to do is build a list of the directories you need to make.

MKDIRS = $(sort $(foreach i,$(OBJS),$(dir $i)))
MKDIRS += bin

Then you need a rule to make them. Let's start simply:

mkdirs:
   mkdir -p $(MKDIRS)

This however is problematic. You'll get error messages from mkdir and the build will stop if any one of those directories already exists. We need to make those directories only if they don't exist. Make does provide the tools to filter that list down to only the directories that don't exist, but I'd rather not do that. To me it's better to use the shell to make some decisions:

mkdirs:
   @sh -c \
     'for d in $(MKDIRS); do \
        if [ ! -d $$d ]; then echo mkdir -p $$d; mkdir -p $$d; fi \
      done'

Now we need to add that rule as a dependency.

$(RELEASE_TARGET): mkdirs $(BIN_DIR)/$(APP_NAME)
   @echo Release built

$(DEBUG_TARGET): mkdirs $(BIN_DIR)/$(APP_NAME)
   @echo Debug built

Note that I've done three things to those targets.

  1. I added the mkdirs target.

  2. I deleted the clean target. You really don't want to do that here. It defeats the purpose of separate compilations. When you have hundreds of source files and make a change in one of them, you just want to recompile that one source file and then rebuild the executable from the hundreds of object files that already exist. Don't do a clean right after you built the executable! You can always do a make clean from the command line if you feel compelled to do so.

  3. I changed the messages. Those messages will be issued after the dependencies have been satisfied. They'll be the last thing you see. To get messages before action starts it's easiest to build a phony target that prints the desired message.

A couple of final notes:

Note that mkdirs is a phony target (and so is all). It's best to add it to your .PHONY list, and that list is best placed up-front.

Finally, the target $(DEBUG_TARGET): mkdirs $(BIN_DIR)/$(APP_NAME) is a ticking time bomb. The same goes for $(RELEASE_TARGET). One day you'll find out about parallel make. There's no guarantee that the directories will be made before the compiler tries to compile the code. The directories won't exist, and kaboom, your make just failed. Making your makefile robust against parallel execution is a matter of a different stackexchange question.

Upvotes: 2

hyde
hyde

Reputation: 62908

From memory, did not check the docs, problem is this:

$(OBJS): $(SOURCES)
    @mkdir -p $(@D)
    $(CXX) -MMD -MP $(CXXFLAGS) -c $< -o $@

That means that every object file depends on all sources. And then the compile command uses $< which I think means the first dependency. So in effect, you compile both types.o and main.o from main.cpp (which is first of the $(SOURCES), I suppose).


One solution would be to use a pattern rule, something like:

 %.o : %.c
     @mkdir -p $(@D)
     $(CXX) -MMD -MP $(CXXFLAGS) -c $< -o $@

Object files are already requied by your rule $(BIN_DIR)/$(APP_NAME): $(OBJS), and this pattern rule will tell make how to generate them.

Upvotes: 2

keltar
keltar

Reputation: 18409

Your $(OBJS): $(SOURCES) rule is not what you think it is. As a result, you're building both main.o and types.o from the same main.cpp file ($< param in command line). Hence you have two identical files which are conflicting, while types.cpp wasn't even built.

Correct rule would be $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp

Upvotes: 3

Related Questions