Hello World in MAKE

I had no experience in building software using MAKE. However as I mentioned in my post Let’s build openjdk, I am very interested in how javenet builds their OpenJDK (It is a very good mean to learn how to compose elegant build scripts by studying from open community). But, I know, before I can enjoy OpenJDK’s build scripts, I need to head into MAKE firstly.

Source codes I will use in this Hello World tutorial:

add.c

#include
#include "calc.h"

void add(const char *string)
{
	printf("I am adder %s\n", string);
}

sub.c

#include
#include "calc.h"

void sub(const char *string)
{
	printf("I am subber %s\n", string);
}

calc.c

#include "calc.h"

int main(int argc, char *argv[])
{
	add("1");
	sub("1");
	return 0;
}

calc.h

extern void add(const char *string);
extern void sub(const char *string);

Compile above source codes manually:

luhuang@luhuang:~/workspace/calculator$ pwd
/home/luhuang/workspace/calculator
luhuang@luhuang:~/workspace/calculator$ ls
add.c  calc.c  calc.h  makefile  sub.c
luhuang@luhuang:~/workspace/calculator$ gcc -g -c add.c
luhuang@luhuang:~/workspace/calculator$ gcc -g -c sub.c
luhuang@luhuang:~/workspace/calculator$ gcc -g -c calc.c
luhuang@luhuang:~/workspace/calculator$ gcc -g -o calculator calc.o add.o sub.o
luhuang@luhuang:~/workspace/calculator$ ./calculator
I am adder 1
I am subber 1
luhuang@luhuang:~/workspace/calculator$ ls
add.c  add.o  calc.c  calc.h  calc.o  calculator  makefile  sub.c  sub.o
luhuang@luhuang:~/workspace/calculator$

You can refer to my post How Compiler build Software for more details.

Let’s start using MAKE!

http://www.gnu.org/software/make/manual/make.html

Preparing and Running Make,

You can simply run the command ‘make’ from shell:

luhuang@luhuang:~$ make
make: *** No targets specified and no makefile found.  Stop.
luhuang@luhuang:~$

make needs a file called the makefile that describes the relationships among files in your program and provides commands for updating each file.

In above command, it fails with error ‘no makefile’ file found as under that directory it doesn’t have a default file called ‘makefile’. So firstly let’s compose a makefile.

# I am a comment
# Hello world in MAKE
calculator: calc.o add.o sub.o
	gcc -g -o calculator calc.o add.o sub.o

add.o: add.c calc.h
	gcc -g -c add.c

sub.o: sub.c calc.h
	gcc -g -c sub.c

calc.o: calc.c calc.h
	gcc -g -c calc.c

In above makefile, it defines the relationships among files calc.c, sub.c, add.c, calc.h. A line started with # means it is a comment and will be ignored.

-g means enable debug. -c means create target file and -o means linking the .o files as a program. you need to put a tab character at the beginning of every recipe line! This is an obscurity that catches the unwary. If you prefer to prefix your recipes with a character other than tab, you can set the .RECIPEPREFIX variable to an alternate character. The first two lines are a rule of make.

A simple makefile consists of “rules” with the following shape:

     target ... : prerequisites ...
             recipe
             ...
             ...

Run it!

luhuang@luhuang:~/workspace/calculator$ make
gcc -g -c calc.c
gcc -g -c add.c
gcc -g -c sub.c
gcc -g -o calculator calc.o add.o sub.o
luhuang@luhuang:~/workspace/calculator$
luhuang@luhuang:~/workspace/calculator$ make
make: `calculator' is up to date.
luhuang@luhuang:~/workspace/calculator$ rm *.o
luhuang@luhuang:~/workspace/calculator$ rm calculator
luhuang@luhuang:~/workspace/calculator$ make -f makefile
gcc -g -c calc.c
gcc -g -c add.c
gcc -g -c sub.c
gcc -g -o calculator calc.o add.o sub.o
luhuang@luhuang:~/workspace/calculator$

You could see that the output sequence of the first make command is similar as our manual run. the makefile can be specified by -f option. Also make is smart that if no changes in source code, it will not recompile the program with output ‘make: `calculator’ is up to date.’. If I need to rerun make, I need to run ‘rm *.o’ and ‘rm calculator’ firstly. Let’s improve it by introducing a new target ‘clean’:

clean:
	rm *.o
	rm calculator

and see,

luhuang@luhuang:~/workspace/calculator$ make -f makefile
gcc -g -c calc.c
gcc -g -c add.c
gcc -g -c sub.c
gcc -g -o calculator calc.o add.o sub.o
luhuang@luhuang:~/workspace/calculator$ ls
add.c  add.o  calc.c  calc.h  calc.o  calculator  makefile  makefile~  sub.c  sub.o
luhuang@luhuang:~/workspace/calculator$ make -f makefile clean
rm *.o
rm calculator
luhuang@luhuang:~/workspace/calculator$ ls
add.c  calc.c  calc.h  makefile  makefile~  sub.c
luhuang@luhuang:~/workspace/calculator$

Let’s continue to improve it by removing the repeated here,

# I am a comment
# Hello world in MAKE
SRCS = add.c sub.c calc.c
OBJS = $(SRCS:.c=.o)
HED  = calc.h
PROG = calculator
CC = gcc
CFLAGS = -g

$(PROG): $(OBJS)
	$(CC) $(CFLAGS) -o $@ $^

$(OBJS): $(HED)

clean:
	rm *.o
	rm $(PROG)

In above improved script, it uses key=value to define variables and use $(key) to use it. The variable CFLAGS exists so you can specify flags for C compilation by implicit rules. Here it has another implicit rule: the target file of fileA.c will be named as fileA.o, in above script,  OBJS = $(SRCS:.c=.o) is using this name convention. The value of $@ is the value of left part of rule and $^ is the right part. So in this case, $@ = ‘calculator’ and $^ = ‘$(SRCS:.c=.o)’.

Ok, let me summarize what I learnt so far.

1. What a rule looks like:

     target ... : prerequisites ...
             recipe
             ...
             ...

2. A simple Makefile and how to issue make command with -f

3. How to define variables and use them.

4. Define additional targets

In the coming week, I will continue to refresh myself in make. Like other languages, make will have conditional control, functions, and include directives. After I equip myself with enough make knowledge, I will digest the make scripts of OpenJDK.

!!! Stay tuned !!! 😉