About makefiles

A makefile is an ASCII file that contains the set of instructions that MAKE uses to build a certain project. Although MAKE assumes your makefile is called MAKEFILE or MAKEFILE.MAK, you can specify a different makefile name with the -f option.
MAKE either builds the target(s) you specify with the make command or it builds the first target it finds in the makefile To build more than a single target, use a symbolic target in your makefile.
Makefiles can contain

Explicit and implicit rules

You write explicit and implicit rules to instruct MAKE how to build the targets in your makefile. In general, these rules are defined as follows:

Explicit rules are instructions for specific files.
Implicit rules are general instructions for files without explicit rules.

All the rules you write follow this general format:

Dependency line
Command line

While the dependency line has a different syntax for explicit and implicit rules, the command line syntax stays the same for both rule types.

MAKE supports multiple dependency lines for a single target, and a single target can have multiple command lines. However, only one dependency line should contain a related command line. For example:

Target1: dependent1 dep2 dep3 dep4 dep5
Target1: dep6 dep7 dep8
bcc -c $**

Explicit rule syntax

Explicit rules specify the instructions that MAKE must follow when it builds specific targets. Explicit rules name one or more targets followed by one or two colons. One colon means one rule is written for the target(s); two colons mean that two or more rules are written for the target(s).
Explicit rules follow this syntax:

target [target...]:[:][{path}] [dependent[s]...]
    [commands]

target

specifies the name and extension of the file to be built (a target must begin a line in the makefile-you cannot precede the target name with spaces or tabs). To specify more than one target, separate the target names with spaces or tabs. Also, you cannot use a target name more than once in the target position of an explicit rule.

path

is a list of directories that tells MAKE where to find the dependent files. Separate multiple directories with semicolons and enclosed the entire path specification in braces.

dependent

is the file (or files) whose date and time MAKE checks to see if it is newer than target. Each dependent file must be preceded by a space. If a dependent appears elsewhere in the makefile as a target, MAKE updates or creates that target before using the dependent in the original target (this in known as a linked dependency).

commands

are any operating system command or commands. You must indent the command line by at least one space or tab, otherwise they are interpreted as a target. Separate multiple commands with spaces.

If a dependency or command line continues on the following line, use a backslash (\) at the end of the first line to indicate that the line continues. For example,

MYSOURCE.EXE: FILE1.OBJ\ #Dependency line
FILE3.OBJ #Dependency line continued
bcc file1.obj file3.obj #Command line
Single targets with multiple rules

A single target can have more than one explicit rule. To specify more than a single explicit rule, use a double colon (::) after the target name. The following example shows targets with multiple rules and commands.

.cpp.obj:
   bcc -c -ncobj $<

.asm.obj:
   tasm /mx $<, asmobj\

mylib.lib :: f1.obj f2.obj #double colon specifies multipe rules
   echo Adding C files
   tlib mylib -+cobjf1 -+cobjf2

mylib.lib :: f3.obj f4.obj
   echo Adding ASM files
   tlib mylib -+asmobjf3 -+asmobjf4

Implicit rule syntax

An implicit rule specifies a general rule for how MAKE should build files that end with specific file extensions. Implicit rules start with either a path or a period. Their main components are file extensions separated by periods. The first extension belongs to the dependent, the second to the target.
If implicit dependents are out-of-date with respect to the target, or if the dependents don't exist, MAKE executes the commands associated with the rule. MAKE updates explicit dependents before it updates implicit dependents.

Implicit rules follow this basic syntax:

[{source_dir}].source_ext[{target_dir}].target_ext:
	[commands]

source_dir

specifies the directory (or directories) containing the dependent files. Separate multiple directories with a semicolon.

.source_ext

specifies the dependent filename extension.

target_dir

specifies the directory where MAKE places the target files. The implicit rule will only be used for targets in this directory. Without specifying a target directory, targets from any directory will match the implicit rule.

.target_ext

specifies the target filename extension. Macros are allowed here.

