GNU Make line continuations

It can be difficult to split lines in GNU Makefiles for better readability without changing their meaning; but there are several ways to work around the line-continuation rules.

Line continuation rules for Makefiles

Most programming languages have a facility for breaking up long lines. For example, in the C language a long line may be split by placing a backslash-newline combination in the middle. Consider this line:

printf("This is a long line that can be split up\n");

It can be split into two lines anywhere the programmer desires, e.g.:

printf("This is a long line that \
can be split up\n");

It can even be split within a word:

printf("This is a long line that can be sp\
lit up\n");

This works nicely because the C pre-processor simply deletes any backslash-newline pairs.

Makefiles have a similar facility, but unlike with C, a backslash-newline combination is not simply deleted; instead, the backslash-newline pair and any surrounding whitespace [1] are together replaced with a single space character. So in a Makefile, the following three assignments to x are the same:

x := This is a long line that can be split up

x := This is a long line\
that can be split up

x := This is a long line    \
               that can be split up

Splitting in this way works without changing the meaning as long as the line is split where there is already a single space; but inserting a backslash-newline elsewhere has the deeply unfortunate consequence of injecting a space that often changes the meaning of the line, e.g.:

# The meaning has been changed:
x := This is a long line that can be sp\
lit up

The above is not the same as previous lines; it is the same as the below line (note the extra space inserted into sp lit):

# Extra space was injected here -------v
x := This is a long line that can be sp lit up

Consider this example simplified from a StackOverflow question. The questioner wanted to construct the recursively defined variable CP to hold a Java classpath built as a colon-separated string of paths:

CP = ${ROOT}/lib1.jar:${ROOT}/lib2.jar:${ROOT}/lib3.jar

But because the paths were so long, he wanted to split up each path component onto its own line as follows:

# This doesn't work:
CP = \
${ROOT}/lib1.jar:\
${ROOT}/lib2.jar:\
${ROOT}/lib3.jar

Unfortunately, his attempt fails because of the injected spaces; it is equivalent to:

# Injected spaces ----v-----------------v
CP = ${ROOT}/lib1.jar: ${ROOT}/lib2.jar: ${ROOT}/lib3.jar

This problem occurs frequently in user-defined functions as well, where in addition to keeping down the line length, it's often desirable to split lines at logical boundaries to aid in readability. Consider the resubst function below, which performs repeated calls to $(subst) until nothing more changes:

# Replace $1 with $2 in $3 until no more changes are made.
resubst = $(if $(findstring $1,$3),$(call $0,$1,$2,$(subst $1,$2,$3)),$3)

Even though it fits on a single line, it would be clearer if it could be formatted to show that the $(if) is choosing between the result of the recursive $(call $0,...) and the input, $3:

# This doesn't work; spaces get injected.
resubst = \
  $(if $(findstring $1,$3),\
       $(call $0,\
              $1,\
              $2,\
              $(subst $1,$2,$3)),\
       $3)

Breaking lines without breaking anything else

Sometimes Make's line-continuation rules cause programmers to despair of eliminating those pernicious injected spaces, so they simply avoid breaking lines entirely. They choose instead to create impenetrable one-liners with no logical formatting to aid in understanding, allowing their logic to either disappear far off-screen beyond their editor's right-hand margin or to be wrapped randomly by their text editor into an unreadable mess spanning across many screen lines. This is understandable, as Make doesn't initially appear to offer any support for breaking lines at sensible places; however, there are several techniques shown below for dealing with line continuations without resorting to inscrutable one-liners.

Break lines where spaces don't matter

Sometimes the easiest solution is to break the line where spaces don't matter. The most natural case occurs for whitespace-separated lists, e.g.:

SRCS :=   \
  file1.c \
  file2.c \
  file3.c

This is the original use case motivating the rules for line splitting, and with GNU Make's line-continuation rules it is equivalent to:

SRCS := file1.c file2.c file3.c

It's also usable where a list is being passed to a function, because adding spaces to the start or end of a list doesn't change the semantics. Consider this example:

OBJS := $(addprefix obj/, \
      $(SRCS:.c=.o))

This is equivalent to:

OBJS := $(addprefix obj/, $(SRCS:.c=.o))

The extra space injected by the backslash-newline-indentation at the start of the second argument to $(addprefix) doesn't hurt anything, because the $(addprefix) function treats that argument as a whitespace-delimited list where extraneous whitespace is ignored.

Because Make removes spaces after a function name, splitting a line there doesn't change the meaning. This is usually better than making an overly long one-liner, though readability can suffer compared to splitting the line at more ideal locations. Consider for example the function toMacro that converts filenames into C macros by replacing punctuation characters ., -, and / with underscores:

toMacro = \
  $(subst \
    -,_,$(subst \
    .,_,$(subst \
    /,_,$1)))

This is equivalent to the below one-liner, where the line continuations do no harm because each is replaced with a single space where one is required anyway:

toMacro = $(subst -,_,$(subst .,_,$(subst /,_,$1)))

Strip the return value

Sometimes wrapping a call to $(strip) around a function's return value can remove all of the injected spaces caused by line continuations. Consider, for example, the below loop to display filenames using $(info):

