DEV Community

Cover image for Make and MakeFiles. What's that?
A Serputov
A Serputov

Posted on

Make and MakeFiles. What's that?


The makefile is a text file(group of declarative config files) that contains the recipe for building your program. It usually resides in the same directory as the sources, and it is usually called Makefile . Each one of these commands should be a separate rule in a makefile.

A lot of you saw this file in popular repositories, but maybe won't remember:

Image description

Running the make command by itself will execute the makefile script named Makefile or makefile in the current directory.

Image description

  • Create new directory
mkdir newDirectory
Enter fullscreen mode Exit fullscreen mode
  • Create Makefile inside new directory
touch/ vi Makefile 
Enter fullscreen mode Exit fullscreen mode
  • Type something in our case
target: dependencies
Enter fullscreen mode Exit fullscreen mode


            echo "Software Portability and Optimization is cool"                                                          

Enter fullscreen mode Exit fullscreen mode

Complex Dependencies(Source)

A more complicated build will involve a number of targets and dependencies. C programs, for example, can be compiled into intermediate files, called object files (.o extension), which can then be combined to produce executables.

Picture this scenario:

There are three object files:

double.c, number.h, and sauce.h compile to make: double.o
half.c, number.h, and sauce.h compile to make: half.o
sauce.c compiles to make: sauce.o

There are two binary targets:

double.o and sauce.o can be linked to produce: double
half.o and sauce.o can be linked to produce: half

The Makefile for these relationships may be written like this:


all:         half double

half:        half.o sauce.o
             ${CC} ${CFLAGS} -o half half.o sauce.o

double:      double.o sauce.o
             ${CC} ${CFLAGS} -o double double.o sauce.o

half.o:      half.c number.h
             ${CC} ${CFLAGS} -c half.c

double.o:    double.c number.h
             ${CC} ${CFLAGS} -c double.c

sauce.o:     sauce.c
             ${CC} ${CFLAGS} -c sauce.c
Enter fullscreen mode Exit fullscreen mode

There are several things worth noting about this Makefile:

Variables are used for the name of the compiler and the compiler flags. This makes it very easy to change these values -- to use the gcc compiler, for example, the CC variable could simply be changed to gcc. If variables were not used, you would have to change every line that invoked the compiler.
all is a dummy target. Since it appears as the first target in the file, it is executed by default if no target is specified as an argument to the make command. It depends on the half and double files, which will be built in order to satisfy the dependency. However, the all target does not specify any commands, and the file all will never be built.

When make is executed the first time, five compilations are performed:

$ make
cc         -O3 -c half.c
cc         -O3 -c sauce.c
cc         -O3 -o half half.o sauce.o
cc         -O3 -c double.c
cc         -O3 -o double double.o sauce.
Enter fullscreen mode Exit fullscreen mode

Note that the commands are not being executed in the order in which they appear in the file -- instead, they are ordered according to dependencies.

When executed a second time, no compilations are performed:

$ make
make: Nothing to be done for 'all'.
Enter fullscreen mode Exit fullscreen mode

If the file half.c was edited or the last-modified timestamp (mtime) was updated, running make would execute two compilations:

$ touch half.c
$ make
cc         -O3 -c half.c
cc         -O3 -o half half.o sauce.o
Enter fullscreen mode Exit fullscreen mode

This reveals the power of make -- it does the absolute minimum in order to build the specified target.

On a large programming project, a binary may be comprised of hundreds or even thousands of source files, and compiling all of those files may take hours. If a software developer edits just one file, it's a waste of time to rebuild everything, so make can save a lot of time -- especially when the software is rebuilt many thousand times.

Fake Targets

It is not uncommon to include "fake" targets in a Makefile -- targets which never get built, but which perform a useful operation. For example, a target of "all" never produces an actual file named "all". Typical fake targets include:

all: build all binaries
docs: builts all documentation (e.g., generates PDFs, HTML, manpages, etc)
install: install all files, building binaries, documentation, etc if required
clean: erases all built intermediate and binary files
dist-clean (or distclean): erases all files not included in the original distribution of the source
check (or test): tests the software

The make command will exit as soon as any command pipeline fails, so fake targets which may non-fatally fail are usually forced to return a success code; for example, to delete files which may or may not exist as part of a "clean" target, code such as this may be used:

rm *.o || true
Enter fullscreen mode Exit fullscreen mode


⚠️ Computer Architecture Blog Post: Link


🖇 Follow me on GitHub

🖇 Follow me on Twitter

_p.s This post was made for my Software Portability and Optimization class.

Top comments (0)