Introduction of Make

* Introduction

Building a program from source code typically involves a few common steps:

– Writing the source code

– Process the code to expand all *#include* files, macros, etc.

– Compiling the expanded source code to machine assembly language

– Compiling assembly language to machine  binary code

– Linking binary code in multiple files or libraries into

  an executable binary program

Executing these steps on a small set of source code may be manageable, but

as the source code gets larger, the  process becomes more complex.

* The Make System

The *make* command looks for a file called *Makefile* which

defines targets and their prerequisites, and the rules, or

recipes, used to create them.

#+BEGIN_EXAMPLE

all: myprog

myprog: myprog.o

gcc myprog.o -o myprog

myprog.o:

gcc -c myprog.c

clean:

rm -f *.o myprog core

#+END_EXAMPLE

To build *myprog* with this *Makefile*:

#+BEGIN_EXAMPLE

make myprog

#+END_EXAMPLE

We could shorten it to just this:

#+BEGIN_EXAMPLE

make

#+END_EXAMPLE

** Rules, Targets, Dependencies

A /rule/ consists of a /target/, the files it depends on and the

actions required to build it.

A rule defines:

  – name of the target(s)

  – how to decide if the target is out of date and needs to be built

  – how to create or update the target

*Make* uses the modification times of the

dependencies to determine when the actions should be invoked.

The commands to build a target are executed if:

  – the target file does not exist

  – the modification time of the prerequisite(s) are newer than

    the existing target file

What happens if the target file exists but the prerequisites do not?

Execution lines must begin with a *<TAB>* character; spaces are not permitted.

Command lines can be continued with a backslash as the last character.

Each line is executed as a separate shell command and processed

by the command interpreter defined in the “SHELL” variable which

defaults to “/bin/sh”.

The exit status of each shell is examined by *make*.  If the shell

completed successfully (exit 0), then *make* continues its execution

with the next line. Otherwise, it exits.

The exit value of a line can be ignored by prefacing it with a *-*.

#+BEGIN_EXAMPLE

clean:

      -rm -f *.o a.out core

#+END_EXAMPLE

** Common Targets

Some common targets are *all* and *clean*.  The target *all* typically

builds all of the program targets of the *Makefile*.  The *clean*

target typically cleans up after the compiler to leave the build directory

in its pre-build state.  These are not required nor predefined, you must

define them, if desired.

#+BEGIN_EXAMPLE

all: mywc

mywc: mywc.o

clean:

        @rm -f *.o mywc a.out core

#+END_EXAMPLE

Normally, *make* will echo each line before executing it.  This can be

turned off with an /’@’/ at the beginning of the line.

** Multiple Targets in a Rule

If multiple targets are defined in a rule, *make* treats each as if it

is a separate target with identical prerequisites and commands.

This is useful to add a prerequisite or when similar recipes are used

for all targets.

#+BEGIN_EXAMPLE

king.o queen.o knight.o rook.o: moves.h

#+END_EXAMPLE

** Variables and Macros

*Make* uses variables extensively. Some variables are predefined by *make*.

Environment variables are inherited from the calling

program, and there can be user-defined variables.

Macros are used similarly to variables.

#+BEGIN_EXAMPLE

CC = gcc

OBJECTS = myprog.o

CFLAGS = -g -v

myprog = “myprog”

$(myprog): $(OBJECTS)

        $(CC) $(CFLAGS) $(OBJECTS) -o $@

#+END_EXAMPLE

Environment variables are inherited from the calling environment.

Every environment variable that *make* sees is turned into a *make*

variable with the same name and value.

An explicit variable assignment in a *Makefile* will override the

environment.

In order to prefer the inherited values, use the command-line

flag /-e/.

Environment variables are passed to rule command lines and

recursive *make* commands.

Variables can be added to the environment to make it available

to shell commands, or prevented from being exported:

#+BEGIN_EXAMPLE

export MYVAR1 MYVAR2

unexport GCC CFLAGS

#+END_EXAMPLE

The value of a variable can be appended after it is set:

#+BEGIN_EXAMPLE

SOURCEFILES = main.c printf.c

   …

SOURCEFILES += add.c

#+END_EXAMPLE

Some variables are defined automatically.

Automatic variables are evaluated fresh for each rule that is

executed, instead of having a fixed value. Some examples:

  – $@      Name of the target

  – $<      Name of the first prerequisite

  – $^      Names of all the prerequisites

  – $?      Names of all prerequisites which are newer than the target

A link to a table of automatic variables is included in the References.

** Suffix & Pattern Rules

By itself, *make* knows how to create a /.o/ file from