: (colon)

marks the end of the dependency line.

commands

are any operating system command or commands. You must indent the command line by at least one space or tab, otherwise they are interpreted as a target.


If two implicit rules match a target extension but no dependent exists, MAKE uses the implicit rule whose dependent's extension appears first in the .SUFFIXES list.

Explicit rules with implicit commands

A target in an explicit rule can get its command line from an implicit rule. The following example shows an implicit rule followed by an explicit rule without a command line.

.c.obj:
    bcc -c $<   #This command uses a macro $< described later

myprog.obj: #This explicit rule uses the command: bcc -c myprog.c

The implicit rule command tells MAKE to compile MYPROG.C (the macro $< replaces the name myprog.obj with myprog.c).

Using MAKE macros

A macro is a variable that MAKE expands into a string whenever MAKE encounters the macro in a makefile. For example, you can define a macro called LIBNAME that represents the string "mylib.lib." To do this, type the line LIBNAME = mylib.lib at the beginning of your makefile. Then, when MAKE encounters the macro $(LIBNAME), it substitutes the string mylib.lib. Macros let you create template makefiles that you can change to suit different projects.
To use a macro in a makefile, type $(MacroName) where MacroName is a defined macro. You can use braces or parentheses to enclose MacroName.

MAKE expands macros at various times depending on where they appear in the makefile:

Nested macros are expanded when the outer macro is invoked.
Macros in rules and directives are expanded when MAKE first looks at the makefile.
Macros in commands are expanded when the command is executed.

If MAKE finds an undefined macro in a makefile, it looks for an operating system environment variable of that name (usually defined with SET) and uses its definition as the expansion text. For example, if you wrote $(PATH) in a makefile and never defined PATH, MAKE would use the text you defined for PATH in your AUTOEXEC.BAT. See your operating system manuals for information on defining environment variables.

Command syntax