# This results in a bunch of spaces (from $(foreach) and line continuation):
$(foreach f,\
          $(SRCS),\
          $(info $(f))))

Because the $(info) function has an empty return value, the overall result of the $(foreach) loop consists solely of undesired spaces. The $(strip) function removes leading and trailing whitespace, as well as normalizing runs of word-separating whitespace into individual space characters. When presented with nothing but whitespace, it strips the result into the empty string:

$(strip \
  $(foreach f,\
            $(SRCS),\
            $(info $(f))))

Strip individual line continuations

Since $(strip) removes whitespace, it can be used in a targeted way to individually remove each space injected by line continuations. Consider again the toMacro function above:

toMacro = \
  $(subst \
    -,_,$(subst \
    .,_,$(subst \
    /,_,$1)))

This is more readable when formatted as below, but the line continuations inject damaging spaces:

# Injected spaces damage the meaning:
toMacro = \
  $(subst -,_,\
  $(subst .,_,\
  $(subst /,_,$1)))

If we individually surround each sequence of backslash-newline-indentation with a call to the strip function, we can remove the injected spaces individually. To make the purpose of these calls stand out, I prefer using the equivalent syntax ${strip} instead of $(strip):

toMacro = ${strip \
  }$(subst -,_,${strip \
  }$(subst .,_,${strip \
  }$(subst /,_,$1)))

After processing the line continuations, this becomes:

toMacro = ${strip }$(subst -,_,${strip }$(subst .,_,${strip }$(subst /,_,$1)))

Each ${strip } invocation evaporates into the empty string at runtime, giving the equivalent of:

toMacro = $(subst -,_,$(subst .,_,$(subst /,_,$1)))

Wrap line continuations with the "and" function

Another shorter choice is the function "and". Typically it is called with two arguments, but when given a single empty argument, it is defined to return the empty string. The word "and" also hints at the concept of line continuation; it reads as "the first line and the second line and so on".

toMacro = ${and \
  }$(subst -,_,${and \
  }$(subst .,_,${and \
  }$(subst /,_,$1)))

Wrap line continuations with empty variables

Another option is to surround the line continuation with the expansion of an empty variable. Consider a variable named e (for "empty"). If no definition for e is given, it will expand to the empty string, resulting in an even shorter way of suppressing the injected spaces:

toMacro = ${e \
  }$(subst -,_,${e \
  }$(subst .,_,${e \
  }$(subst /,_,$1)))

After processing line continuations, this becomes:

toMacro = ${e }$(subst -,_,${e }$(subst .,_,${e }$(subst /,_,$1)))

Unfortunately, our variable e isn't actually being used here; rather, the referenced variable is named e<space> (an e followed by a space character). The injected space character has corrupted the variable name. But all is not lost; Make permits spaces in variable names, so if we leave the variable e<space> undefined, Make will still evaluate it to the empty string. In fact, it's possible to have a variable whose name is a single space, evaluated using the syntax ${ }. We can remove the e and compress things down to:

toMacro = ${\
  }$(subst -,_,${\
  }$(subst .,_,${\
  }$(subst /,_,$1)))

After processing line continuations, this becomes:

toMacro = ${ }$(subst -,_,${ }$(subst .,_,${ }$(subst /,_,$1)))

This is fairly low syntactical overhead to remove the injected spaces; it costs us only four punctuation characters per line continuation (namely, $, {, \, and }). But we can still do better.

Two-character line continuations

The general way to expand a variable is by wrapping with $() or ${}; but for the special case of a single-character variable name, the use of brackets is optional. Since backslash-newline turns into a single space character, we can expand the single-character variable named <space> simply by prefixing the backslash with $:

toMacro = $\
  $(subst -,_,$\
  $(subst .,_,$\
  $(subst /,_,$1)))

After processing line continuations, this becomes:

toMacro = $ $(subst -,_,$ $(subst .,_,$ $(subst /,_,$1)))

Each occurrence of $<space> expands to the empty string, making a low-overhead and readable method of breaking lines without injecting damaging spaces. It is compatible with GNU Make at least as old as version 3.75 (from August of 1996) and probably even older; it's hard to acquire source for those very early versions in order to verify this behavior.

It's hard to beat two-character line continuations for readability, but we can still do better.

Zero-character line continuations

In the body of user-defined functions, it's important for readability to break lines at sensible boundaries and to indent lines to emphasize their logical structure. It would be ideal if Make would simply remove newlines and spaces used for indentation within the bodies of user-defined functions; but though Make won't do it for us, nothing stops us from doing it ourselves (except the lack of GNU Make 3.81 or newer).

Consider again the toMacro function, this time written with no line continuations at all:

# Ideal syntax, but it doesn't work yet:
toMacro =
  $(subst -,_,
  $(subst .,_,
  $(subst /,_,$1)))

Without the backslashes, Make doesn't have any way to know that all four lines above are part of the function's definition. But there is an alternate syntax for defining recursively expanded variables using define/endef which provide convenient starting and ending delimiters. In this form, Make at least knows which lines belong to our toMacro function:

