0

I created my Makefile for a simple program but it returns undefined reference for class functions constantly:

g++ -c src/main.cpp -o lib/main.o
g++ -c src/functions.cpp -o lib/functions.o
g++ -c src/Circular.cpp -o lib/Circular.o
g++ lib/main.o -o bin/app.exe
c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: lib/main.o:main.cpp:(.text+0x20): undefined reference to `Circular::Circular()'
collect2.exe: error: ld returned 1 exit status
make.exe: *** [app.exe] Error 1

Here is my Makefile:

app.exe: lib/main.o lib/Circular.o lib/functions.o
g++ lib/main.o -o bin/app.exe

lib/functions.o: src/functions.cpp
g++ -c src/functions.cpp -o lib/functions.o

lib/Circular.o: src/Circular.cpp
g++ -c src/Circular.cpp -o lib/Circular.o

lib/main.o: src/main.cpp
g++ -c src/main.cpp -o lib/main.o

Here is a short snippet of main.cpp:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <string>


#include "../include/Circular.h"
#include "../include/functions.h"

using namespace std;

int main(int argc, const char * argv[]) {

     Circular item;
     return 0;
 }

Circular.h:

#include "node.h"


class Circular
{
    public:
        Circular();
        
        node *start;
        node *last;
        int counter;
}

Circular.cpp:

#include "../include/Circular.h"
#include <iostream>

using namespace std;


Circular::Circular()
{
    start = NULL;
    last = NULL;            
}

and node.h:

struct node
{
    int data;
    struct node *next;
    struct node *prev;
};

I know the problem is about linker and with Makefile but even though I tried different possible solutions, somehow it doesn't work. Therefore, maybe someone can see the mistake I am making. Thanks!

Asil
  • 684
  • 1
  • 6
  • 15
  • 1
    Tip: in C++ use `nullptr` instead of C's `NULL`. – tadman Aug 13 '20 at 17:07
  • Why does `main.o` have so many dependencies? It should have just `src/main.cpp`. Each file *can* have header dependencies as well, but that can be a hassle to maintain without automation. Also `main.exe` should have dependencies listed with their full path, like `lib/main.o` instead of just `main.o`. – tadman Aug 13 '20 at 17:08
  • This `Makefile` and what's running don't match. Notice the build command `g++ lib/main.o lib/main.o lib/functions.o -o bin/app.exe` which includes `main.o` twice and omits `Circular`. – tadman Aug 13 '20 at 17:11
  • @tadman I thought it is necessary since, it includes others. – Asil Aug 13 '20 at 17:11
  • It doesn't include the source, it includes the headers. There's a big difference. If you want header dependency tracking and you're using GCC, [look at this potential solution](https://stackoverflow.com/questions/2394609/makefile-header-dependencies). – tadman Aug 13 '20 at 17:12
  • @tadman The difference comes from a duplication mistake I did as I was posting. Corrected the mistake then but forgot to change the error message, I will edit it. – Asil Aug 13 '20 at 17:14
  • Now your final link pass only includes `lib/main.o` so you're clearly missing the other dependencies. Your `Makefile` should work as posted, but whatever you're actually running looks different. – tadman Aug 13 '20 at 17:18
  • Changing dependencies from ```main.o``` format to ```lib/main.o``` format makes gcc to try to compile the first line straight away before compiling the others. – Asil Aug 13 '20 at 17:22
  • That's the point of a `Makefile`. If it needs to build the dependencies then it builds those first before assembly. You may be jumping ahead here and compiling old versions of the `.o` files before rebuilding those. I'd suggest looking at that question I linked to for a better `Makefile` template because this one is clearly not working for you. Getting this right can be a bit tricky, but there are ways to do it that are well-established. – tadman Aug 13 '20 at 17:23
  • 1
    You may also need to specify the paths to your `.o` files like `lib/main.o: ...` instead of just `main.o: ...` I'd also remove all `.o` files to be sure you're not compiling stale assets. – tadman Aug 13 '20 at 17:25
  • 1
    I deleted the old ```.o``` files and ran make.exe again and gives me the error: ```make.exe: *** No rule to make target `lib/main.o', needed by `app.exe'. Stop.``` You mean changing the ```main.o:``` also to ```lib/main.o``` ? – Asil Aug 13 '20 at 17:25
  • If you're using paths like `src/` and `lib/` that's fine, but you *must be consistent* in their usage. – tadman Aug 13 '20 at 17:26
  • ```g++ -c src/main.cpp -o lib/main.o g++ lib/main.o -o bin/app.exe c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: lib/main.o:main.cpp:(.text+0x20): undefined reference to `Circular::Circular()'``` This is the new error after deleting ```.o```dependencies – Asil Aug 13 '20 at 17:29
  • I thought this dependencies like ```Circular.o:``` is defined internally in the Makefile to let it understand what to compile first. Therefore I did not use their path extentions. Isn't that so? – Asil Aug 13 '20 at 17:31
  • Look very, very closely at the line it's executing. You're doing `main` twice, again, inexplicably. That can't work. You need to have each file listed once and once only. Unless that link stage includes all the parts it won't work. If it doesn't do that check the `Makefile` definition again. – tadman Aug 13 '20 at 17:31
  • What do you mean by doing twice? I want to create a ```main.o``` from ```main.cpp``` first and then create ```.exe``` from the ```.o``` files, if that is what you mean. – Asil Aug 13 '20 at 17:34
  • The `Makefile` you've shown here and what you're running seem dramatically different. I can't help with what I can't see. – tadman Aug 13 '20 at 17:35
  • It's hard to read in the comments. Can you update the question with an edit instead? – tadman Aug 13 '20 at 17:38
  • Sorry, I updated now the post with the final form – Asil Aug 13 '20 at 17:41

