Make

This document mainly summarizes the “GNU make” manual.

1 overview

The make utility automatically determines which pieces of a large program need to be recompiled, and issues commands to recompile them. It runs in two phases:

  • first phase

    • reads all makefile’s, including the ones included in a makefile

    • internalize all the variables, values, implicit/explicit rules, and builds a dependency graph of all targets and prerequisites

  • second phase

    • determine which targets need to be updated and run the recipes accordingly.

When you give the command:

$ make

make reads the makefile in the current directory and begins by processing the first rule, the default goal, whose name does not start with ‘.’.

  • This behavior can be overridden using the command line (see Arguments to Specify the Goals) or with the .DEFAULT_GOAL special variable.

Before make can fully process this rule, it must process the rules for the files, prerequisites, that the first rule depends on. Each of these files is processed according to its own rule.

  • These rules say to update each file by compiling its source file.

  • The recompilation must be done if the source file, or any of the header files named as prerequisites, is more recent than the object file, or if the object file does not exist.

A Rule that is not a prerequisite of the goal, or a prerequisite of a prerequisite, …, that rule is not processed, unless you tell make to do so (eg. the shell command $ make clean).

2 Rule

2.1 Explicit rules

targets : prerequisites
        recipe
        …
targets : prerequisites ; recipe
        recipe
        …
  • A rule tells make two things:

    • when the targets are out of date, and

    • how to update them when necessary.

  • The targets are file names that needs to be updated, if necessary.

    • in case of multiple targets, they are separated by spaces.

    • Wildcard characters may be used

    • a name of the form a(m) represents member m in archive file a (archiving members as targets).

    • Usually there is only one target per rule, but occasionally there is a reason to have more.

  • The recipe specifies how to update the target

    • lines start with a tab character (or the first character in the value of the .RECIPEPREFIX variable).

    • How to update the target is specified by a recipe. is one or more lines to be executed by the shell (normally ‘sh’), but with some extra features

    • The first recipe line

      • may appear on the line after the prerequisites, with a tab character, or

      • may appear on the same line, with a semicolon.

      Either way, the effect is the same. There are other differences in the syntax of recipes.

  • A target is considered out of date if it does not exist or if it is older, in terms of last-modification time, than any of the prerequisites.

    • The prerequisites consist of file names (with wildcards or archive members) separated by spaces.

    • If any of the prerequisites changes, the contents of the existing target file are considered no longer valid.

  • long line: There is no limit on the length of a line but we may use a backslash / followed by a newline, but this is not required. make will combine the newline with surrounding spaces into a single space.

    • If we don’t want space for the line breaking, we can do the following:

      var := one$\
             word
      

      This will be interpreted as var := one$ word that is var := one$( )word and since the variable ” ” does not exists, i.e., ‘$( )=’, it becomes var := oneword.

2.2 Implicit rules

  • The following does not provides recipes for rules but it works since they are regarded as ‘implicit’, i.e., make can ‘deduce’ these rules by itself:

    objects = main.o kbd.o command.o display.o \
              insert.o search.o files.o utils.o
    
    edit : $(objects)
            cc -o edit $(objects)
    
    $(objects) : defs.h
    kbd.o command.o files.o : command.h
    display.o insert.o search.o files.o : buffer.h
    
  • there are built-in rules that can be used implicitly

  • chained rules combines implicit rules

  • pattern rules defines implicit rules and its target contains %

  • suffix rules are the obsolete way of defining implicit rules for make but supported for backward compatibility.

  • implicit rule search describes how make searches for an implicit rule

2.3 Last-resort rule

  • last resort is a recipe for rules which cannot be found, explicitly or implicitly, written as a terminal ‘match-anything pattern rule’, so that it will match any target, with no prerequisites.

3 operators