# Ideal syntax, but contains damaging newlines and indendation:
define toMacro
  $(subst -,_,
  $(subst .,_,
  $(subst /,_,$1)))
endef

When using define, Make does not remove unescaped newline characters or indentation. We can verify this by printing out the value of toMacro:

$(info toMacro$(value toMacro)»)

Yielding:

toMacro=«  $(subst -,_,
  $(subst .,_,
  $(subst /,_,$1)))»

The newlines terminating the first two lines are retained, though Make always removes the final newline. In addition, the two-space indentation is preserved for each line in toMacro as well. All we require is the ability to remove newlines and any spaces used for indentation and then to redefine toMacro with this updated value. For this, we need a couple of helpers.

First, we define the variable empty to be the empty string and the variables space and newline to contain a literal space and newline, respectively:

empty :=

space := $(empty) $(empty)

define newline


endef

Next, we need a way to programmatically create a recursively expanded variable:

# Define recursively expanded variable $1 with value $2.
defineVar = $(eval define $1$(newline)$2$(newline)endef)

These, along with the resubst function introduced earlier, are sufficient to let us define functions two-dimensionally with arbitrary newlines and indentation, then launder away the newlines and indentation to make a single-line function without undesired whitespace.

The general method for laundering a function body comprises three steps:

  1. Prepend a newline to the function body;
  2. Repeatedly replace newline-space pairs with newlines;
  3. Remove all newlines.

The first step ensures that all leading spaces used for indentation are immediately preceded by a newline. Next, replacing a newline-space with a newline deletes the first space of each indented line; this is repeated until all indentation has been removed. Finally, all newline characters are removed, yielding the laundered function body. We define the function def that accepts the name of a multi-line function containing indentation and redefines its body as a single-line function without indentation or newlines:

# $1 - name of function to redefine as a normalized single-line function.
def = $\
  $(call defineVar,$\
         $1,$\
         $(subst $(newline),$\
                 $(empty),$\
                 $(call resubst,$\
                        $(newline)$(space),$\
                        $(newline),$\
                        $(newline)$(value $1))))

Now we can define a function and immediately launder it via def, e.g.:

define toMacro
  $(subst -,_,
  $(subst .,_,
  $(subst /,_,$1)))
endef
$(call def,toMacro)

We can see the resulting function body using $(info):

$(info Normalized toMacro$(value toMacro)»)

With output:

Normalized toMacro=«$(subst -,_,$(subst .,_,$(subst /,_,$1)))»

Caveats

There are a few caveats to consider when inserting extra function calls or variable expansions to swallow the spaces injected by line continuations:

  • The inserted function call or variable expansion can be inserted almost anywhere, but not immediately following an unescaped $. Consider this example:

    output := $(input)
    

    If a line break were attempted using ${strip \} immediately after the $, it would become:

    output := $${strip \
              }(input)
    

    Make will view the pair $$ as a request for a literal $ which changes the logic, resulting in the buggy literal value ${strip }(input).

    However, this is not a significant restriction; breaking a line after an unescaped $ hurts readability, so there is little temptation to choose such a location for a line break.

  • The inserted line-breaking logic hangs around in recursively expanded variable bodies. This causes no problems as long as the $(value) of the variable is never inspected by unsuspecting code. This is not a significant restriction for user-defined functions because their $(value) is typically never inspected; but if a recursively expanded variable holds data that might be later examined, the line-breaking logic could get in the way. Consider the CPPDEFINES variable below:

    CPPDEFINES=$(localCPPDEFINES) -DVARIABLE=VALUE
    

    If this were a long line and a line break were inserted just before the =VALUE, the result would be:

    CPPDEFINES=$(localCPPDEFINES) -DVARIABLE$\
      =VALUE
    

    After Make processes the line continuation, the result is equivalent to:

    CPPDEFINES=$(localCPPDEFINES) -DVARIABLE$ =VALUE
    

    Filtering code scanning for -DVARIABLE=NAME would fail because the inserted logic was inserted at a poorly chosen location, e.g.:

    $(filter-out -DVARIABLE=VALUE,$(value CPPDEFINES))
    

    But it's rare to see such filtering on the $(value) of a variable, and no issue arises if the variable is expanded in the usual way (in this case, via $(CPPDEFINES)) before filtering. Also, data variables are often lists that are easily broken between words anyway using simple backslash-newline continuations. But if there is still an issue for a particular variable, post-processing the $(value) (as done in the def function above) can ensure the variable contains no extraneous line-breaking logic.

Conclusion

By using the above techniques, we can avoid creating unreadable GNU makefiles with unreadable one-liners or lines broken at forced locations. Outside of functions, break lines using \ where whitespace is desired or at least permissible, and use $\ where whitespace is not desired. For user-defined functions, use define/endef and the def function provided above for maximum readability.

Footnotes

[1]Actually, the line continuation rules are more complicated. The POSIX specification says to replace the backslash-newline pair and optional leading white space on the next line with a single space. GNU Make is not POSIX compliant by default; it additionally strips any whitespace preceding the backslash and any consecutive backslash-newline-whitespace sequences. But if the special .POSIX target is defined, then recent versions of GNU Make adhere to the POSIX rules.