Foognostic blogs Seeking knowledge of foo

21Dec/08Off

GNU’ve got to be kidding me — Makefiles?

It's very nerdy but several years ago I really enjoyed getting to learn and use GNU Make. It's best known for compiling and linking C and C++, but it really is a powerful general purpose tool. I'm trying to get back into it and decided to publish that refresher process. Jibe or jive as you will!

First, here is foo.cpp:

#include <iostream>
int main() {
    std::cout << "Hello, world" << std::endl;
    return 0;
}
And here is the Makefile to compile it, link it, and run it:
run: foo.cpp
    g++ foo.cpp
    ./a.out
There are three concepts here.

  1. run
    -- this is the target, the goal, what you are trying to do.
  2. foo.cpp
    -- the dependencies of the goal.
  3. g++ foo.cpp
    and
    ./a.out
    . Look ma, shell commands! Nothing more, nothing less than the CLI environment many live and breathe.

Just type 'make' and

Hello, world

will follow shortly. This is a pretty bad Makefile though for lots of reasons. Only having one step to compile, link, and run means compiling and linking will happen everytime the Makefile is processed. To fix that, give each stage its own rule:

run: a.out
    ./a.out

a.out: foo.o g++ foo.o

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

Running, linking, and compiling from top to bottom. Make automatically tracks modification dates of dependencies. It will only execute shell commands for objects with modified dependencies. Here is the output from the first time I ran this Makefile.
g++ -c foo.cpp
g++ foo.o
./a.out
Hello, world
Without changing foo.cpp I immediately ran make again. Here was the output:
./a.out
Hello, world
So that takes care of one glaring problem in the original Makefile. But I just created bar.cpp (it compiles, trust me) and I don't want to add a nearly duplicate rule. That would mean duplicating rules everytime I add a new source file, right?

Make solves that with a page from the Perl playbook; a little punctuation is good, and too much punctuation is awesome! This is perfectly clear, right?

a.out: %.o
    g++ $^

%.o: %.cpp g++ -c $<

'%' is a wildcard character, similar to '+' and '*' in traditional regular expressions. The others I pulled from the documentation on automatic variables, but they refer to the dependencies in the rule (things following the : character).

So how'd that go? FAIL.

make: *** No rule to make target %.o', needed bya.out'.  Stop.
And this is where I remembered gmake has a healthy, happy family of different types of rules. Using '%' in the target of a rule makes it a pattern rule. But the rule for a.out is not a pattern rule, so it looked at %.o as a literal file name. What if I use

a.out: *.o

instead?

g++ -c bar.cpp
g++ .o
Undefined symbols:
  "_main", referenced from:
      start in crt1.10.5.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
make: ** [a.out] Error 1
Crap, it did what I said, not what I meant. I said match *.o, which becomes true as soon as one is created. What I wanted was to match ALL the .o files. No getting around that, but at least we can act a little more grown up and use <gasp>variables</gasp>.
SRCS = foo.cpp bar.cpp
OBJS = foo.o bar.o

a.out: $(OBJS) g++ $^

And that works. But now we have another problem! Who wants to update the Makefile everytime a new cpp file is added?? Well, of course there is a solution for this. Half is strange and half is really cool:
SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o)
The strange half is calling a GNU Make function 'wildcard'. That just seems strangely difficult for something reasonably common. Anyways, the cool part was the syntactic sugar to create OBJS by replacing .cpp with .o from $SRCS.

That's about all the refresher I can handle for now. It should be enough to get me going... having done non-trivial cross-platform builds with gmake I know more is possible but I don't need it sitting here at home. At least I know great documentation will be ready when I need it.

Tagged as: Comments Off
Comments (0) Trackbacks (0)

Sorry, the comment form is closed at this time.

Trackbacks are disabled.

Foognostic blogs is Digg proof thanks to caching by WP Super Cache