1

I was looking at this flow diagram to understand how makefiles really operate but I'm still struggling to 100% understand what's going on.

I have a main.cpp file that calls upon some function that is defined in function.h and function.cpp. Then, I'm given the makefile:

main: main.cpp function.o
    g++ main.cpp function.o -o main

mainAssembly: main.cpp
    g++ -S main.cpp

function.o: function.cpp
    g++ -c function.cpp

clean:
    rm -f *.o *.S main

linkerError: main.cpp function.o
    g++ main.cpp function.o -o main

What's going on? From what I understand so far is that we are compiling function.cpp, which turns into an object file? Why is this necessary?

I don't know what the mainAssembly part is really doing. I tried reading the g++ flags but I still have trouble understand what this is. Is this just compiling main.cpp with the headers? Shouldn't we also convert main into an object file as well?

I guess main is simply linking everything together into an exe called main? And I'm completely lost on what clean and linkerError are trying to do. Can someone help me understand what is going on?

Community
  • 1
  • 1
Ayumu Kasugano
  • 440
  • 4
  • 13

1 Answers1

2

That flowchart confuses more than it explains as it seems needlessly complicated. Each step is actually quite simple in isolation, and there's no point in jamming them all into one chart.

Remember a Makefile simply establishes a dependency chain, an order of operations which it tries to follow, where the file on the left is dependent on the files on the right.

Here's your first part where function.o is the product of function.cpp:

function.o: function.cpp
    g++ -c function.cpp

If function.cpp changes, then the .o file must be rebuilt. This is perhaps incomplete if function.h exists, as function.cpp might #include it, so the correct definition is probably:

function.o: function.cpp function.h
    g++ -c function.cpp

Now if you're wondering why you'd build a single .cpp into a single .o file, consider programs at a much larger scale. You don't want to recompile every source file every time you change anything, you only want to compile the things that are directly impacted by your changes. Editing function.cpp should only impact function.o, and not main.o as that's unrelated. However, changing function.h might impact main.o because of a reference in main.cpp. It depends on how things are referenced with #include.

This part is a little odd:

mainAssembly: main.cpp
    g++ -S main.cpp

That just dumps out the compiled assembly code for main.cpp. This is an optional step and isn't necessary for building the final executable.

This part ham-fistedly assembles the two parts:

main: main.cpp function.o
    g++ main.cpp function.o -o main

I say that because normally you'd compile all .cpp files to .o and then link the .o files together with your libstdc++ library and any other shared libraries you're using with a tool like ld, the linker. The final step in any typical compilation is linking to produce a binary executable or library, though g++ will silently do this for you when directed to, like here.

I think there's much better examples to work from than what you have here. This file is just full of confusion.

tadman
  • 208,517
  • 23
  • 234
  • 262
  • 1
    Thank you! Can you re-explain what you meant when you redefined function.o? Are you saying we may need to include function.h in case function.cpp includes it in its code? Also, how would a better compilation exactly look like? – Ayumu Kasugano Jul 20 '17 at 05:15
  • [This answer on Makefiles](https://stackoverflow.com/questions/1484817/how-do-i-make-a-simple-makefile-for-gcc-on-linux) looks a lot more conventional. Every `.h` file that you've made, that *could change*, should be listed as a dependency. You can see that in the `$(HEADERS)` part of that example. – tadman Jul 20 '17 at 05:17
  • Read `x: y z` as "the creation of file x depends on files y and z". Later you may have something like `a: x z` where that means "creating a depends on x and z" which means it also indirectly depends on `y` because `x` does. That's where that flowchart gets really batty, when those dependencies accumulate. It's like saying "sandwich: bread tuna mustard" and then later "lunch: sandwich salad" where your lunch ends up depending indirectly on having bread, tuna and mustard. – tadman Jul 20 '17 at 05:19