1

I modified the gcc/g++ option to place all object files into separate directory posting for my scenario. However, returns error when running make link.

Project Tree

|--- bin
|    |--- main
|--- obj
|    |--- object1.o
|    |--- object2.o
|    |--- ...
|--- src
|    |--- source1.cpp
|    |--- source2.cpp
|    |--- ...
|--- main.cpp

Makefile

SRCDIR = ./src
OBJDIR = ./obj
BINDIR = ./bin
INCDIR = ./src

CC = g++

EXEC = main
SRC = $(wildcard $(SRCDIR)/*.cpp main.cpp)
OBJ = $(addprefix $(OBJDIR)/,$(notdir $(SRC:.cpp=.o)))

compile: $(OBJ)

$(OBJ): $(SRC)
    $(CC) -c $< -o $@

link:
    $(CC) -o $(EXEC) $(OBJ)

clean:
    find -type f -name "$(EXEC)" -delete
    find -type f -name "*.o" -delete
    find -type f -name "*~" -delete

Eg.:

main.cpp

#include <iostream>
#include "Source.h"

using namespace std;

int main(int argc, char* argv[]) {
    Source::hello();
    return 0;
}

Source.h

#include "Source.h"

#ifndef SOURCE_H
#define SOURCE_H

#include <iostream>

using namespace std;

class Source {

    public:
        static void hello();

};

#endif

Source.cpp

#include "Source.h"

void Source::hello() {
    cout << "Hello Makefile" << endl;
}

make compile ok! Create all objects (obj/*.o)

Problem: make link

Erro:

g++ -o main ./obj/Source.o ./obj/main.o
./obj/main.o: In function `Source::hello()':
Source.cpp:(.text+0x0): multiple definition of `Source::hello()'

I know the problem

g++ -c src/Source.cpp -o obj/Source.o

g++ -c src/Source.cpp -o obj/main.o.

I do not know how to solve. Any idea what could be wrong?

Community
  • 1
  • 1
Alan Valejo
  • 1,305
  • 3
  • 24
  • 44
  • 1
    When you are linking, the linker needs to find all the references. It seems to not find the `Classes::functions()` function - which is probably not a problem in your makefile, but in the source file that contains (or SHOULD contain) the `Classes::functions()` - obviously, without seeing your source code, it's nearly impossible to say exactly what is wrong here. – Mats Petersson Sep 14 '13 at 21:49
  • Included my code in post – Alan Valejo Sep 14 '13 at 22:05
  • `Source.h` probably shouldn't have `#include "Source.h"` in it... – Carl Norum Sep 14 '13 at 22:10
  • Strange. Your source.cpp says `Source::Hello` and then `Source::print` in main.cpp, which doesn't exist anywhere else - I'd expect to see a "missing" function, rather than a double declaration. I have a feeling you have edited your source in a way that "alters" the behaviour compares to your REAL source code. – Mats Petersson Sep 14 '13 at 22:12
  • Ok, I can reproduce the problem, but I don't quite know how to fix it. Both `main.o` and `src/Source.o` are made from `src/Source.cpp` (you can see this when you do `make compile` – Mats Petersson Sep 14 '13 at 22:42
  • Knows another way to do a make separating srcdir and objdir? – Alan Valejo Sep 14 '13 at 22:44
  • I think I know WHY it's compiling everything to the same .o file - it's because you are using `$<` in the rule for `$(OBJ): $(SRC)`. I'm still trying to figure out what the fix is - I'm sure it's really trivial, but right now I can't see it. – Mats Petersson Sep 14 '13 at 23:01
  • I realized the problem `g++ -c src/Source.cpp -o obj/Source.o` and `g++ -c src/Source.cpp -o obj/main.o`. I do not know how to solve. – Alan Valejo Sep 14 '13 at 23:04
  • Don't `using namespace std;` in your header file ;-) Nor `#include ` since it's not needed in that header file. – lmat - Reinstate Monica May 03 '17 at 20:53

1 Answers1

2

It may not be the PERFECT solution, but this works:

SRCDIR = ./src
OBJDIR = ./obj
BINDIR = ./bin
INCDIR = ./src

CC = g++

EXEC = main
SRC = $(wildcard $(SRCDIR)/*.cpp main.cpp)
OBJ = $(addprefix $(OBJDIR)/,$(notdir $(SRC:.cpp=.o)))

CXX_FLAGS = -I $(INCDIR) -Wall

.PHONY : compile
compile: $(OBJ)

$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
    $(CC) -c $(CXX_FLAGS) $< -o $@

$(OBJDIR)/main.o: main.cpp
    $(CC) -c $(CXX_FLAGS) $< -o $@

.PHONY : link
link: compile
    $(CC) -o $(EXEC) $(OBJ)

.PHONY : clean
clean:
    find -type f -name "$(EXEC)" -delete
    find -type f -name "*.o" -delete
    find -type f -name "*~" -delete

Note that I've made an automatic rule to compile all the SRCDIR sources, and then a special rule for main, since main is not in the same directory, so the first rule doesn't work.

I also added .PHONY for compile, link and clean, as those are not "real targets", they are just names used to tell make what you want done. It doesn't matter, until at some point you happen to have a file called compile, link or clean in the directory - at that point, your makefile suddenly either fails to work correctly (e.g. it doesn't "compile", because there is a file called compile in your directory, and compile doesn't depend on anything else, so there is nothing to be done according to make).

(For my own benefit, I also added a dependency for compile to link, so I didn't have to type make clean compile link, but could use just make clean link).

As discussed in the comments, the problem is caused by $< being expanded to "the first element in the rule", which is always srcdir/Source.cpp, rather than "whatever source corresponds to the object file.

I think one could do something using for example VPATH or maybe $(<F) variables... But I can't figure out how right now.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227