Commands immediately follow an explicit or implicit rule and must begin on a new line with a space or tab.
Commands can be any operating system command, but they can also include MAKE macros, directives, and special operators that your operating system wonít recognize (however, note that | can't be used in commands). Here are some sample commands:

cd..

bcc -c mysource.c

COPY *.OBJ C:\PROJECTA

bcc -c $(SOURCE) #Macros are explained later in the chapter.

Commands follow this general syntax:

[prefix...] commands
Command prefixes

Commands in both implicit and explicit rules can have prefixes that modify how MAKE treats the commands. The following table lists the prefixes you can use in makefiles:

Prefix Description
@
Don't display the command while it's being executed.
-num
Stop processing commands in the makefile when the exit code returned from command exceeds the integer num. Normally, MAKE aborts if the exit code is nonzero. No space is allowed between - and num.
-
Continue processing commands in the makefile, regardless of the exit codes they return.
&
Expand either the macro $**, which represents all dependent files, or the macro $?, which represents all dependent files stamped later than the target. Execute the command once for each dependent file in the expanded macro.
!
Will behave like the & prefix.
Using @

The following command uses the @ prefix, which prevents MAKE from displaying the command onscreen.

diff.exe : diff.obj
    @bcc diff.obj
Using -num and -

The -num and - prefixes control the makefile processing when errors occur. You can choose to continue with the MAKE process if an error occurs or you can specify a number of errors to tolerate.

In the following example, MAKE continues processing if BCC returns errors:

target.exe : target.obj
target.obj : target.cpp
-bcc -c target.cpp
Using &

The & prefix issues a command once for each dependent file. It is especially useful for commands that don't take a list of files as parameters. For example,

copyall : file1.cpp file2.cpp
&copy $** c:\temp


invokes COPY twice as follows:

copy file1.cpp c:\temp
copy file2.cpp c:\temp

Without the & modifier, MAKE would call COPY only once. Note: the & prefix only works with $** and $! macros.

.suffixes

The .suffixes directive tells MAKE the order (by file extensions) for building implicit rules.
The syntax of .suffixes is

.suffixes: .ext [.ext ...]

where .ext represents the dependent file extensions you list in your implicit rules. For example, you could include the line .suffixes: .asm .c .cpp to tell MAKE to interpret implicit rules beginning with the ones dependent on .ASM files, then .C files, then .CPP files, regardless of what order they appear in the makefile.

The following .suffixes example tells MAKE to look for a source file first with an .ASM extension, next with a .C extension, and finally with a .CPP extension. If MAKE finds MYPROG.ASM, it builds MYPROG.OBJ from the assembler file by calling TASM. MAKE then calls TLINK; otherwise, MAKE searches for MYPROG.C to build the .OBJ file or it searches for MYPROG.CPP.

.suffixes: .asm .c .cpp

myprog.exe: myprog.obj
bcc myprog.obj

.cpp.obj:
bcc -P -c $<
.asm.obj:
tasm /mx $<
.c.obj:
bcc -P- -c $<

Using MAKE directives

MAKE directives resemble directives in languages such as C and Pascal. In MAKE, they perform various control functions, such as displaying commands onscreen before executing them. MAKE directives begin either with an exclamation point or a period, and they override any options given on the command line. Directives that begin with an exclamation point must appear at the start of a new line.
The following table lists the MAKE directives and their corresponding command-line options:

Directive Option Description
.autodepend  -a  Turns on autodependency checking
.cacheautodepend  -c Turns on autodependency caching
!cmdswitches   Uses + or - followed by non-string option letters to turn each option on or off. Spaces or tabs must appear before the + or - operator, none can appear between the operator and the option letters.
!elif   Acts like a C else if
!else   Acts like a C else
!endif   Ends an !if, !ifdef, or !ifndef statement
!error   Stops MAKE and prints an error message
!if   Begins a conditional statement
!ifdef    Acts like a C #ifdef, testing whether a given macro has been defined
!ifndef   Acts like a C #ifndef, testing whether a given macro is undefined
.ignore -i MAKE ignores the return value of a command
!include   Acts like a C #include, specifying a file to include in the makefile
.keep -K Keeps temporary files that MAKE creates (MAKE usually deletes them)
!message   Prints a message to stdout while MAKE runs the makefile
.noautodepend -a- Turns off autodependency checking
.nocacheautodepend -c- Turns off autodependency caching
.noIgnore -i- Turns off .Ignore
.nokeep -K- Does not keep temporary files that MAKE creates
.nosilent -s- Displays commands before MAKE executes them
.noswap -S- Tells MAKE not to swap itself out of memory before executing a command
.path.ext   Tells MAKE to search for files with the extension .ext in path directories
.precious   Saves the target or targets even if the build fails
.silent -s MAKE executes commands without printing them first
.suffixes   Determines the implicit rule for ambiguous dependencies
.swap -S Tells MAKE to swap itself out of memory before executing a command
!undef    Clears the definition of a macro. After this, the macro is undefined

Defining MAKE macros

The general syntax for defining a macro in a makefile is:

MacroName = expansion_text.

MacroName

is case-sensitive (MACRO1 is different from Macro1).

MacroName

is limited to 512 characters.

expansion_text

is limited to 4096 characters. Expansion characters may be alphanumeric, punctuation, or spaces.

You must define each macro on a separate line in your makefile and each macro definition must start on the first character of the line. For readability, macro definitions are usually put at the top of the makefile. If MAKE finds more than one definition of MacroName, the new definition overwrites the old one.

You can also define macros using the -D command-line option. No spaces are allowed before or after the equal sign (=); however, you can define more than one macro by separating the definitions with spaces. The following examples show macros defined at the command line:

make -Dsourcedir=c:\projecta
make -Dcommand="bcc -c"
make -Dcommand=bcc option=-c

Macros defined in makefiles overwrite those defined on the command line.

.autodepend

Autodependencies are the files that are automatically included in the targets you build, such as the header files included in your C++ source code. With .autodepend on, MAKE compares the dates and times of all the files used to build the .OBJ, including the autodependency files. If the dates or times of the files used to build the .OBJ are newer than the date/time stamp of the .OBJ file, the .OBJ file is recompiled. You can use .autodepend (or -a) in place of forming linked dependencies.

Default MAKE actions

When you issue a MAKE command, MAKE looks for the file BUILTINS.MAK, which contains the default rules for MAKE (use the -r option to ignore the default rules). MAKE looks for this file first in the current directory, then in the directory where MAKE.EXE is stored. After loading BUILTINS.MAK, MAKE looks in the current directory for a file called MAKEFILE or MAKEFILE.MAK (use the -f option to specify a file other than MAKEFILE). If MAKE canít find either of these files, it generates an error message.

After loading the makefile, MAKE tries to build only the first explicit target listed in the makefile by checking the time and date of the dependent files of the first target. If the dependent files are more recent than the target file, MAKE executes the commands to update the target.
If one of the first targetís dependent files is used as a target elsewhere in the makefile, MAKE checks that targetís dependencies and builds it before building the first target. This chain reaction is called a linked dependency.

If something during the build process fails, MAKE deletes the target file it was building. Use the .precious directive if you want MAKE to keep the target when a build fails.
You can stop MAKE after issuing the MAKE command by pressing Ctrl+Break or Ctrl+C.

.precious

If a MAKE build fails, MAKE deletes the target file. The .precious directive prevents the file deletion, which you might desire for certain kinds of targets. For example, if your build fails to add a module to a library, you might not want the library to be deleted.
The syntax for .precious is

    .precious: target [target ...]

!include

This directive is like the #include preprocessor directive for the C or C++ language-it lets you include the text of another file in the makefile:

     !include filename

You can enclose filename in quotation marks (" ") or angle brackets (< >) and nest directives to unlimited depth, but writing duplicate !include directives in a makefile isn't permitted-you'll get the error message cycle in the include file.

Rules, commands, or directives must be complete within a single source file; you can't start a command in an !include file, then finish it in the makefile.

MAKE searches for !include files in the current directory unless you've specified another directory with the -I command-line option.

!message

The !message directive lets you send messages to the screen from a makefile. You can use these messages to help debug a makefile that isn't working properly. For example, if you're having trouble with a macro definition, you could put this line in your makefile:

!message The macro is defined here as: $(MacroName)

When MAKE interprets this line, it will print onscreen (assuming the macro expands to .CPP):

The macro is defined here as: .CPP

.path.ext

The .path.ext directive tells MAKE where to look for files with a certain extension. The following example tells MAKE to look for files with the .c extension in C:\SOURCE or C:\CFILES and to look for files with the .obj extension in C:\OBJS.

.path.c = C:\CSOURCE;C:\CFILES
.path.obj = C:\OBJS

Using macros in directives

You can use the $d macro with the !if conditional directive to perform some processing if a specific macro is defined. Follow the $d with macro name enclosed in parentheses or braces, as shown in the following example:

!if $d(DEBUG)          #If DEBUG is defined,
bcc -v f1.cpp f2.cpp   #compile with debug information;
!else 			   #otherwise
bcc -v- f1.cpp f2.cpp  #don't include debug information.
!endif

!error

The syntax of the !error directive is:

    !error message

MAKE stops processing and prints the following string when it encounters this directive:

     Fatal makefile exit code: Error directive: message

Embed !error in conditional statements to abort processing and print an error message, as shown in the following example:

!if !$d(MYMACRO)
#if MYMACRO isn't defined
!error MYMACRO isn't defined
!endif

If MYMACRO isn't defined, MAKE terminates and prints:

Fatal makefile 4: Error directive: MYMACRO isn't defined
Error-checking controls

!undef

!undef (undefine) clears the given macro, causing an !ifdef MacroName test to fail.
The syntax of the !undef directive is

!undef MacroName

 

© Cornel Mironel Niculae, 2003-2004

06-May-2004