2 Answers2

1

The Makefile should be structured to build the dependencies, then the final assembly into a .exe. Each path should be specified exactly as it is, not approximated:

app.exe: lib/main.o lib/Circular.o lib/functions.o 
  g++ lib/main.o lib/Circular.o lib/functions.o -o app.exe

lib/main.o: src/main.cpp
  g++ -c src/main.cpp -o lib/main.o

lib/functions.o: src/functions.cpp
  g++ -c src/functions.cpp -o lib/functions.o

lib/Circular.o: src/Circular.cpp
  g++ -c src/Circular.cpp -o lib/Circular.o

The key here is be consistent and that includes things like the order of things specified in this file. Whatever order you pick, stick to it. This makes tracking down problems way easier.

If this project gets more complex you probably want to pivot to using a dependency tracking Makefile template instead of this homebrew one. Note how in those you don't need to specify a rule for each file, but instead a rule for each type of file, as in .cpp -> .o, and the rest happens automatically.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • I used exactly the same Makefile but the error remains same: ```c:/mingw/bin/../lib/gcc/mingw32/9.2.0/../../../../mingw32/bin/ld.exe: lib/main.o:main.cpp:(.text+0x20): undefined reference to `Circular::Circular()'``` – Asil Aug 13 '20 at 17:46
  • Which `g++` command did it run? I hope you're to the point where you can read that output and check it's linking correctly. If not, focus on that particular line. – tadman Aug 13 '20 at 17:47
  • I get the error in this line: ```g++ lib/main.o -o bin/app.exe``` – Asil Aug 13 '20 at 17:48
  • That is not the command that this `Makefile` runs, so you're doing something very odd here. This doesn't even reference `bin/app.exe`, so you must have something different on your end. – tadman Aug 13 '20 at 17:49
  • I am using windows and since ```make``` doesn't work in windows, downloaded this: http://gnuwin32.sourceforge.net/packages/make.htm and copied the ```make.exe``` into the project file where the ```Makefile``` is and I run it ,if you mean that. – Asil Aug 13 '20 at 17:54
  • I'd suggest using either the actual Gnu Make program, possibly under [WSL](https://learn.microsoft.com/en-us/windows/wsl/about) if necessary, or moving to Visual Studio which eliminates the need for such a thing and can produce executables without all of this fuss. That port you have is from 2006! – tadman Aug 13 '20 at 19:13
1

I managed to create a Makefile from this source .

The Makefile looks like this:

CXX = g++
CXXFLAGS = -std=c++17 -Wall
LXXFLAGS = -std=c++17
OBJECTS = main.o Circular.o functions.o
TARGET = main

$(TARGET): $(OBJECTS)
    $(CXX) $(LXXFLAG) $(OBJECTS) -o $(TARGET)
main.o: main.cpp Circular.cpp Circular.h functions.cpp functions.h
    $(CXX) $(CXXFLAGS) -c main.cpp
Circular.o: Circular.cpp 
    $(CXX) $(CXXFLAGS) -c Circular.cpp
functions.o: functions.cpp 
    $(CXX) $(CXXFLAGS) -c functions.cpp
clean:
    rm -f $(TARGET) $(OBJECTS)

And also added cout to you Circular constructor to check the execution as below:

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

using namespace std;


Circular::Circular()
{
    start = NULL;
    last = NULL;   
    cout << "Yes!" << endl;     
}

Here's the result: Output
Don't forget to put a semicolon for your Circular class in your Circular.h.
NOTE: If you aren't able to use make in cmd,use choco install make.

Hamed
  • 26
  • 4
  • 1
    It's an anti-pattern to add `-c` into the `CXXFLAGS` variable. That should appear in the rule. The `CXXFLAGS` variable should also go on the link line since some flags are meaningful to the linker as well (such as `-g`, `-pthread`, etc.) Also, you don't need `Circular.cpp` as a prerequisite of `main.o`. – MadScientist Aug 13 '20 at 20:59
  • @MadScientist You are totally right about the `-c` flag.Although,any important flags for linker can be added inside `LXXFLAGS`.Besides,I added `Circular.cpp` so if there were any changes in `Circular.cpp` and not inside `main.cpp` then the object files would be regenerated. – Hamed Aug 14 '20 at 05:52
  • If there are changes in `Circular.cpp` then you don't need to rebuild `main.o`. You need to rebuild `Circular.o`, which will happen due to its rule. – MadScientist Aug 14 '20 at 13:17