In software development, Make is a command-line interface software tool that performs actions ordered by configured dependencies as defined in a configuration file called a makefile. It is commonly used for build automation to Software build executable (such as a computer program or library) from source code. But, not limited to building, Make can perform any operation available via the operating system shell.
Make is widely used, especially in Unix and Unix-like , even though many competing technologies and tools are available, including similar tools that perform actions based on dependencies, some compilers and interactively via an integrated development environment.
In addition to referring to the original Unix tool, Make is also a technology since multiple tools have been implementation with roughly the same functionality including similar makefile syntax and semantics.
Feldman describes the inspiration to write Make as arising from a coworker's frustration with the available tooling of the time:
Before Make, building on Unix mostly consisted of written for each program's codebase. Make's dependency ordering and out-of-date checking makes the build process more robust and more efficient. The makefile allowed for better organization of build logic and often fewer build files.
Make is widely used in part due to its early inclusion in Unix, starting with PWB/UNIX 1.0, which featured a variety of software development tools.
POSIX includes standardization of the basic features and operation of the Make utility, and is implemented with varying degrees of compatibility with Unix-based versions of Make. In general, simple makefiles may be used between various versions of Make with reasonable success. GNU Make, Makepp and some versions of BSD Make default to looking first for files named "GNUmakefile", "Makeppfile" and "BSDmakefile" respectively, which allows one to put makefiles which use implementation-defined behavior in separate locations.
A makefile defines targets where each is either a file to generate or is a user-defined concept, called a phony target.
Make updates the targets passed as arguments:
If no target is specified, Make updates the first target in the makefile which is often a phony target to perform the most commonly used action.
Make skips build actions if the target file timestamp is after that of the source files. How to sort Linux ls command file output Doing so optimizes the build process by skipping actions when the target file is up-to-date, but sometimes updates are skipped erroneously due to file timestamp issues including restoring an older version of a source file, or when a network filesystem is a source of files and its clock or time zone is not synchronized with the machine running Make. Also, if a source file's timestamp is in the future, make repeatedly triggers unnecessary actions, causing longer build time.
When Make starts, it uses the makefile specified on the command-line or if not specified, then uses the one found by via specific search rules. Generally, Make defaults to using the file in the working directory named . GNU Make searches for the first file matching: , , or .
Make processes the options of the command-line based on the loaded makefile.
Makefiles can contain the following constructs: 3.1 What Makefiles Contain , GNU make, Free Software Foundation
target [target ...]: [component ...] [command 1] . . . [command n]
Usually a rule has a single target, rather than multiple.
A dependency line may be followed by a recipe: a series of Tab character indented command lines that define how to generate the target from the components (i.e. source files). If any prerequisite has a more recent timestamp than the target file or the target does not exist as a file, the recipe is performed.
The first command may appear on the same line after the prerequisites, separated by a semicolon,
Each command line must begin with a tab character. Even though a space character is also whitespace, Make requires tab. Since this often leads to confusion and mistakes, this aspect of makefile syntax is subject to criticism. Eric S. Raymond describes it as "one of the worst design botches in the history of Unix""Chapter 15. Tools: make: Automating Your Recipes", The Art of Unix Programming, Eric S. Raymond 2003 and The Unix-Haters Handbook said "using tabs as part of the syntax is like one of those pungee sic stick traps in The Green Berets". Feldman explains the choice as caused by a Technical debt for an early implementation difficulty, and preserved by a desire for backward compatibility with the very first users:
GNU Make since version 3.82 allows the choice of any symbol (one character) as the recipe prefix using the .RECIPEPREFIX special variable:
Each command is executed in a separate Unix shell. Since operating systems use different shells, this can lead to unportable makefiles. For example, GNU Make (all POSIX Makes) executes commands with /bin/sh by default, where Unix commands like cp are normally used. In contrast, Microsoft's nmake executes commands with cmd.exe where Batch file commands like copy are available but not necessarily cp.
Since a recipe is optional, the dependency line can consist solely of components that refer to other targets:
The following example rule is evaluated when Make updates target file.txt via . If file.html is newer than file.txt or file.txt does not exist, then the command is run to generate file.txt from file.html.
lynx -dump file.html > file.txt
A command can have one or more of the following prefixes (after the tab):
Ignoring errors and silencing echo can alternatively be obtained via the special targets and .
Microsoft's NMAKE has predefined rules that can be omitted from these makefiles, e.g. .
For example, the macro is frequently used in makefiles to refer to the location of a C compiler. If used consistently throughout the makefile, then the compiler used can be changed by changing the value of the macro rather than changing each rule command that invokes the compiler.
Macros are commonly named in all-caps:
A macro value can consist of other macro values. The value of macro is expanded on each use lazy evaluation.
A macro is used by expanding either via $ NAME or $( NAME). The latter is safer since omitting the parentheses leads to Make interpreting the next letter after the as the entire variable name. An equivalent form uses curly braces rather than parentheses, i.e. , which is the style used in BSD.
Macros can be composed of shell commands by using the command substitution operator !=.
The command-line syntax for overriding a macro is:
Makefiles can access predefined internal macros, with and being common.
# echo components YOUNGER than TARGET
echo $?
# echo TARGET name
echo $@
A common syntax when defining macros, which works on BSD and GNU Make, is to use , , and instead of the equal sign ().
.html.txt:
lynx -dump $< > $@
When called from the command line, the example above expands:
A pattern rule looks like an ordinary rule, except that its target contains exactly one character within the string. The target is considered a pattern for matching file names: the can match any substring of zero or more characters, See section Pattern Matching Rules in the SunPro man page while other characters match only themselves. The prerequisites likewise use to show how their names relate to the target name.
The example above of a suffix rule would look like the following pattern rule:
%.txt : %.html
lynx -dump $< > $@
target: component \ component command ; \ command | \ piped-command
all:
echo "Hello $(LOGNAME), nothing to do by default"
echo "Try 'make help'"
help:
egrep "^# target:" [Mm]akefile
dist:
tar -cf $(RELEASE_DIR)/$(RELEASE_FILE) && \
gzip -9 $(RELEASE_DIR)/$(RELEASE_FILE).tar
Below is a simple makefile that by default (the "all" rule is listed first) compiles a source file called "helloworld.c" using the system's C compiler and also provides a "clean" target to remove the generated files if the user desires to start over. The and are two of the so-called internal macros (also known as automatic variables) and stand for the target name and "implicit" source, respectively. In the example below, expands to a space delimited list of the prerequisites. There are a number of other internal macros. Automatic Variables GNU `make'
all: helloworld
helloworld: helloworld.o
helloworld.o: helloworld.c
clean:
$(CC) $(LDFLAGS) -o $@ $^
$(CC) $(CFLAGS) -c -o $@ $<
$(RM) helloworld helloworld.o
Many systems come with predefined Make rules and macros to specify common tasks such as compilation based on file suffix. This lets users omit the actual (often unportable) instructions of how to generate the target from the source(s). On such a system the makefile above could be modified as follows:
helloworld: helloworld.o
clean:
.SUFFIXES: .c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
$(RM) helloworld helloworld.o
.c.o:
$(CC) $(CFLAGS) -c $<
That "helloworld.o" depends on "helloworld.c" is now automatically handled by Make. In such a simple example as the one illustrated here this hardly matters, but the real power of suffix rules becomes evident when the number of source files in a software project starts to grow. One only has to write a rule for the linking step and declare the object files as prerequisites. Make will then implicitly determine how to make all the object files and look for changes in all the source files.
Simple suffix rules work well as long as the source files do not depend on each other and on other files such as header files. Another route to simplify the build process is to use so-called pattern matching rules that can be combined with compiler-assisted dependency generation. As a final example requiring the gcc compiler and GNU Make, here is a generic makefile that compiles all C files in a folder to the corresponding object files and then links them to the final executable. Before compilation takes place, dependencies are gathered in makefile-friendly format into a hidden file ".depend" that is then included to the makefile. Portable programs ought to avoid constructs used below.
PROGRAM = foo
C_FILES := $(wildcard *.c)
OBJS := $(patsubst %.c, %.o, $(C_FILES))
CC = cc
CFLAGS = -Wall -pedantic
LDFLAGS =
LDLIBS = -lm
all: $(PROGRAM)
$(PROGRAM): .depend $(OBJS)
depend: .depend
.depend: cmd = gcc -MM -MF depend $(var); cat depend >> .depend;
.depend:
-include .depend
%: %.o
clean:
.PHONY: clean depend
ifneq (,)
This makefile requires GNU Make.
endif
$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(PROGRAM) $(LDLIBS)
@echo "Generating dependencies..."
@$(foreach var, $(C_FILES), $(cmd))
@rm -f depend
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
$(CC) $(CFLAGS) -o $@ $<
rm -f .depend $(OBJS)
Another approach is to use meta-build tools like CMake, Meson etc.
|
|