PSCF v1.3.1
Make and Makefiles

Basics: Adding a New Source File (Prev)         Build System Files (Overview) (Next)

The build system for PSCF uses the unix "make" command. Understanding the build system thus requires some familiarity with make. The discussion of given below is intended to serve as an initial introduction for those who have little or no experience with the make command. Much more complete documentation is provided by the online manual for the GNU make command.

Make command

The "make" command is a standard unix utility that is used to control compilation and installation of software packages. It is invoked from a terminal command line by typing the command name "make", usually followed by one or more arguments that are known as "targets". Each target specifies either the name of a file that should create or a label for a specific action.

Whenever make is invoked, it searches for an associated "makefile" that specifies actions that can be performed by make when it is invoked from the current working directory. This makefile must be a file named "makefile" or "Makefile" that is located in the current working directory (i.e,.
in the directory from which the make command was invoked). Each possible target that may be given as an argument of the make command must be the target of a "rule" in the associated makefile.

Makefile rules

Each makefile defines a set of "rules". Each rule provide instructions for how to perfom a specific action or type of action. The general syntax for a makefile rule is:

target: prerequisites
recipe

The "target" is a character string that may be either the name of a file that would be created by the associated action or a arbitrary label for the rule. The "recipe" associated with a rule is a unix shell command or a sequence of such commands that must be invoked to perform the action.
The "prerequisites" entry is a list of the names of files that are required as inputs to the recipe.

The list of prerequisites must appear on the same line as the target, separated from the target by a colon. The recipe must start on the line after the line containing the target, and may span one or more lines. In a rather peculiar quirk of makefile syntax, every line of a makefile recipe must start with an invisible tab character.

The simplest type of makefile rule is one in which the target is the name of an object file that is created by compiling a source file. In this case, the list of prerequisites normally contains both the name of the associated compilable *.cpp or *.cu source file and the names of all of the *.h header files and (in some cases) template implementation *.tpp files that the source file directly or indirectly includes. In simple cases, the recipe for such a rule could be a command that calls the compiler to compile the source file and create the target object file.

Example: Suppose A is the name of a C++ class that is defined in a header file A.h, for which member functions that are defined in a source file A.cpp in the current working directory. A simple makefile rule for using the gcc compiler to create an associated object file named A.o might look like this:

A.o: A.cpp A.h
gcc -o A.o A.cpp

The list of prerequisites for such a rule should include paths for all source file (e.g., A.cpp) and for all header files that are directly or indirectly included into the source file via preprocessor "#include" directives.. For example, if either A.h or A.cpp contained a preprocessor directives to include another header file named "B.h" that contains the definition of another class named B, then B.h would also need to be included in the list of prerequisites for the target A.o.

Types of target

Makefile rules can contain the following three different types of target:

  • File name targets, for which the target is the name of a specific file that needs to be created by the build system, such as an object, library or executable file.
  • File pattern targets, for which the target is given by a filename pattern that, for example, might match any filename with a specified file name extension.
  • Phony targets, for which in which the target is a label such as "clean" or "all" but that is neither a file name nor a file name pattern, but merely serves as a label for the rule.

Rules with targets that are file name patterns are referred to here as pattern rules. The simple example discussed above has a file name target. Pattern rules and phony targets are discussed in more detail below.

File name targets and phony targets may be used as command line arguments of the make command. Use of file name target as an argument of make indicates that the target file should be built or updated if necessary, by applying an associated recipe. Use of a phony target as a command line argument instructs make to first update any out-of-date prerequisites of the phony action, and then apply the associated recipe.

Out-of-date targets

To ensure file validity and avoid unnecessary work, the make utility can determine whether files that are listed as targets of rules are outdated or up-to-date. When a rule is invoked to build a target file, such as an object file, the recipe associated with the rule is actually applied only if the target file either does not exist or if it already exists but is out-of-date. An existing target file is considered out-of-date if any of its pre-requisistes is a file that has been modified after the last time the target file was modified.

Consider a simple case in which a user invoke the rule to build a particular object file *.o target twice in a row, without editing any prerequisite C++ files in between. In this case, make would not invoke the recipe to recreate the object file the second time the rule is invoked, because it would be able to determine that the target object file is newer than all of its prerequisites, and that it is thus already up to date.