4 assignments

  • lists

    objects = main.o foo.o bar.o utils.o
    

    defines a variable named objects with value “main.o foo.o bar.o utils.o”. Whitespace around the variable name and immediately after the ‘=’ is ignored.

  • recursively expanded variables: if it contains references to other variables, these references are expanded whenever this variable is substituted (called recursive expansion but doesn’t seem to be really recursive but ‘asynchronous’).

    foo = $(bar)
    bar = $(ugh)
    ugh = Huh?
    
    all:;echo $(foo)
    

will echo Huh?.

  • simply expanded variables: this is synchronous in a sense that it contains their values as of the time the variable was defined.

    x := foo
    y := $(x) bar
    x := later
    

    is equivalent to

    y := foo bar
    x := later
    
  • conditional variable: define if undefined

    FOO ?= bar
    

    is equivalent to

    ifeq ($(origin FOO), undefined)
      FOO = bar
    endif
    
  • shell assignment: execute a shell script (on the RHS) and set a variable (on the LHS) to its output.

    hash != printf '\043'
    file_list != find . -name '*.c
    

    is equivalent to

    hash := $(shell printf '\043')
    var := $(shell find . -name "*.c")
    

    the exit status of the just-invoked shell script is stored in the .SHELLSTATUS variable

  • += append

    variable := value
    variable += more
    

    is equivalent to

    variable := value
    variable += more
    

    and similar to

    temp = value
    variable = $(temp) more
    

    The difference is that when += is applied to a recursively expanded variable, it stores the text verbatim (value), and saves these variable (temp) and function references to be expanded later, when you refer to the new variable (as $(variable)). Here, the variable temp is never defined though.

5 directives

A directive is an instruction for make to do something special while reading the makefile.

  • include <filenames> suspend reading the current makefile and read one or more other makefiles before continuing. The environment variable MAKEFILES, if defined, does the same without the explicit include directive.

  • ifeq, ifneq, ifdef, ifndef are conditional directives. See “conditional”.

  • define is another way of setting a value for a variable that can include the newline characters. Eg:

    define := two-lines
    echo foo
    echo $(bar)
    endef
    

    is functionally equivalent to

    two-lines := echo foo; echo $(bar)
    

    (‘functionally’ equivalent because two shell commands separated by ‘;’ behave like two separated shell commands.)

    • We can use any value assigning operator ‘=’, ‘+=’, etc, in place of ‘:=’. If omitted, it will be assumed the asynchronous ‘=’.

    • The last newline character will be ignored. So, if we want to assign a single newline character to a variable, we have to put two lines:

      define newline
      
      
      endef
      

6 functions

  • origin <variable> will return the ‘origin’ of ~<variable>.

    $(origin variable)
    

    will return a string that indicates how the variable was defined.

    • undefined’: it was never defined

    • default’: a default definition; the later definition if multiply defined

    • environment’: if inherited from the outer environment

    • environment override’: inherited from the environment but ‘over-rided’ by ‘-e’ option

    • file’: defined in a makefile

    • command line’: defined on the command line

    • override’: defined with an ~override’ directive in a makefile

    • automatic’: automatic variable defined for the execution of the recipe

  • include <filenames> suspend reading the current makefile and read one or more other makefiles before continuing.

    include foo *.mk $(bar)
    

    is equivalent to

    include foo a.mk b.mk c.mk bish bash
    

7 special variables

  • .SHELLSTATUS

  • .DEFAULT_GOAL

  • ~MAKEFILES

8 special targets

8.1 Phony targets

  • Consider the following:

    clean:
            rm *.o temp
    

    There are two possibilities:

    • if there is no file named “clean” then $ make clean will run the rm command;

    • if there is a file named “clean” then, since there is no prerequisites and clean does not create a file, the file will be considered up to date and the recipe won’t be executed.

  • If we want to run the recipe regardless of whether a file “clean” exists or not, we explicitly declare the target to be phony by making it a prerequisite of the special target .PHONY

8.2 clean

  • This is a typical example of using .PHONY rule.

    .PHONY: clean
    clean:
            rm *.o temp
    

