In this article, Ask Mr. Make covers what's new in GNU Make 3.81.
GNU Make has supported SysV Make's [email protected] syntax in the prerequisite list of a rule. For example, it was possible to say foo: [email protected] and it was equivalent to foo: foo.c (i.e. [email protected] had the value that [email protected] has in the rule's commands). That's changed in GNU Make 3.81. To get that functionality you must defined .SECONDEXPANSION in the Makefile. As a bonus GNU Make supports all the standard automatic variables (that make sense---$$? will always be blank). This happens because GNU Make will expand the prerequisite list of a rule twice: once when it reads the Makefile and once again when searching for what to make. For example, you can do the following:
and get the output
That's because when the Makefile was read all: $$(FOO) was expanded to all: $(FOO) and then later when figuring out how to build all $(FOO) was expanded to the value FOO had when Makefile parsing ended (in this case bar). Note that this means that if you enabled second expansion and have file names with $'s in them (e.g. you have foo: foo$bar) then the $'s will need to be escaped by writing $$.
GNU Make now supports non-nested else branches by having the conditional on the same line as the else. For example, it's now possible to write:
which would previously have been:
-LThe new command-line option -L (and its equivalent --check-symlink-times) causes GNU Make to consider the modification time of the symlink and not the file pointed to by the symlink as GNU Make decides which files need to be remade.
.INCLUDE_DIRS, .FEATURES, .DEFAULT_GOAL, and MAKE_RESTARTS
.INCLUDE_DIRS: the list of directories that GNU Make will search when looking for Makefiles to be included. This variable is set by the standard list of directories built into GNU Make and modified by the -I command-line option. Although it's possible to change the value of .INCLUDE_DIRS this has no affect on how GNU Make searches for Makefiles. For example, running make -I /usr/foo on Linux with this Makefile outputs /usr/foo /usr/local/include /usr/local/include /usr/include:
.FEATURES: this variable expands to a list of features that GNU Make supports and can be used to determine if a specific feature is available. With GNU Make 3.81 on Linux .FEATURES is target-specific order-only second-expansion else-if archives jobserver check-symlink. This means that GNU Make 3.81 supports target- and pattern-specific variables, has order-only prerequisites, supports second-expansion (.SECONDEXPANSION), supports else if non-nested conditionals, supports ar files, has support for parallel making using the jobserver and supported the new -L command-line option for checking symlinks. To test if a specific feature is available you can use $(filter...). For example, has-order-only := $(filter order-only,$(.FEATURES)) sets has-order-only to true if the GNU Make running has order-only prerequisite support. However, this is not backwards compatible (for example, checking .FEATURES for target-specific under GNU Make 3.80 would indicate that target-specific variables are not available, which they are); a backwards compatible check needs to first determine whether .FEATURES is present by seeing if it is non-blank.
.DEFAULT_GOAL. Normally if no goal is specific on the GNU Make command-line GNU Make will build the first target it sees in the first Makefile it parses. It's a Make tradition that the target is called all or something similar. It's possible to override this behaviour by setting the .DEFAULT_GOAL variable anywhere in a Makefile. For example, the following Makefile will build all when run, with no goal on the command-line, despite the fact that the first target encountered is called fail: .DEFAULT_GOAL can also be read to determine the current default goal, and if set to blank (.DEFAULT_GOAL :=) GNU Make will automatically pick up the next target it encounters as the default goal.
MAKE_RESTARTS: this is the count of the number of times that GNU Make has restarting while performing Makefile remaking. If GNU Make has not restarted then MAKE_RESTARTS is blank and not 0.
$(info text...): $(info) is like the existing $(warning text...) function, but it prints the expanded text... argument to stdout without reporting the Makefile and line number. For example, the following Makefile generates the output Hello, World!:
$(lastword LIST): returns the last word of a GNU Make list. Previously this was possible using existing GNU Make functions by writing $(word $(words LIST),LIST); but $(lastword) is more efficient. If you are using the GNU Make Standard Library there's an function called last which is the same as $(lastword). If you are using GNU Make 3.81 and GMSL 1.0.6 or above last automatically uses the built-in $(lastword) for speed.
$(flavor VAR): returns the flavor of a variable (either recursive for recursively expanded or simple for simply expanded). For example, the following Makefile prints that REC is recursive and SIM is simple:
$(or ...) and $(and ...): $(or ...) returns a non-blank string if any of its arguments is non-blank (you'll recall that GNU Make's $(if) treats a non-blank string is true and a blank string as false). $(and ...) returns a non-blank string if and only if all its arguments are non-blank. If you are using the GNU Make Standard Library there are and and or functions as part of the library. If you are using GNU Make 3.81 and GMSL 1.0.6 or above then the new built-in functions are not overriden with the GMSL versions for speed and compatibility. This means that Makefiles that use GMSL are fully backwards and forwards-compatible with GNU Make 3.81.
$(abspath ...) and $(realpath ...): $(abspath ...) returns the absolute path relative to the directory that GNU Make was started in (and taking into account any -C command-line options). The path has all . and .. elements resolved and duplicate /'s removed. For example the following Makefile (at least on my machine) prints /home/jgc/bar when it's placed in /home/jgc:
$(info $(abspath foo/./..//////bar)) all: ; @true
Note that GNU Make does not check that the path exists, it just resolves the path elements to make an absolute path. $(realpath) returns the same result as $(abspath) except that any symbolic links are resolved. For example, if bar is symlinked to over-here then the following Makefile would return /home/jgc/over-here if read from /home/jgc:
$(info $(realpath ../jgc/./bar)) all: ; @true