The decision regarding whether an existing target of a rule is older than any of its prequisites is based on examination of file time stamps for files listed as prerequisites. Time stamps are metadata associated with files in a unix file system, which indicate when the file was created, and the most recent time that a file was modified. An existing file that is the target of a makefile rule is considered out-of-date if any prerequisite of that target either:

  • is a file that has been modified since the last time that the target file was created or modified, or
  • is the target of another makefile rule that can be shown to be out-of-date by examination of the prerequisites of that rule.

A target file that does not yet exist is always considered to be out-of-date, as are phony targets (targets that are not file names).

In a system with properly constructed prerequisites, the rule to construct a *.o object file by compiling an associated *.cpp or *.cu source file should list all of the header files that are directly or indirectly included by the source file. If a header file is directly or indirectly included by multiple source files, then modification of that header will cause all of the associated object files to become outdated.

Recursive updating of prerequisites

When a makefile rule is invoked, the make command first checks if any of the prerequisites of that rule are targets of other rules and, if so, whether any such prerequisites are out-of-date. If any prerequisites are found to be out of date, make first applies the rules required to update all out-of-date prerequisite files before applying the required recipe to update the primary target.

This recursive behavior is only relevant to rules that have prerequisites that are intermediate files that are created by the build system, using instructions given other rules. Specifically, it effects the behavior of rules that create libraries by archiving object files, and of rules that create executable files by linking object files and libraries.

As an example of this behavior, consider the case of a rule that builds an executable file. The rule for constructing an executable file often has a recipe that links one object file that contains object code for the main function to one or more library files. The prerequisites for such a rule includes the object file for the main program and the required libraries. Each library file may itself be the target of a rule that lists the object files that are combined to form the library as prerequisites. Each of those object files is the target of a rule that compiles a corresponding source file, with a prerequisite list that includes the source file and header files that are directly or indirectly included by the source file. Invocation of a rule to build an executable file can thus cause a cascade of events in which the make utility first compiles a set of source files, in order to create or update any non-existent or out-of-date object files, then uses an archiver to create or update any outdated library files, and finally use a linker to create the requested executable file.

This recursive behavior normally has no affect on rules that create object file targets. The prerequisites of such rules are usually C++ files that are not created by the build system, and so are not targets of other rules.

Pattern rules

A pattern rule is typically used to specify a general procedure for creating any target file with a specific file extension by applying a specific recipe to any corresponding files with the same base name but a different file name extension. For example, a simple pattern rule might tell the build system how to compile any object file with file name extension .o by compiling a corresponding C++ source file with file name extension *.cpp.

The target of a pattern rule must contain exactly one instance of the wildcard symbol %, which may be matched by any continuous string of characters. The % symbol may also used in one or more of the prerequisites of a pattern rule to represent the same string as the one that matches % in the pattern for the target.

For example, a pattern rule with a target and prerequisite of the form

%.o: %.cpp
recipe...

specifies how make any object file target with a file name of the form %.o by compiling a source file of the form %.cpp, in which % represents the shared base name of the two files.

Other automatically defined variables may be used in the recipe associated with a pattern rule to refer to the target and various prerequisites. Specifically:

  • The symbol $@ refers to the target
  • The symbol $< refers to the first prerequisite
  • The symbol $^ refers to a list of all prerequisites

A pattern rule can be used to build a specific target file if all of the following are true:

  • There is no explicit rule for the target file that provides a recipe for how to construct the target
  • The pattern for the target in the pattern rule matches the actual target file name, and
  • There exist files that match the patterns for any prerequisite files listed in the pattern rule.

An explicit rule that provides a recipe will always be used in preference to a pattern rule. The use of explicit rules that do not contain a recipe is discussed below.

More detailed documentation of pattern rules is available here .

File target rules with no recipe

A pattern rule may be used to construct a target file for which there exists an explicit rule that lists prerequisites, but that does not contain a recipe. When the make command tries to build the target of such a rule, it searches for an applicable pattern-rule and uses the recipe for such a pattern rule if if can find one.

For example, conside the following hypothetical rule for an object file target named A.o:

A.o: A.cpp A.h B.h