8.3 silent

  • .SILENT without prerequisites prevents all recipe echoing, as if all recipes started with ‘@’.

9 The dollar sign

  • $(<function> <arguments>) or $(<function> <arguments>)

    • a function call

    • <function> is a function name

    • <arguments> separated from the function name by one or more spaces or tabs, and if there is more than one argument, then they are separated by commas

    • If the arguments themselves contain other function calls or variable references, it is wisest to use the same kind of delimiters for all the references; write $(subst a,b,$(x)), not $(subst a,b,${x})

  • In case if one need to put $ in a target or prerequisite, see the manual “§4.2 Rule Syntax”.

  • A variable with $ in its name might have strange results, see the manual “§6.3.2 Computed Variable Names”.

  • $(<variable>) or ${<variable}

    • strict textual substitution of a variable’s value, called the variable referencing. The following

      foo = c
      prog.o : prog.$(foo)
              $(foo)$(foo) -$(foo) prog.$(foo)
      

      is equivalent to

      prog.o : prog.c
              cc -c prog.c
      
    • can be used in any context: targets, prerequisites, recipes, most directives, and new variable values

    • $ followed by a character other than ‘$’, ‘(‘ or ‘{‘ treats that single character as the variable name; thus $foo is regarded as $(f) + the string oo

10 Recipe echoing

  • Each line of the recipe is printed (echoed) before it is executed (send to the shell).

  • When a line starts with ‘@’, the echoing of that line is suppressed and the ‘@’ is discarded before the line is passed to the shell.

    echo About to make distribution files
    

    will result the following duplicates

    echo About to make distribution files
    About to make distribution files
    

    but

    @echo About to make distribution files
    

    will give

    About to make distribution files
    

11 flags

  • ‘-n’ or ‘–just-print’ it only echoes most recipes, including even the recipe lines starting with ‘@’, without executing them (dry-running).

  • ‘-s’ or ‘–silent’ flag to make prevents all echoing, as if all recipes started with ‘@’. A rule in the makefile for the special target .SILENT without prerequisites has the same effect

12 conditional

syntax

<conditional-directive>
<text-if-true>
else
<text-if-false>
endif

Conditionals may be nested many times or it may not have else and text-if-false part at all.

12.1 ifdef, ifndef

  • ifdef <variable-name>

  • If the value of that variable has a non-empty, the text-if-true is effective;

    bar =
    foo = $(bar)
    ifdef foo
    frobozz = yes
    else
    frobozz = no
    endif
    

    sets frobozz to yes (the variable is not expanded - otherwise the result would be no.)

  • otherwise, the text-if-false, if any, is effective.

    • Variables that have never been defined have an empty value.

      ifdef foo
      frobozz = yes
      else
      frobozz = no
      endif
      

      sets frobozz to no.

  • Because ifdef does not expand the variable, it will return true for all definitions except those like foo =. To test for an empty value, use ifeq ($(foo),).

12.2 ifeq, ifneq

  • ifeq (arg1, arg2) or ifeq 'arg1' 'arg2' or ifeq "arg1" "arg2"

    • ifeq expand all variable references in arg1 and arg2 and compare them.

    • If they are identical, the text-if-true is effective;

    • otherwise, the text-if-false, if any, is effective.

    foo: $(objects)
    ifeq ($(CC),gcc)
            $(CC) -o foo $(objects) $(libs_for_gcc)
    else
            $(CC) -o foo $(objects) $(normal_libs)
    endif
    

When the variable CC has the value ‘gcc’, the above example has this effect:

foo: $(objects)
        $(CC) -o foo $(objects) $(libs_for_gcc)

When the variable CC has any other value, the effect is this:

foo: $(objects)
        $(CC) -o foo $(objects) $(normal_libs)
  • ifneq (arg1, arg2) or ifneq 'arg1' 'arg2' or ifneq "arg1" "arg2" will do the opposite.