its corresponding /.c/ file. It has the following

rule based on filename suffixes predefined:

#+BEGIN_EXAMPLE

.c.o:

$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<

#+END_EXAMPLE

The rule may also be written as a /Pattern/ rule:

#+BEGIN_EXAMPLE

%.o: %.c

$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<

#+END_EXAMPLE

The /%/ represents the basename of the target file.  Note the use of

/variables/ to generalize the rule. The /$@/ is the current target and

the /$</ is the current dependency.

Similar to the implicit rule for /.o/ files, *make* has

a predefined implicit rule for making an executable

file from a /.c/ file.

#+BEGIN_EXAMPLE

$(LINK.c) -o $@ $< $(LDLIBS)

#+END_EXAMPLE

The rule uses a macro that is defined as:

#+BEGIN_EXAMPLE

LINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)

#+END_EXAMPLE

Because of predefined macros and implicit rules, our previous example can

be shortened to this:

#+BEGIN_EXAMPLE

all: myprog

myprog:

clean:

rm -f *.o myprog core

#+END_EXAMPLE

Implicit rules make used of many variables, such as:

   – AR           Program to make archive libraries; default *ar*

   – CC           Program for compiling C programs; default *cc*

   – CFLAGS       Extra flags for the C compiler

   – LDFLAGS      Extra flags for the linker

A link to a table of some common variables used by implicit rules

as part of the References.

** Directives

Several /directives/ are available to modify the behavior of *make*:

*** Conditonal Directives

/Conditional/ directive causes parts of the *Makefile* to be used or ignored

based on the value of variables:

  – ifeq /( arg1 , arg2 )/

  – ifneq /( arg1 , arg2 )/

  – ifdef /condition/

  – ifndef /condition/

  – else

  – endif

*** Other Directives

The *include* directive causes *make* to read the specified file(s)

before continuing.  The *override* directive changes the value of a

variable which was set on the *make* command line:

  – include /file1/ /file2/ /…/

  – override /var = value/

** Recursive *make*

The *make* command can be used in a *Makefile* just as any other

command.  This is useful when there are multiple susbsystems which

comprise one larger system:

#+BEGIN_EXAMPLE

subdir = /some/other/dirname

subtarget:

        $(MAKE) -C $(subdir)

#+END_EXAMPLE

Environment variables are passed to each command line and recursive *make*.

By default, only environment variables that came from the environment or the

*make* command line will be passed. The /export/ directive can pass other

variables.

What happens in the following example?

#+BEGIN_EXAMPLE

FOO = foo

show:

      echo $$FOO

#+END_EXAMPLE

** Command Line Arguments

*Make* has many command line arguments which can alter its behavior.

Variables can be set on the command line.

Some common arguments:

  – make -i          /# Ignore the exit value of shell commands/

  – make -d          /# Print debugging information/

  – make -n          /# Dry-run; only print what would be done/

  – make -t          /# Touch files instead of running rules/

  – make -e          /# Environment variables override *Makefile* assignments/

  – make -r          /# Disable the built-in implicit rules/

  – make -j /N/        /# Run N jobs at once/

  – make VAR=value   /# Set the value of variable VAR/

** Further Exploration

Most GNU source code (and many others) use

*autoconf* and *automake* to create templates for *make*

which make it more portable and can better account for the

build machine environment.  These are commands that make

*Makefiles*.

* Make Tutorials Available on the Web

** A Simple Makefile Tutorial:

    [[http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/]]

** Tutorial by Example:

    [[http://mrbook.org/blog/tutorials/make/]]

** A tutorial and reference *PDF*:

    [[https://www.tutorialspoint.com/makefile/makefile_tutorial.pdf]]

* Make References

** The GNU *make* reference manual:

    [[https://www.gnu.org/software/make/manual/make.html

** Catalogue of Built-In Rules

   [[https://www.gnu.org/software/make/manual/make.html#Catalogue-of-Rules]]

** Default Suffix Rules and Predefined Macros

   [[https://docs.oracle.com/cd/E19504-01/802-5880/6i9k05dhg/index.html#make-95873]]

** List of Automatic Variables

   [[https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html]]

** Variables Used by Implicit Rules

   [[https://www.gnu.org/software/make/manual/html_node/Implicit-Variables.html]]

** A Short Guide to Makefiles:

    [[https://users.cs.duke.edu/~ola/courses/programming/Makefiles/Makefiles.html]]

** A GNU *make* cheat sheet on /GitHub/

   Includes a *Makefile* for building a PDF version from LaTex source files:

    [[https://github.com/mxenoph/cheat_sheets.git]]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s