The Makefile
The Makefile
Comments Rules Dependency Lines Shell Lines Macros Macro Modifiers Inference Rules Response Files Makefile Directives Make reads its instructions from text files. An initialization file is read first, followed by the makefile. The initialization file holds instructions for all makes and is used to customize the operation of Make. Make automatically reads the initialization file whenever it starts up. Typically the initialization file is named make.ini and it resides in the directory of make.exe and mkmf.exe. The name and location of the initialization file is discussed in detail on Page . The makefile has instructions for a specific project. The default name of the makefile is literally makefile, but the name can be specified with a command-line option. With a few exceptions, the initialization file holds the same kind of information as does a makefile. Both the initialization file and the makefile are composed of the following components: comments, dependency lines, directives, macros, response files, rules and shell lines.
Comments [Top]
The simplest makefile statement is a comment, which is indicated by the comment character #. All text from the comment character to the end of the line is ignored. Here is a large comment as might appear in a makefile to describe its contents: # # Makefile for Opus Make 6.1 # # Compiler: Microsoft C 6.0 # Linker: Microsoft Link 5.10 # The comment character could also be used at the end of another makefile statement: some makefile statement # a comment
Rules [Top]
A rule tells Make both when and how to make a file. As an example, suppose your project involves compiling source files main.c and io.c then linking them to produce the executable project.exe. Withholding a detailed explanation for a bit, here is a makefile using Borland C which will manage the task of making project.exe:
Additional Dependencies
In C and in other programming languages it is possible to include the contents of a file into the file currently being compiled. Since the compiled object depends on the contents of the included file, we add the included file as a source of the object file. Assume each of main.c and io.c include def.h. We can either change two dependency lines in the makefile: main.obj : main.c becomes main.obj : main.c def.h io.obj : io.c becomes io.obj : io.c def.h or add a new line which lists only the additional dependencies: main.obj io.obj : def.h Notice that there are two targets on the left of the colon. This line means that both main.obj and io.obj depend on def.h. Either of these methods are equivalent. The example makefile now looks like: project.exe : main.obj io.obj tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib main.obj : main.c bcc ms c main.c io.obj : io.c bcc ms c io.c main.obj io.obj : incl.h
project.exe : main.obj io.obj tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib tells Make that making project.exe requires running the program tlink to link main.obj and io.obj. This shell line would be run only if main.obj or io.obj was newer than project.exe. For tlink, c0s is the small model start-up object file and the cs is the small model library. The /Lf:\bc\lib flag tells tlink that the start-up object file and library files can be found in the f:\bc\lib directory. A target can have more than one shell line, listed one after the other, such as: project.exe : main.obj io.obj echo Linking project.exe tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib >tlink.out The first line shows that command processor commands can be executed by Make. The second line shows redirection of output, where the output of the tlink program is redirected to the tlink.out file. After each shell line is executed, Make checks the shell line exit status. By convention, programs return a 0 (zero) exit status if they finish without error and non-zero if there was an error. The first shell line returning a non-zero exit status causes Make to display the message: OPUS MAKE: Shell line exit status exit_status. Stop. This usually means the program being executed failed. Some programs return a non-zero exit status inappropriately and you can have Make ignore the exit status by using a shell-line prefix. Prefixes are characters that appear before the program name and modify the way Make handles the shell line. For example: project.exe : main.obj io.obj tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib The prefix tells Make to ignore the exit status of shell line. If the exit status was non-zero Make would display the message: OPUS MAKE: Shell line exit status exit_status (ignored) See Page for more information on shell lines and shell-line prefixes.
Macros [Top]
The example makefile is reproduced here: project.exe : main.obj io.obj tlink c0s main.obj io.obj, project.exe,, cs /Lf:\bc\lib main.obj : main.c bcc ms c main.c io.obj : io.c bcc ms c io.c main.obj io.obj : def.h We see that the text main.obj io.obj occurs repeatedly. To cut down on the amount of repeated text, we can use a macro definition to assign a symbol to the text.
Here is the above example written with the introduction of four macros:
OBJS = main.obj io.obj MODEL = s CC = bcc CFLAGS = m$(MODEL) project.exe : $(OBJS) tlink c0$(MODEL) $(OBJS), project.exe,, c$(MODEL) /Lf:\bc\lib main.obj : main.c $(CC) $(CFLAGS) c main.c io.obj : io.c $(CC) $(CFLAGS) c io.c $(OBJS) : incl.h
The value of the OBJS macro is the list of object files to be compiled. The macro definitions for MODEL, CC and CFLAGS were introduced so that it is easier to change the compiler memory model, name of the C compiler and its options. Make automatically imports environment variables as macros, so you can reference an environment variable such as PATH with the makefile expression $(PATH).
Run-Time Macros
Make defines some special macros whose values are set dynamically. These macros return information about the current target being built. As examples, the .TARGET macro is name of the current target, the .SOURCE [2] macro is the name of the inferred source (from an inference rule) or the first of the explicit sources and the .SOURCES [3] macro is the list of all sources. Using run-time macros the example can be written: OBJS = main.obj io.obj CC = bcc MODEL = s CFLAGS = m$(MODEL) project.exe : $(OBJS) tlink c0$(MODEL) $(OBJS), $(.TARGET),, c$(MODEL) /Lf:\bc\lib main.obj : main.c $(CC) $(CFLAGS) c $(.SOURCE) io.obj : io.c $(CC) $(CFLAGS) c $(.SOURCE) $(OBJS) : incl.h As you can see, the shell lines for updating main.obj and io.obj are identical when run-time macros are used. Run-time macros are important for generalizing the build process with inference rules, as shown on Page .
Filename Components
There is a set of macro modifiers for accessing parts of file names. For example, with the macro definition: SRCS = d:\src\main.c io.asm Some of the modifiers are: Modifier, and description Example Value $(SRCS,D) d:\src . D, the directory $(SRCS,E) .c .asm E, the extension (or suffix) $(SRCS,F) main.c io.asm F, the file name
Tokenize
Another modifier is the Wstr modifier, which replaces whitespace between elements of the macro with str, a string. The str can be a mix of regular characters and special sequences, the most important sequence being \n which represents a newline character (like hitting the enter key). For example: $(OBJS,W space +\n) is main.obj + io.obj
Other Modifiers
Other modifiers include: @ (include file contents), LC (lower case), UC (upper case), M (member) and N (non-member). The M and N modifiers and the S (substitute) modifier use regular expressions for powerful and flexible pattern-matching. See Page for more information on all macro modifiers.
This tells Make that the program tlink accepts LINK-style response files. When a shell line executes tlink, Make checks if the shell line is longer than allowed by the operating system and automatically produces a response file if necessary.
The layout of this makefile is fairly traditional macros are defined first, the primary target follows the macros and the extra dependency information is last. This example also uses the %abort directive to abort Make if the makefile does not support a particular compiler. Directives can also be used at run time, to control the shell lines Make executes. For more information about directives, see Page .
Footnotes
[1]You may be accustomed to source meaning a file containing source code. We will say source file when we mean this. [2]This was called .IMPLICIT in Opus Make v5.2x and you can still use that name for .SOURCE. [3]This was called .ALL in Opus Make v5.2x and you can still use that name for .SOURCES.