C++ weird linker errors, multiple definition of

I’m trying to use makefile for compilation of project in C++, it works fine while I have only main.cpp, but once I add somethink more it break and I can’t figure out why. Notice that it even doesn’t recognize main.

The error image (maybe better for reading): enter image description here

The error text (just in case):

make --jobs=9 build
mkdir -p "./dist" "./dist/obj"
g++ -std=c++17 -o dist/a.out dist/obj/hh.o dist/obj/main.o
dist/obj/main.o: In function `hh::hh()':
hh.cpp:(.text+0x0): multiple definition of `hh::hh()'
dist/obj/hh.o:hh.cpp:(.text+0x0): first defined here
dist/obj/main.o: In function `hh::hh()':
hh.cpp:(.text+0x0): multiple definition of `hh::hh()'
dist/obj/hh.o:hh.cpp:(.text+0x0): first defined here
dist/obj/main.o: In function `hh::b()':
hh.cpp:(.text+0xc): multiple definition of `hh::b()'
dist/obj/hh.o:hh.cpp:(.text+0xc): first defined here
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
make: *** [dist/a.out] Error 1
Makefile:12: recipe for target 'dist/a.out' failed

project structure

root
├───.idea
├───assets
├───dist
│   └───obj
├───doc
├───examples
└───src

My Makefile looks like this

COMPILER :=g++
CPPFLAGS :=-std=c++17
DIRS :=./dist

BUILD_DIR :=./dist
OBJS_DIR :=$(BUILD_DIR)/obj
SRC_DIR :=./src
EXEC_FILE :=a.out

# names of obj files (.o gets added later)
OBJS :=hh main

# uses linker to link everythink together
# using functions addsuffix and addprefix adding path and .o to the name of object 
# files specified in variable OBJS
$(BUILD_DIR)/$(EXEC_FILE): $(addsuffix .o, $(addprefix $(OBJS_DIR)/, $(OBJS)))
    $(COMPILER) $(CPPFLAGS) -o [email protected] $^

# recipe for any file in source files
$(OBJS_DIR)/%.o: $(SRC_DIR)/*.cpp
    $(COMPILER) $(CPPFLAGS) -c $< -o [email protected]

# recipe for any nested file in source files
$(OBJS_DIR)/%.o: $(SRC_DIR)/*/*.cpp
    $(COMPILER) $(CPPFLAGS) -c $< -o [email protected]

# generates dependecies into Makefile.d
deps:
    $(COMPILER) -MM $(SRC_DIR)/*.cpp > Makefile.d

-include Makefile.d

# Callable "scripts" for my IDE
.PHONY: dir_struct, clean, all, rebuild, build

all: dir_struct $(BUILD_DIR)/$(EXEC_FILE)

build: all

rebuild: clean dir_struct all

clean:
    rm -r dist

dir_struct:
    mkdir -p "$(BUILD_DIR)" "$(OBJS_DIR)"

just some dummy code only for testing

// main.cpp
#include "hh.h"

int main() {
    hh g;
    g.b();
    return 0;
}

.

// hh.cpp
#include "hh.h"

hh::hh() = default;

void hh::b() {}

.

#ifndef HH_H
#define HH_H


class hh {
public:
    hh();
    void b();
};


#endif //HH_H

Answer

$(OBJS_DIR)/%.o: $(SRC_DIR)/*.cpp
    $(COMPILER) $(CPPFLAGS) -c $< -o [email protected]

Here you use a wildcard to compile all object files using all cpp files as dependencies. This results every .o file containing only the definitions of the first cpp file listed by $(SRC_DIR)/*.cpp resulting in the name conflict.

Instead of doing this you should create .o files containing only symbols from the corresponding cpp file:

$(OBJS_DIR)/%.o: $(SRC_DIR)/%.cpp
    $(COMPILER) $(CPPFLAGS) -c $< -o [email protected]