This rule contains a target and prerequisites, but no recipe. If the make command needs to construct A.o, and finds this rule, it will look for a applicable pattern rule to provide a recipe. In this case, it would succeed if it finds a pattern rule to create an object file target that matches %.o by compling a source file that matches %.cpp (i.e., a *.cpp file with the same base name as the target), and if A.cpp and the other prerequisites of A.o all exist.

In the PSCF build system, each *.d dependency file contains a makefile rule that contains an object file as the target and a list C++ file prerequisites, but no recipe. Recipes used to create *.o files are instead obtained from pattern rules that provide general recipes for compiling *.cpp files (using a C++ compiler) and for compiling *.cu files (using the NVIDIA CUDA compiler).

Phony targets

A makefile rule target that is not a file name or a file name pattern is referred to as a "phony" target. Rules with phony targets are used to label an action that is not designed to build a specific target file. Phony targets are always considered to be out-of-date. Invoking make with an argument that is the name of a phony target always causes make to first create or update any outdated prerequisites of that target, and then apply any recipe associated with the phony target.

PSCF makefiles generally contain several standard phony targets. Every makefile contains phony targets named "all" and "clean". Upper level directories often also have a targets named "veryclean". The purposes of these standard targets are:

  • "all" builds all of the target files that the makefile is responsible for constructing.
  • "clean" removes any intermediate files that would be generated by the "all" target.
  • "veryclean" removes all files that would be created by the "all" target, as well as files created by the configure script.

The "all" target generally specifies a list of prerequisites but does not contain a recipe. Invocation of the "all" rule thus causes make to create or update all of the prerequisites of the rule. The list of prerequisites of the "all" target normally includes all of the file targets that the makefile in the current working directory is responsible for constructing. Depending on the location of the makefile, this may include a list of object file targets, a library file target, and an executable file target.

The "clean" and "veryclean" targets instead generally contain recipes but have no prerequisites. Invoking either of these rules thus simply causes make to apply the recipe. Recipes for these targets remove files created previously by the build system.

Makefiles can specify explicitly that particular targets are phony (i.e., do not correspond to file names) by listing them as prerequisites of a standard built-in target named .PHONY. Thus for example, a makefile with two phony targets named "all" and "clean" could include a rule of the form

.PHONY: all clean

to declare that these two names are phony targets.

Makefile variables

Makefiles may define and use variables. The value of a makefile variable is generally a character string. By convention, almost all variable names used in PSCF are strings of upper case letters and underscores, such SRC_DIR.

An expression for assigning a value to a makefile variable contains the name of the variable on the left of an equals sign and the value to the right. For example, the expression

ROOT_DIR = /users/smith/pscfpp
Python package of all python modules provided with PSCF.
Definition __init__.py:1

assigns the variable ROOT_DIR a value that is an absolute path to a directory.

A reference to the value of a variable is represented by a symbol in which the variable name in enclose in parentheses and a $ sign is placed before the opening parenthesis. For example, the symbol $(ROOT_DIR) represents the value of the variable ROOT_DIR. References to makefile variable values may be used in other makefile expressions. For example, after the variable ROOT_DIR is assigned a value, the makefile variable SRC_DIR could be assigned a value by the expression

SRC_DIR = $(ROOT_DIR)/src

in which the variable value $(ROOT_DIR) is expanded into the actual path to the PSCF root directory and then concatenated with the string "/src" to obtain the path to the PSCF src directory.

Makefile include directive

Makefiles can use the "include" directive to include other makefiles or makefile fragments. For example, the directive

include $(SRC_DIR)/pscf/sources.mk

causes the sources.mk file in the PSCF src/pscf directory to be copied verbatim into the current makefile.

Comment lines

Any line in a makefile that begins with a pound symbol ("#") is a comment that is ignored when the file is processed by the make command.

Default targets

If the make command is invoked without any argument, by typing

make

on a line by itself, then the make command will invokes the first rule in the associated makefile, which is known as the default rule.

In the PSCF build system, the "all" command is the default target for makefiles located in the PSCF root directory, and in the roots of the bld and src directory trees. Invoking "make" from any of these three directories is thus equivalent to invoking "make all".

The "all" target is not the default target, however, in lower-level directories, i.e., in namespace-level subdirectories of src or bld, and lower-level subdirectories of src.


Basics: Adding a New Source File (Prev)         Build System (Up)         Build System Files (Overview) (Next)