10

I am trying to write a makefile which uses macros to create multiple executables from multiple files at once. I tried searching through previously answered questions but, because I am fairly new to programming in C as well as working with gcc, I was not able to find an answer to my question.

Here is what I have so far:

CC=gcc
CFLAGS=-I.
OBJ = ex1.c ex3.c
EXECUTABLE = ex1 ex3

$(EXECUTABLE): $(OBJ)
    gcc -o $@ $^ $(CFLAGS)

clean:
    rm -f $(EXECUTABLE)

I would like the line

$(EXECUTABLE): $(OBJ)

to create executables ex1 and ex3 from files ex1.c ex3.c respectively.

4 Answers 4

19

For this particular case, where each executable has a single source file with .c extension, all you need is a one line Makefile:

all: ex1 ex3

The built-in default rules for make then work already:

$ make
cc -O2 -pipe   ex1.c  -o ex1
cc -O2 -pipe   ex3.c  -o ex3

Behind the scene, make is using the POSIXly mandated built-in single suffix rule

.c:
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<

Vary the command to your liking with make CC=gcc CFLAGS=-O2 LDFLAGS=-s and similar.

Trivia of the day: in fact, if you are willing to name the targets when invoking make, you can use an empty or even run without any Makefile:

$ make -f /dev/null CC=gcc CFLAGS=-O2 LDFLAGS=-s ex1 ex3
gcc -O2 -s ex1.c  -o ex1
gcc -O2 -s ex3.c  -o ex3
$ rm -f Makefile ex1 ex3
$ make CC=gcc CFLAGS=-O2 LDFLAGS=-s ex1 ex3
gcc -O2 -s ex1.c  -o ex1
gcc -O2 -s ex3.c  -o ex3

Make magic!

As a rule of thumb, don't reinvent the wheel (or rules), use the rules that are already there. It simplifies your and make's life a lot. This makes for small and sexy makefiles to impress the ladies with :-)

Sign up to request clarification or add additional context in comments.

1 Comment

Very helpful! Thanks for some insight into the inner workings of the make command.
3

Some suggestions (assuming you use GNU make, not something else)

First, run once make -p, you'll understand what builtin rules make is knowing. Look in particular for COMPILE.c and LINK.c

Then, I suggest

 CFLAGS= -g -Wall -I.

(because you really want -g for debugging, and -Wall to get most warnings)

And you probably don't need

$(EXECUTABLE): $(OBJ)
    gcc -o $@ $^ $(CFLAGS)

However, I suggest adding before most other rules

.PHONY: all clean

all: $(EXECUTABLES)

Actually, I would code your Makefile (for GNU make!) as follow

# file Makefile
CC= gcc
RM= rm -vf
CFLAGS= -Wall -g
CPPFLAGS= -I.
SRCFILES= ex1.c ex2.c ## or perhaps $(wildcard *.c)
OBJFILES= $(patsubst %.c, %.o, $(SRCFILES))
PROGFILES= $(patsubst %.c, %, $(SRCFILES))

.PHONY: all clean

all: $(PROGFILES)
clean:
     $(RM) $(OBJFILES) $(PROGFILES) *~
## eof Makefile

Remember that tab is a significant character in Makefile-s (action part of rules). In this answer, lines starting with four spaces at least should really start with a tab character.

Once everything is debugged consider running make clean to clean everything, and then make -j CFLAGS=-O2 all to compile in parallel everything with optimizations.

At last, I recommend using remake and running remake -x to debug complex Makefile-s

Of course, I'm supposing that your directory has only single-file programs.

BTW, there are other build automation tools. Perhaps you might consider using omake or ninja. For building large programs (millions of source code lines) consider also automake, ccache, cmake, icecream. In some cases, consider generating some C code with GPP, GNU bison, SWIG, etc... or using your own Python or Guile script (or C meta-program). See also this draft report.

Don't forget to use a version control system like git for your source files. It is also time to learn such a tool.

3 Comments

COMPILE.c and LINK.c are GNUisms; you make an assupmtion here that the OP is using GNU make. The better (portable) idea is to use the built-in single suffix rule for .c.
Yes, I added a mention of GNU make
Thank you for the advice as well as suggesting git and omake.
1

The following answer includes multiple executable such as initiate, process1, process2, ..., process4.

LOCAL_INCLUDE=./

all: clean process_first process_second init

process_first:
    gcc -g -o process1  -I$(LOCAL_INCLUDE) process1.c  -lzmq  -L. -L./.
    gcc -g -o process2  -I$(LOCAL_INCLUDE) process2.c  -lzmq  -L. -L./.

process_second:
    gcc -g -o process3  -I$(LOCAL_INCLUDE) process3.c  -lzmq  -L. -L./.
    gcc -g -o process4  -I$(LOCAL_INCLUDE) process4.c  -lzmq  -L. -L./.

init:
    gcc -g -o initiate -I$(LOCAL_INCLUDE) initiate.c -lzmq -lconfig -lpthread -L. -L./. -ldl -lrt

clean:
    rm -rf init_manager.o init_manager
    rm -rf process1 process2 process3 process4

NOTE: It is a good practice to clean and touch all the executable files before making them again.

Comments

0

You're close, but you need a pattern rule:

$(EXECUTABLE): % : %.c

And then a default rule to make it build both:

all: $(EXECUTABLE)

2 Comments

Pattern rules likes this are a GNUism it would appear. The built-in single suffix rule for the .c suffix does the trick already.
I'm looking into pattern rules now. Thank you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.