My golden Makefiles for compiling C programs

This post was written by eli on August 11, 2016
Posted Under: Linux,Software

Single-source utilities

This is the Makefile I use for compiling a lot of simple utility programs, one .c file per utility:

CC=    gcc
FLAGS=  -Wall -O3 -g -fno-strict-aliasing

ALL=    broadclient broadserver multicastclient multicastserver
all:    $(ALL)

clean:
      rm -f $(ALL)
      rm -f `find . -name "*~"`

%:    %.c Makefile
      $(CC) $< -o $@ $(FLAGS)

The ALL variable contains the list of output files, each have a corresponding *.c file. Just an example above.

The last implicit rule (%: %.c) tells Make how to create an extension-less executable file from a *.c file. It’s almost redundant, since Make attempts to compile the corresponding C file anyhow, if it sees a target file with no extension (try “make –debug=v”). If the rule is removed, and the CFLAGS variable is set to the current value of FLAGS, it will work the same, except that the Makefile itself won’t be dependent on.

IMPORTANT: Put the dynamic library flags (e.g. -lm, not shown in the example above) last in the command line, or “undefined reference” errors may occur on some compilation platforms (Debian in particular). See my other post.

Multiple-source utilites

CC=    gcc
ALL=    util1 util2
OBJECTS = common.o
HEADERFILES = common.h
LIBFLAGS=-fno-strict-aliasing
FLAGS=    -Wall -O3 -g -fno-strict-aliasing

all:    $(ALL)

clean:
 rm -f *.o $(ALL)
 rm -f `find . -name "*~"`

%.o:    %.c $(HEADERFILES)
 $(CC) -c $(FLAGS) -o $@ $<

$(ALL) : %: %.o Makefile $(OBJECTS)
 $(CC) $< $(OBJECTS) -o $@ $(LIBFLAGS)

Note that in this case LIBFLAGS is used only for linking the final executables

Strict aliasing?

It might stand out that the -fno-strict-aliasing flag is the only one with a long name, so there’s clearly something special about it.

Strict aliasing means (in broad strokes) that the compiler has the right to assume that if there are two pointers of different types, they point at different memory regions (and not just not having the same address). In other words, dereferences of pointers (as in *p) of different types are treated as independent non-pointer variables. Reordering and elimination of operations is allowed accordingly.

For example, a struct within a struct. If you have a pointer to the outer struct as well as one to the inner struct, you’re on slippery ice.

The actual definition is actually finer, and some of it is further explained on this page (or just Google for “strict aliasing”). Regardless, Linus Torvalds explains why this flag is used in the Linux kernel here.

The thing is that unless you’re really aware of the detailed rules, there’s a chance that you’ll write code that works on one version of gcc and fails on another. The difference might be where and how this or another compiler decided to optimize the code. Such optimization may involve reordering of operations with mutual dependency or optimizing away things that have no impact unless some pointers are related.

This is true in particular for code that plays with pointer casting. So if the code is written in the spirit of “a pointer is just a pointer, what could go wrong”, -fno-strict-aliasing flag is your friend.

Add a Comment

required, use real name
required, will not be published
optional, your blog address