tags:

views:

342

answers:

4

I don't know much about creating Makefiles, but I've been reading the make manual and I have made some progress. My Makefile works and does what I want.

My situation usually involves between 1 and 3 different program needed to be compiled and sent to my TA for marking and such via a webform. The structure of each application is 'prog.c', 'prog_lib.h', and 'prog_lib.c'. In the past, I have been creating separate directories for each program and creating separate Makefiles for each directory to build the program contained within. I then tar each folder and submit them separately.

Recently, the TAs have been asking for all source files to be in one directory and one Makefile with the various targets to be built so their marking applications can work without any human intervention.

I was wondering how someone more experienced would improve this Makefile and how my situation in general is usually solved? I would like to reduce the amount of typing I need to do when I move on to my next assignment and have to update several places.

Here is my Makefile:

ASSIGNMENT = 3
TARNAME = Assignment$(ASSIGNMENT).tar.bz2

CC = gcc
CFLAGS = -O2 -Wall -ansi -pedantic -W # I like warnings
LDFLAGS = -lm
DEBUG = -g # to resolve symbols in GDB and valgrind

FREQ_OUT = frequency_table
FREQ_SOURCES = frequency_table.c frequency_table_lib.c
FREQ_OBJECTS = frequency_table.o frequency_table_lib.o

DECODE_OUT = decode
DECODE_SOURCES = decode.c decode_lib.c
DECODE_OBJECTS = decode.o decode_lib.o

SOURCES = $(FREQ_SOURCES) $(DECODE_SOURCES)
OBJECTS = $(FREQ_OBJECTS) $(DECODE_OBJECTS)
OUT = $(FREQ_OUT) $(DECODE_OUT)

.PHONY: info
info:
    @echo -e "make info\n" \
            "\tmake all \t\t\tMake all targets\n" \
            "\tmake frequency_table \t\tMakes frequency table\n" \
            "\tmake decode \t\t\tMakes decode\n" \
            "\tmake dist \t\t\tMakes tar archive of sources and Makefile\n" \
            "\tmake clean \t\t\tRemoves all the object files and executables\n" \
            "\tmake distclean \t\t\tPerforms clean and removes tar archive"

.PHONY: all
all:    $(OUT)

$(FREQ_OUT):    $(FREQ_OBJECTS)
    $(CC) $(CFLAGS) $(DEBUG) $(LDFLAGS) $(FREQ_OBJECTS) -o $@

$(DECODE_OUT):  $(DECODE_OBJECTS)
    $(CC) $(CFLAGS) $(DEBUG) $(LDFLAGS) $(DECODE_OBJECTS) -o $@

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

.PHONY: dist
dist: $(SOURCES)
    @echo "Creating tar archive. See $(TARNAME)"
    tar cvjf $(TARNAME) $(SOURCES) $(wildcard *_lib.h) Makefile

.PHONY: clean
clean:
    rm -f $(OUT) $(OBJECTS)

.PHONY: distclean
distclean: clean
    rm -f $(TARNAME)
+2  A: 

In my experience, you shouldn't need the .o target in there, that's implicit.

Also, the implicit version normally includes the value of $(CPPFLAGS), which is where you should put any -Ipath or -Dmacro options that you might require.

I'd also include the $(DEBUG) in $(CFLAGS), rather than list it explicitly in any of the build targets.

Alnitak
+3  A: 

You really don't need the $(CC) $(CFLAGS) $(DEBUG) $(LDFLAGS) $(FREQ_OBJECTS) -o $@ lines. make already knows how to build binaries.

If your filenames are constant for different binaries (binary.c and binary_lib.c), you can also create a general rule for that:

FOO := $(shell ls *_lib.c)
BIN = $(FOO:%_lib.c=%)

$(BIN) : % : %.o %_lib.o

EDIT: Here's how it works:

  1. FOO is the list of all files ending with _lib.c
  2. BIN is the same list, with the "_lib.c" suffixes removed, so it's the list of your binaries
  3. The last line is your make rule. The rule states each foo in $(BIN) depends on foo.o and foo_lib.o
Can Berk Güder
This works really well but I'm not exactly sure why it works. Could you clarify this a bit? Specifically the $(BIN) line. What purpose does the lone % serve?
Nick Presta
Thanks for the explanation. I am using this method and it works very well.
Nick Presta
+1  A: 

Example:

# CC, CFLAGS, etc. go here

# Object variables go here
MYPROG_OBJECTS = main.o someother.o

# all, info, etc. targets go here

# first actual target:
myprog: $(MYPROG_OBJECTS)
    <Do the linking stuff>

# This will take care of the objects, sources are placed in src directory:
%.o: src/%.c
   <The usual compilation commands>

# Automatic dependency magic:
%.d: src/%.c
   $(CC) -MM -o$@ $<

-include (MYPROG_OBJECTS:%.o=%.d)
TrayMan
+3  A: 

Make the 'all' target the first one, unless you are really sure your users should have to type something after 'make' to get the project to build. The 'info' target is nice but aconventional.

(I do have one makefile where the default target is not all - in a directory with source code for 100+ commands. I do not want 'make all' to be the default; I expect to build just the one or two that I want to build. There is an 'all'. But that is very unusual. Normally, the default should be 'all'.)

Also, neither '$(FREQ_OUT)' nor '$(DECODE_OUT)' is a PHONY target; they are real programs, aren't they? The 'all', 'info', 'dist', 'clean', 'realclean' etc targets - those are phony. But the programs you build are not.

Jonathan Leffler
Thanks for the tip and correct about the .PHONY targets which are real. I've removed that part and updated the example.
Nick Presta